package tywaves.simulator

import chisel3.RawModule
import chisel3.simulator.PeekPokeAPI
import svsim.Workspace
import tywaves.circuitmapper.TypedConverter

object TywavesSimulator extends PeekPokeAPI {

  /** Generate tywaves info and optionally run the waveform */
  val WithTywavesWaveforms: Boolean => Tywaves = (runWaves: Boolean) => Tywaves(runWaves, waitFor = true)

  /**
   * Generate tywaves info and optionally run the waveform without blocking sbt
   */
  val WithTywavesWaveformsGo: Boolean => Tywaves = (runWaves: Boolean) => Tywaves(runWaves, waitFor = false)

  /** Use this method to run a simulation */
  def simulate[T <: RawModule](
      module:   => T,
      settings: Seq[SimulatorSettings] = Seq(),
      simName:  String = "defaultSimulation",
  )(body: T => Unit): Unit = {

    // Create a new simulator instance
    val simulator = new TywavesSimulator

    val containTywaves = settings.exists(_.isInstanceOf[Tywaves])

    val finalSettings =
      if (containTywaves)
        settings ++ Seq(FirtoolArgs(TypedConverter.firtoolBaseArgs))
      // Seq(FirtoolArgs(Seq("-O=debug", "-g", "--emit-hgldd", "--split-verilog", "-o=WORK.v")))
      else settings

    simulator.simulate(module, finalSettings, simName)(body)

    if (simulator.finalTracePath.nonEmpty && containTywaves) {
      // Get the extra scopes created by ChiselSim backend: TOP, svsimTestbench, dut
      val extraScopes = Seq("TOP", Workspace.testbenchModuleName, "dut")

      // Create the debug info from the firtool and get the top module name
      // TODO: this may not be needed anymore, since the debug info can be generated directly from chiselsim, by giving the right options to firtool
      // But the problem is to call chiselstage with debug options
      TypedConverter.createDebugInfoHgldd(
        () => module,
        workingDir = simulator.wantedWorkspacePath,
        additionalFirtoolArgs = simulator.getFirtoolArgs,
      )

      // Run tywaves viewer if the Tywaves waveform generation is enabled by Tywaves(true)
      val (runWaves, waitFor) =
        if (finalSettings.contains(Tywaves(runWaves = true, waitFor = true))) { (true, true) }
        else if (finalSettings.contains(Tywaves(runWaves = true, waitFor = false))) { (true, false) }
        else { (false, false) }
      if (runWaves)
        TywavesInterface.run(
          vcdPath = simulator.finalTracePath.get,
          hglddDirPath = Some(TypedConverter.getDebugInfoDir(gOpt = true)),
          extraScopes = extraScopes,
          topModuleName = TypedConverter.getTopModuleName,
          waitFor = waitFor,
        )
    } else if (containTywaves)
      throw new Exception("Tywaves waveform generation requires a trace file. Please enable VcdTrace.")

  }

}
class TywavesSimulator extends ParametricSimulator

private[simulator] case class Tywaves(runWaves: Boolean, waitFor: Boolean) extends SimulatorSettings


package tywaves.utils

import java.nio.file.Paths
import scala.collection.mutable

class UniqueHashMap[K, V] extends mutable.HashMap[K, V] {

  private val _debugList = new mutable.ListBuffer[(K, V)]()
  private val debugList  = new mutable.ListBuffer[(K, V, Int)]()

  /** Same implementation of [[mutable.HashMap.put]] */
  def putOrReplace(key: K, value: V): Option[V] =
    super.put(key, value)

  override def put(key: K, value: V): Option[V] = {
    if (super.contains(key)) {
      this.foreach(Console.err.println(_))

      throw new Exception(s"Key $key already exists in the map. This is likely a bug in the parser.\n" +
        s"Probably a new ID mapping is needed or the element is parsed twice.\n" +
        s"Val is $value.")
    }
    if (_debugList.contains((key, value))) {
      // Get that element and update that element
      val index = _debugList.indexOf((key, value))
      debugList(index) = ((key, value, debugList(index)._3 + 1))
    } else {
      _debugList += ((key, value))
      debugList += ((key, value, 0))
    }
    super.put(key, value)
  }

  def dumpFile(file: String, header: String, append: Boolean = true): Unit = {
    val path = Paths.get(file)
    java.nio.file.Files.createDirectories(path.getParent)
    val bw = new java.io.BufferedWriter(new java.io.FileWriter(file, append))
    bw.write(s"\n$header\n")

      println(s"$key: $value")
    }
  }
  def debugLog(): Unit =
    debugList.foreach(println)
}

    this.foreach { case (key, value) =>
      bw.write(s"$key: $value\n")
    }
    bw.close()
  }


import chisel3.Bundle
import org.scalatest.flatspec.AnyFlatSpec

import scala.reflect.runtime.universe._

class GetNameTest extends AnyFlatSpec {
  behavior of "GetNameTest"
  def getClassName[T: TypeTag](obj: T): String = {
    val className = typeOf[T].typeSymbol.asClass.name.toString
    s"$className"
  }
  def getName(obj: Any): String =
    obj.getClass.getCanonicalName match {
      case null => "Unknown Class Name"
      case _    => obj.getClass.getCanonicalName
    }

  def printNames(obj: Bundle): Unit = {
    println("GetSimpleName: " + obj.getClass.getSimpleName)
    println("GetName: " + obj.getClass.getName)
    println("GetCanonicalName: " + obj.getClass.getCanonicalName)
    println("GetTypeName: " + obj.getClass.getTypeName)
    println("GetType: " + obj.className)
  }

  it should "get anonymous class name" in {
    val anonBundle = new Bundle {}
    printNames(anonBundle)
    println(anonBundle.className)
    println(getClassName(anonBundle))
//    println(getClassName(an))
  }

  it should "get named class name" in {
    val namedBundle = new MyBundle
    printNames(namedBundle)
    println(getClassName(namedBundle))

  }

  it should "get anonymous class name from named class" in {
    val anonBundle = new MyBundle {}
    printNames(anonBundle)
    println(getClassName(anonBundle))

  }

}


package bar

import chisel3._

class Baz(n: Int) extends Bundle {
  val a = UInt(n.W)
  val b = UInt(n.W)

  val nestedBundle = new Bundle {
    val z = Bool()
  }
}

class Bar extends Module {
  val io = IO(new Bundle {
    val a   = Input(Bool())
    val b   = Input(Bool())
    val out = Output(Bool())
  })

  val inputSum  = IO(Input(new Baz(8)))
  val outputSum = IO(Output(SInt(8.W)))

  when(inputSum.nestedBundle.z === true.B) {
    outputSum := inputSum.a.asSInt + inputSum.b.asSInt
  }.otherwise {
    outputSum := inputSum.a.asSInt - inputSum.b.asSInt
  }

}

object MainBar extends App {
  import circt.stage.ChiselStage

  println(
    ChiselStage.emitSystemVerilog(
      new Bar,
      firtoolOpts = Array(
        "-O=debug",
        "-g",
        "-emit-hgldd",
        "-output-final-mlir=Foo.mlir",
      ),
    )
  )
}

  val cable =
    Wire(Bool()) // do not use reserved verilog words as val names (val wire) -> tywaves-demo does not work for them yet
  cable := io.a & io.b


package bar

import org.scalatest.flatspec.AnyFlatSpec
import tywaves.simulator.ParametricSimulator._
import tywaves.simulator.simulatorSettings._
import chisel3.fromBooleanToLiteral
import tywaves.simulator.TywavesSimulator

class BarTest extends AnyFlatSpec {
  behavior of "BarTest"
  it should "trace simple bar" in {
    simulate(
      module = new Bar,
      settings = Seq(VcdTraceWithUnderscore, SaveWorkdir),
      simName = "trace_simple_bar",
    ) { c =>
      c.clock.step()
      c.io.a.poke(true)
      c.io.b.poke(false)
      c.io.out.expect(false.B)

      c.clock.step()
      c.io.a.poke(true)
      c.io.b.poke(true)
      c.io.out.expect(true.B)

      c.clock.step()
      c.clock.step(cycles = 10)
    }
  }

  it should "trace simple bar with tywaves" in {
    import tywaves.simulator.TywavesSimulator._

    simulate(
      module = new Bar,
      settings = Seq(VcdTraceWithUnderscore, SaveWorkdir, WithTywavesWaveforms(false)),
      simName = "trace_simple_bar_with_tywaves",
    ) { c =>
      c.clock.step()
      c.io.a.poke(true)
      c.io.b.poke(false)
      c.io.out.expect(false.B)

      c.clock.step()
      c.io.a.poke(true)
      c.io.b.poke(true)

      c.clock.step(cycles = 10)
    }
  }
}


package foo

import chisel3._
import circt.stage.ChiselStage

class MyBundle extends Simple {
  val a = UInt(8.W)
  val b = UInt(8.W)
  val c = UInt(8.W)

  val bundle = new Bundle {
    val z = Bool()
  }
}

class Foo extends Module {
  val x    = IO(Input(Bool()))
  val s    = IO(Input(new MyBundle))
  val io_a = Wire(Bool())

  val io = IO(new Bundle {
    val a   = Input(Bool())
    val b   = Input(Bool())
    val out = Output(UInt(8.W))
  })

  //  val vec = VecInit(Seq.fill(4)(0.U(8.W)))
  //  val reg = RegInit(0.U(8.W))

  dontTouch(io_a)
  //  dontTouch(vec)

  io_a   := 1.U
  io.out := io.a + io.b

  printf(p"s: $s\n")
}

object MainFoo extends App {

  println(
    ChiselStage.emitSystemVerilog(
      new Foo,
      firtoolOpts = Array(
        "-O=debug",
        "-g",
        "-emit-hgldd",
        "-output-final-mlir=Fooa.mlir",
      ),
    )
  )

}


package foo

// The high level simulation API: it uses svsim internally
import tywaves.simulator.ParametricSimulator._
import org.scalatest.flatspec.AnyFlatSpec
import tywaves.simulator.simulatorSettings._

object RunFoo {

  /** Run multiple times */
  def apply(c: => Foo): Unit = {
    // Inputs and expected results
    val a = Seq(0, 1, 0, 1)
    val b = Seq(0, 0, 1, 1)

    // Reset
    c.io.a.poke(false)
    c.io.b.poke(true)
    c.clock.step()

    for (i <- a.zip(b)) {
      c.io.a.poke(i._1)
      c.io.b.poke(i._2)
      c.clock.step()
    }
  }
}

class FooTest extends AnyFlatSpec {
  behavior of "FooTest"

  it should "trace with underscore" in {
    // val testName = testNames.headOption.getOrElse("Unknown Test")

    simulate(new Foo, Seq(VcdTrace), simName = "trace") { c =>
      c.io.a.poke(true)
      c.io.b.poke(0)
    }
  }

}

    simulate(new Foo, Seq(VcdTraceWithUnderscore), simName = "trace_with_underscore") {
      println("Running test: " + it)
      RunFoo(_)
    }


package gcd

import chisel3._

/** A simple module useful for testing Chisel generation and testing */
class GCD extends Module {
  val io = IO(new Bundle {
    val a             = Input(UInt(32.W))
    val b             = Input(UInt(32.W))
    val loadValues    = Input(Bool())
    val result        = Output(UInt(32.W))
    val resultIsValid = Output(Bool())
  })

  val x = Reg(UInt(32.W))
  val y = Reg(UInt(32.W))

  when(x > y)(x := x -% y).otherwise(y := y -% x)

  io.result        := x
  io.resultIsValid := y === 0.U
}

when(io.loadValues) { x := io.a; y := io.b }


package gcd

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.must.Matchers

import tywaves.simulator._
import tywaves.simulator.ParametricSimulator._
import tywaves.simulator.simulatorSettings._
import chisel3._
//import chisel3._
class GCDTest extends AnyFunSpec with Matchers {
  describe("ParametricSimulator") {
    it("runs GCD correctly") {
      simulate(new GCD(), Seq(VcdTrace, SaveWorkdirFile("gcdWorkDir"))) { gcd =>

        gcd.clock.step()
        gcd.io.loadValues.poke(0.B)
        gcd.clock.stepUntil(sentinelPort = gcd.io.resultIsValid, sentinelValue = 1, maxCycles = 10)
        gcd.io.resultIsValid.expect(true.B)
        gcd.io.result.expect(12)
      }
    }
  }

  describe("TywavesSimulator") {
    it("runs GCD correctly") {
      import TywavesSimulator._

      simulate(
        new GCD(),
        Seq(VcdTrace, WithTywavesWaveforms(false), WithFirtoolArgs(Seq("-g", "--emit-hgldd")), SaveWorkdir),
        simName = "runs_GCD_correctly",
      ) {
        gcd =>
          gcd.io.a.poke(24.U)
          gcd.io.b.poke(36.U)
          gcd.io.loadValues.poke(1.B)
          gcd.clock.step()
          gcd.io.loadValues.poke(0.B)
          gcd.clock.stepUntil(sentinelPort = gcd.io.resultIsValid, sentinelValue = 1, maxCycles = 10)
          gcd.io.resultIsValid.expect(true.B)
          gcd.io.result.expect(12)
      }
    }
  }

}

        gcd.io.a.poke(24.U)
        gcd.io.b.poke(36.U)


package hierarchicalmodules

import chisel3._
import chisel3.util.Counter

class Blink(period: Int) extends Module {
  assert(period > 0, "limit must be greater than 0")
  val io = IO(new Bundle {
    val enable: Bool = Input(Bool())
    val led:    Bool = Output(Bool())
  })

  val cnt:    Counter = Counter(period)
  val ledReg: Bool    = RegInit(false.B)

object MainBlink extends App {
  import circt.stage.ChiselStage

  println(
    ChiselStage.emitSystemVerilog(
      new Blink(4),
      firtoolOpts = Array(
        "-O=debug",
        "-g",
        "--disable-all-randomization",
        "--strip-debug-info",
      ),
    )
  )
}

  when(io.enable) {
    when(cnt.inc() && cnt.value === (period - 1).U) {
      ledReg := ~ledReg
    }
  }


package hierarchicalmodules

import chisel3.fromBooleanToLiteral
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.must.Matchers

class BlinkTest extends AnyFunSpec with Matchers {

  private def blinkTb(dut: => Blink): Unit = {
    import chisel3.simulator.PeekPokeAPI._
    dut.reset.poke(true.B)
    dut.io.enable.poke(true.B)
    dut.clock.step(5)
    dut.reset.poke(false.B)

    dut.io.led.expect(false.B)
    dut.clock.step() // 1
    dut.io.led.expect(false.B)
    dut.clock.step() // 2
    dut.io.led.expect(false.B)
    dut.clock.step() // 3
    dut.io.led.expect(false.B)
    dut.clock.step() // 4
    dut.io.led.expect(true.B)
    dut.clock.step() // 5
    dut.io.led.expect(true.B)
    dut.clock.step() // 6
    dut.io.led.expect(true.B)
    dut.clock.step()
    dut.clock.step(30)
  }

  describe("Blink with parametric simulator") {
    import tywaves.simulator.ParametricSimulator._
    import tywaves.simulator.simulatorSettings._

    it("should work") {
      simulate(
        new Blink(4),
        Seq(VcdTrace, WithFirtoolArgs(Seq("-O=debug", "-g"))),
        simName = "blink_with_parametric_sim_should_work",
      ) { dut =>
        blinkTb(dut)
      }
    }
  } // end of describe("Blink with parametric simulator")

  describe("Blink with tywaves simulator") {
    import tywaves.simulator.TywavesSimulator._
    import tywaves.simulator.simulatorSettings._

    it("should work") {
      simulate(
        new Blink(4),
        Seq(VcdTrace, WithTywavesWaveforms(false), SaveWorkdirFile("workdir")),

        simName = "blink_with_tywaves_sim_should_work",
      ) { dut =>
        blinkTb(dut)
      }


package hierarchicalmodules

import chisel3._

class SubBlink(n: Int) extends Module {
  val io = IO(new Bundle {
    val enable = Input(Bool())
    val led    = Output(Bool())
  })
  val blinkerx = Module(new Blink(n))

  blinkerx.io.enable := io.enable
  io.led             := blinkerx.io.led
}

class AnotherModule extends Module {
  val out = IO(Output(UInt(2.W)))

  val outReg = RegInit(false.B)
  outReg := ~outReg
  out    := outReg
}
class AMultiBlink extends Module {
  val io = IO(new Bundle {
    val enable: Bool = Input(Bool())
    val leds:   UInt = Output(UInt(4.W))
  })

  val anotherModule: AnotherModule = Module(new AnotherModule)

  val blinker:  SubBlink = Module(new SubBlink(5))
  val blinker2: Blink    = Module(new Blink(3))
  val blinker3: Blink    = Module(new Blink(2))

  blinker.io.enable  := io.enable
  blinker2.io.enable := io.enable
  blinker3.io.enable := io.enable

  when(anotherModule.out === 1.U) {
    blinker.io.enable  := false.B
    blinker2.io.enable := false.B
  }
  io.leds :=
    (blinker.io.led << 3).asUInt + (blinker.io.led << 2).asUInt + (blinker.io.led << 1).asUInt + (blinker.io.led << 0).asUInt

}

object MainMultiBlink extends App {
  import circt.stage.ChiselStage

  println(

    ChiselStage.emitSystemVerilog(
      new AMultiBlink,
      firtoolOpts = Array(
        "-O=debug",
        "-g",


package hierarchicalmodules

import chisel3.fromBooleanToLiteral
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.must.Matchers

class MultiBlinkTest extends AnyFunSpec with Matchers {

  private def blinkTb(dut: => AMultiBlink): Unit = {
    import chisel3.simulator.PeekPokeAPI._
    dut.reset.poke(true.B)
    dut.io.enable.poke(false.B)
    dut.clock.step(5)
    dut.reset.poke(false.B)
    dut.io.enable.poke(true.B)

    dut.clock.step() // 1
    dut.clock.step() // 2
    dut.clock.step() // 3
    dut.clock.step() // 4

  describe("Multi Blink with parametric simulator") {
    import tywaves.simulator.ParametricSimulator._
    import tywaves.simulator.simulatorSettings._

    it("should work") {
      simulate(
        new AMultiBlink,
        Seq(VcdTrace, WithFirtoolArgs(Seq("-O=debug", "-g"))),
        simName = "multiblink_with_parametric_sim_should_work",
      ) { dut =>
        blinkTb(dut)
      }
    }
  } // end of describe("MultiBlink with parametric simulator")

  describe("MultiBlink with tywaves simulator") {
    import tywaves.simulator.TywavesSimulator._
    import tywaves.simulator.simulatorSettings._

    it("should work") {
      simulate(
        new AMultiBlink,
        Seq(VcdTrace, WithTywavesWaveforms(false), SaveWorkdirFile("workdir")),
        simName = "multiblink_with_tywaves_sim_should_work",
      ) { dut =>
        blinkTb(dut)
      }
    }
  } // end of describe("Blink with tywaves simulator")
}

    dut.clock.step() // 5
    dut.clock.step() // 6
    dut.clock.step()


package hierarchicalmodules

import chisel3.{Bits, Data, Module, Printable, UInt, UIntFactory, Wire, dontTouch, fromBooleanToLiteral, fromIntToWidth}
import chisel3.util.Counter
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.must.Matchers
import tywaves.simulator.TraceVcd

class UseCounter extends AnyFunSpec with Matchers {

  class Char {
    val value: UInt = UInt(8.W)

  }
  object Char {
    def apply(): Char = new Char
  }

  class MyCounter extends Module {
    import chisel3._

    val out: UInt = IO(Output(UInt(8.W)))

    val cnt: Counter = Counter(4)

    cnt.inc()
    out := cnt.value
  }

  describe("Blink with tywaves simulator") {
    import tywaves.simulator.TywavesSimulator._
    import tywaves.simulator.simulatorSettings._

    it("should work") {
      simulate(
        new MyCounter,
        Seq(VcdTrace, WithTywavesWaveforms(false), SaveWorkdirFile("workdir")),
        simName = "blink_with_tywaves_sim_should_work",
      ) { dut =>
        dut.clock.step(10)
      }
    }
  }
}


package memories

import chisel3._
import chisel3.util.log2Ceil

class MemIOBundle[T <: Data](depth: Int, t: T) extends Bundle {
  val rdAddr = Input(UInt(log2Ceil(depth).W))
  val rdData = Output(t)
  val wrEna  = Input(Bool())
  val wrData = Input(t)

/** A simple  module for testing memories in Tywaves */
class BlockMem[T <: Data](depth: Int, t: T) extends Module {
  val io = IO(new MemIOBundle(depth, t))

  val mem = SyncReadMem(depth, t)
  io.rdData := mem.read(io.rdAddr)

  when(io.wrEna) {
    mem.write(io.wrAddr, io.wrData)
  }
}

  val wrAddr = Input(UInt(log2Ceil(depth).W))
}


package memories

import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.must.Matchers

import tywaves.simulator._
import tywaves.simulator.simulatorSettings._
import chisel3._

class BlockMemTest extends AnyFunSpec with Matchers {
  describe("TywavesSimulator") {
    import TywavesSimulator._

    it("runs BlockMem of UInt8") {
      val t = UInt(8.W)
      simulate(new BlockMem(15, t), Seq(VcdTrace, WithTywavesWaveforms(false)), simName = "runs_mem_uint8")(dut =>
        dut.clock.step(2)
      )
    }

    it("runs BlockMem of Bundle") {

      class ComplexElement extends Bundle {
        val a = new Bundle {
          val subA1 = UInt(8.W)
          val subA2 = SInt(8.W)
        }
        val payload = Bits(8.W)
      }

      val t = new ComplexElement
      simulate(
        new BlockMem(4, t),
        Seq(VcdTrace, WithTywavesWaveforms(false), SaveWorkdirFile("workdir")),
        simName = "runs_mem_bundle",
      )(dut => dut.clock.step(2))
    }

    it("runs BlockMem of Vec") {
      val t = Vec(4, UInt(8.W))
      simulate(new BlockMem(4, t), Seq(VcdTrace, WithTywavesWaveforms(false)), simName = "runs_mem_vec")(dut =>
        dut.clock.step(2)
      )
    }

    it("runs BlockMem of Enum") {
      object SelType extends ChiselEnum { val A, B, C = Value }
      val t = SelType()
      simulate(new BlockMem(4, t), Seq(VcdTrace, WithTywavesWaveforms(false)), simName = "runs_mem_enum")(dut =>
        dut.clock.step(2)
      )
    }

    it("runs BlockMem of Enum in Bundle") {
      object SelType extends ChiselEnum { val A, B, C = Value }
      class ComplexElement extends Bundle {
        val sel     = SelType()
        val payload = Bits(8.W)
      }

      val t = new ComplexElement
      simulate(new BlockMem(4, t), Seq(VcdTrace, WithTywavesWaveforms(false)), simName = "runs_mem_enum_bundle")(dut =>
        dut.clock.step(2)
      )
    }

    it("runs BlockMem of Enum in Vec") {

      val t = Vec(4, SelType())
      simulate(new BlockMem(4, t), Seq(VcdTrace, WithTywavesWaveforms(false)), simName = "runs_mem_enum_vec")(dut =>
        dut.clock.step(2)
      )
    }

    it("runs BlockMem of Enum in 2D-Vec") {
<TARGET>
      val t = Vec(4, Vec(2, SelType()))
      simulate(new BlockMem(4, t), Seq(VcdTrace, WithTywavesWaveforms(false)), simName = "runs_mem_enum_2d_vec")(dut =>
        dut.clock.step(2)
      )
    }

  }
}


package tywaves.simulator

import gcd.GCD
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.must.Matchers

class ImportSimulatorReverseOrder extends AnyFunSpec with Matchers {

          gcd.io.loadValues.poke(true.B)
      }
    }
    it("Should import chisel after tywaves") {
      import tywaves.simulator.TywavesSimulator._
      import chisel3._
      simulate(new GCD()) {
        gcd =>
          gcd.clock.step()
          gcd.io.loadValues.poke(true.B)
      }
    }
  }

}

  describe("Issue 27") {
    it("Should import chisel before tywaves") {
      import chisel3._
      import tywaves.simulator.TywavesSimulator._


package tywaves.simulator

import tywaves.simulator.ParametricSimulator._
import tywaves.simulator.simulatorSettings._
import chisel3._
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.must.Matchers

import java.nio.file.{Files, Paths}

import gcd.GCD

class ParametricSimulatorSpec extends AnyFunSpec with Matchers {

  private def gcdTb(gcd: => GCD): Unit = {
    gcd.io.a.poke(24.U)
    gcd.io.b.poke(36.U)
    gcd.io.loadValues.poke(1.B)
    gcd.clock.step()
    gcd.io.loadValues.poke(0.B)
    gcd.clock.stepUntil(sentinelPort = gcd.io.resultIsValid, sentinelValue = 1, maxCycles = 10)
    gcd.io.resultIsValid.expect(true.B)
    gcd.io.result.expect(12)
  }

  describe("ParametricSimulator Runs") {

    it("runs GCD correctly without settings") {
      simulate(new GCD())(gcd => gcdTb(gcd))
    }

    it("runs GCD with VCD trace file") {
      simulate(new GCD(), Seq(VcdTrace))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/trace.vcd")))
    }

    it("runs GCD with VCD underscore trace") {
      simulate(new GCD(), Seq(VcdTraceWithUnderscore))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/trace_underscore.vcd")))
    }

    it("runs GCD with VCD trace file with name") {
      simulate(new GCD(), Seq(VcdTrace, NameTrace("gcdTbTraceName")))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/gcdTbTraceName.vcd")))
    }

    it("runs underscore when VcdTrace and VcdTraceWithUnderscore are used") {
      simulate(new GCD(), Seq(VcdTrace, VcdTraceWithUnderscore))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/trace_underscore.vcd")))

      simulate(new GCD(), Seq(VcdTraceWithUnderscore, VcdTrace))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/trace_underscore.vcd")))
    }

    it("runs GCD with VCD trace file with name and VCD underscore trace") {
      simulate(new GCD(), Seq(VcdTrace, VcdTraceWithUnderscore, NameTrace("gcdTb1")))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/gcdTb1_underscore.vcd")))

      simulate(new GCD(), Seq(VcdTraceWithUnderscore, VcdTrace, NameTrace("gcdTb2")))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/gcdTb2_underscore.vcd")))

      simulate(new GCD(), Seq(NameTrace("gcdTb3"), VcdTraceWithUnderscore, VcdTrace))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/gcdTb3_underscore.vcd")))
    }

    it("uses a name for the simulation") {
      simulate(new GCD(), Seq(VcdTrace, NameTrace("gcdTb1")), simName = "use_a_name_for_the_simulation")(gcd =>
        gcdTb(gcd)
      )
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/use_a_name_for_the_simulation/gcdTb1.vcd")))
    }

        gcdTb(gcd)
      )
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/myWorkdir2")))
      assert(Files.exists(
        Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/myWorkdir2/support-artifacts/GCD.dd")
      ))
    }

  }

  describe("ParametricSimulator Exceptions") {

    it("throws an exception when NameTrace is used without VcdTrace or VcdTraceWithUnderscore") {
      intercept[Exception] {
        simulate(new GCD(), Seq(NameTrace("")))(_ => gcdTb _)
      }
    }

    it("throws an exception with two or more NameTrace") {
      intercept[Exception] {
        simulate(new GCD(), Seq(VcdTrace, NameTrace("a"), NameTrace("b")))(_ => gcdTb _)
      }
    }

    it("throws an exception with two or more SaveWorkdir") {
      intercept[Exception] {
        simulate(new GCD(), Seq(SaveWorkdir, SaveWorkdir))(_ => gcdTb _)
      }
    }

    it("throws an exception with two or more SaveWorkdirFile") {
      intercept[Exception] {
        simulate(new GCD(), Seq(SaveWorkdirFile("a"), SaveWorkdirFile("b")))(_ => gcdTb _)
      }
    }

  }
}

    it("save the workdir with a name") {
      simulate(new GCD(), Seq(VcdTrace, SaveWorkdirFile("myWorkdir")))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/ParametricSimulator/defaultSimulation/myWorkdir")))
    }


package tywaves.simulator

import tywaves.simulator.TywavesSimulator._
import tywaves.simulator.simulatorSettings._
import chisel3._
import gcd.GCD
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.must.Matchers

import java.nio.file.{Files, Paths}

class TywavesSimulatorSpec extends AnyFunSpec with Matchers {

  private def gcdTb(gcd: => GCD): Unit = {
    gcd.io.a.poke(24.U)
    gcd.io.b.poke(36.U)
    gcd.io.loadValues.poke(1.B)
    gcd.clock.step()
    gcd.io.loadValues.poke(0.B)
    gcd.clock.stepUntil(sentinelPort = gcd.io.resultIsValid, sentinelValue = 1, maxCycles = 10)
    gcd.io.resultIsValid.expect(true.B)
    gcd.io.result.expect(12)
  }

  describe("New TywavesFunctionalities") {

    it("runs GCD with waveform generation") {
      simulate(new GCD(), Seq(VcdTrace, WithTywavesWaveforms(false)), simName = "runs_gcd_with_waveform_generation") {
        gcd =>
          gcdTb(gcd)
      }

      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/runs_gcd_with_waveform_generation/trace.vcd")))
      assert(Files.exists(
        Paths.get(
          "test_run_dir/GCD/TywavesSimulator/runs_gcd_with_waveform_generation/hgldd/debug/GCD.dd"
        )
      ))
    }

    it("runs GCD with waveform generation and custom name trace") {
      simulate(
        new GCD(),
        Seq(VcdTrace, NameTrace("gcdTest"), WithTywavesWaveforms(false)),
        simName = "runs_gcd_with_waveform_generation",
      ) { gcd =>
        gcdTb(gcd)
      }

      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/runs_gcd_with_waveform_generation/gcdTest.vcd")))
      assert(Files.exists(
        Paths.get(
          "test_run_dir/GCD/TywavesSimulator/runs_gcd_with_waveform_generation/hgldd/debug/GCD.dd"
        )
      ))
    }

    it("raises an exception when Tywaves is used without VcdTrace") {
      intercept[Exception] {
        simulate(new GCD(), Seq(WithTywavesWaveforms(false)))(_ => gcdTb _)
      }
    }

  }

  describe("Tywaves with ParametricSimulator Functionalities") {

    it("runs GCD correctly without settings") {
      simulate(new GCD())(gcd => gcdTb(gcd))
    }

    it("runs GCD with VCD trace file") {
      simulate(new GCD(), Seq(VcdTrace))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/trace.vcd")))
    }

    it("runs GCD with VCD underscore trace") {
      simulate(new GCD(), Seq(VcdTraceWithUnderscore))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/trace_underscore.vcd")))
    }

    it("runs GCD with VCD trace file with name") {
      simulate(new GCD(), Seq(VcdTrace, NameTrace("gcdTbTraceName")))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/gcdTbTraceName.vcd")))
    }

    it("runs underscore when VcdTrace and VcdTraceWithUnderscore are used") {
      simulate(new GCD(), Seq(VcdTrace, VcdTraceWithUnderscore))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/trace_underscore.vcd")))

      simulate(new GCD(), Seq(VcdTraceWithUnderscore, VcdTrace))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/trace_underscore.vcd")))
    }

    it("runs GCD with VCD trace file with name and VCD underscore trace") {
      simulate(new GCD(), Seq(VcdTrace, VcdTraceWithUnderscore, NameTrace("gcdTb1")))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/gcdTb1_underscore.vcd")))

      simulate(new GCD(), Seq(VcdTraceWithUnderscore, VcdTrace, NameTrace("gcdTb2")))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/gcdTb2_underscore.vcd")))

      simulate(new GCD(), Seq(NameTrace("gcdTb3"), VcdTraceWithUnderscore, VcdTrace))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/gcdTb3_underscore.vcd")))
    }

    it("uses a name for the simulation") {

      simulate(new GCD(), Seq(VcdTrace, SaveWorkdirFile("myWorkdir")))(gcd => gcdTb(gcd))
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/myWorkdir")))
    }

    it("uses firtool args") {
      simulate(new GCD(), Seq(WithFirtoolArgs(Seq("-g", "--emit-hgldd")), SaveWorkdirFile("myWorkdir2")))(gcd =>
        gcdTb(gcd)
      )
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/myWorkdir2")))
      assert(Files.exists(
        Paths.get("test_run_dir/GCD/TywavesSimulator/defaultSimulation/myWorkdir2/support-artifacts/GCD.dd")
      ))
    }

  }

  describe("TywavesSimulator Exceptions") {
    it("throws an exception when NameTrace is used without VcdTrace or VcdTraceWithUnderscore") {
      intercept[Exception] {
        simulate(new GCD(), Seq(NameTrace("")))(_ => gcdTb _)
      }
    }

    it("throws an exception with two or more NameTrace") {
      intercept[Exception] {
        simulate(new GCD(), Seq(VcdTrace, NameTrace("a"), NameTrace("b")))(_ => gcdTb _)
      }
    }

    it("throws an exception with two or more SaveWorkdir") {
      intercept[Exception] {
        simulate(new GCD(), Seq(SaveWorkdir, SaveWorkdir))(_ => gcdTb _)
      }
    }

    it("throws an exception with two or more SaveWorkdirFile") {
      intercept[Exception] {
        simulate(new GCD(), Seq(SaveWorkdirFile("a"), SaveWorkdirFile("b")))(_ => gcdTb _)
      }
    }

  }
}

      simulate(new GCD(), Seq(VcdTrace, NameTrace("gcdTb1")), simName = "use_a_name_for_the_simulation")(gcd =>
        gcdTb(gcd)
      )
      assert(Files.exists(Paths.get("test_run_dir/GCD/TywavesSimulator/use_a_name_for_the_simulation/gcdTb1.vcd")))


// See README.md for license details.

package ethcontroller.design

import Chisel._
import ethcontroller.interfaces.MIIChannel
import ethcontroller.protocols.EthernetConstants
import ocp._

/**
  * The RX channel of the Ethernet controller manages both
  * the RX MII channel and implements FIFO access to the connected buffers
  *
  * @param numFrameBuffers the numbers of buffers used
  * @param timeStampWidth  the timestamp bit-width
  */
class EthRxController(numFrameBuffers: Int, timeStampWidth: Int) extends Module {
  val io = new Bundle() {
    val ocp = new OcpCoreSlavePort(32, 32)
    val ramPorts = Vec.fill(numFrameBuffers) {
      new OcpCoreMasterPort(32, 32)
    }
    val miiChannel = new MIIChannel().asInput
    val rtcTimestamp = UInt(INPUT, width = timeStampWidth)
  }

  /**
    * Constants and types defs
    */
  val constRamIdWidth = log2Ceil(numFrameBuffers)
  val constRamAddrWidth = log2Ceil(EthernetConstants.constEthFrameLength)
  val stWaitSFD :: stCollectPayload :: Nil = Enum(UInt(), 2)
  val miiRx = Module(new MIIRx())

  /**
    * Registers
    */
  val stateReg = Reg(init = stWaitSFD)
  val ethByteReg = RegEnable(miiRx.io.ethByte, miiRx.io.ethByteDv)
  val ethByteDvReg = Reg(next = miiRx.io.ethByteDv)
  val ocpMasterReg = Reg(next = io.ocp.M)
  val wrRamIdReg = Reg(init = UInt(0, width = constRamIdWidth))
  val rdRamIdReg = Reg(init = UInt(0, width = constRamIdWidth))
  val byteCntReg = Reg(init = UInt(0, width = constRamAddrWidth))
  val ramMasterRegs = Vec.fill(numFrameBuffers) {
    Reg(new OcpCoreMasterSignals(constRamAddrWidth, 32))
  }
  val ocpDataReg = Reg(init = Bits(0, width = 32))
  val ocpRespReg = Reg(init = OcpResp.NULL)
  val rxEnReg = Reg(init = true.B)
  val rxFrameSizeReg = Reg(init = UInt(0, width = constRamAddrWidth)) //bytes
  val fifoCountReg = Reg(init = UInt(0, width = constRamIdWidth + 1))
  val fifoEmptyReg = Reg(init = true.B)
  val fifoFullReg = Reg(init = false.B)
  val fifoPopReg = Reg(init = false.B)
  val fifoPushReg = Reg(init = false.B)
  val macRxFilterReg = Reg(init = UInt(0, width = EthernetConstants.constEthMacLength))
  val ethRxCtrlRegMap = Map(
    "rxEn" -> Map("Reg" -> rxEnReg, "Addr" -> Bits("h00")),
    "frameSize" -> Map("Reg" -> rxFrameSizeReg, "Addr" -> Bits("h01")),
    "fifoCount" -> Map("Reg" -> fifoCountReg, "Addr" -> Bits("h02")),
    "fifoEmpty" -> Map("Reg" -> fifoEmptyReg, "Addr" -> Bits("h03")),
    "fifoFull" -> Map("Reg" -> fifoFullReg, "Addr" -> Bits("h04")),
    "fifoPop" -> Map("Reg" -> fifoPopReg, "Addr" -> Bits("h05")),
    "fifoPush" -> Map("Reg" -> fifoPushReg, "Addr" -> Bits("h06")),
    "macFilter" -> Map("Reg" -> macRxFilterReg, "Addr" -> Bits("h07"))
  )

  /**
    * Fifo Management
    */
  fifoEmptyReg := fifoCountReg === 0.U
  fifoFullReg := fifoCountReg === numFrameBuffers.U

  /**
    * State machine Control
    */
  val ramWrSelOH = UIntToOH(wrRamIdReg)
  val ramRdSelOH = UIntToOH(rdRamIdReg)

  val ocpSelRam = ~ocpMasterReg.Addr(constRamAddrWidth) && ocpMasterReg.Cmd =/= OcpCmd.IDLE
  val validRamPop = fifoPopReg && !fifoEmptyReg
  val validRamPush = fifoPushReg && !fifoFullReg

  val ethRamWrEn = ethByteDvReg && stateReg === stCollectPayload
  val ocpRamRdEn = ocpMasterReg.Cmd === OcpCmd.RD && ocpSelRam

  fifoPushReg := false.B
  fifoPopReg := false.B
  switch(stateReg) {
    is(stWaitSFD) {
      when(miiRx.io.ethSof && !fifoFullReg) {
        stateReg := stCollectPayload
      }
    }
    is(stCollectPayload) {
      when(miiRx.io.ethEof) {
        stateReg := stWaitSFD
        rxFrameSizeReg := byteCntReg
        byteCntReg := 0.U
        fifoPushReg := true.B
      }
    }
  }

  when(ethRamWrEn) {
    byteCntReg := byteCntReg + 1.U
  }

  when(validRamPop) {
    rdRamIdReg := rdRamIdReg + 1.U
    fifoCountReg := fifoCountReg - 1.U
  }

  when(validRamPush) {
    wrRamIdReg := wrRamIdReg + 1.U
    fifoCountReg := fifoCountReg + 1.U
  }

  /**
    * Ram Master Control
    */
  for (ramId <- 0 until numFrameBuffers) {
    ramMasterRegs(ramId).Addr := RegNext(Mux(ramId.U === wrRamIdReg && ocpSelRam, byteCntReg(constRamAddrWidth - 1, 2), ocpMasterReg.Addr(constRamAddrWidth - 1, 2)))
    ramMasterRegs(ramId).ByteEn := RegNext(Mux(ramId.U === wrRamIdReg && ocpSelRam, UIntToOH(byteCntReg(1, 0), width = 4), Mux(ocpRamRdEn, ocpMasterReg.ByteEn, Bits("b0000"))))

  /**
    * Regs Master Control
    */
  ocpRespReg := OcpResp.NULL
  when(ocpSelRam) {
    ocpDataReg := io.ramPorts(rdRamIdReg).S.Data
    ocpRespReg := Mux(!fifoFullReg, io.ramPorts(rdRamIdReg).S.Resp, OcpResp.FAIL)
  }.otherwise {
    when(ocpMasterReg.Cmd === OcpCmd.RD) {
      ocpRespReg := OcpResp.DVA
      when(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("rxEn")("Addr")) {
        ocpDataReg := ethRxCtrlRegMap("rxEn")("Reg")
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("frameSize")("Addr")) {
        ocpDataReg := ethRxCtrlRegMap("frameSize")("Reg")
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoCount")("Addr")) {
        ocpDataReg := ethRxCtrlRegMap("fifoCount")("Reg")
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoEmpty")("Addr")) {
        ocpDataReg := ethRxCtrlRegMap("fifoEmpty")("Reg")
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoFull")("Addr")) {
        ocpDataReg := ethRxCtrlRegMap("fifoFull")("Reg")
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoPop")("Addr")) {
        ocpDataReg := ethRxCtrlRegMap("fifoPop")("Reg")
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoPush")("Addr")) {
        ocpDataReg := ethRxCtrlRegMap("fifoPush")("Reg")
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("macFilter")("Addr")) {
        ocpDataReg := ethRxCtrlRegMap("macFilter")("Reg")
      }
    }.elsewhen(ocpMasterReg.Cmd === OcpCmd.WR) {
      when(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("rxEn")("Addr")) {
        ethRxCtrlRegMap("rxEn")("Reg") := ocpMasterReg.Data.orR
        ocpRespReg := OcpResp.DVA
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("frameSize")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoCount")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoEmpty")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoFull")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoPop")("Addr")) {
        ethRxCtrlRegMap("fifoPop")("Reg") := true.B //set on write and reset in the next clock cycle
        ocpRespReg := OcpResp.DVA //is the responsibility of the master to check first the count ?
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("fifoPush")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(4, 2) === ethRxCtrlRegMap("macFilter")("Addr")) {
        ethRxCtrlRegMap("macFilter")("Reg") := ocpMasterReg.Data
        ocpRespReg := OcpResp.DVA
      }
    }.otherwise {
      ocpRespReg := OcpResp.NULL
    }
  }

  /**
    * I/O plumbing
    */
  io.ocp.S.Data := ocpDataReg
  io.ocp.S.Resp := ocpRespReg
  miiRx.io.miiChannel <> io.miiChannel
  miiRx.io.rxEn := ethRxCtrlRegMap("rxEn")("Reg")
  for (ramId <- 0 until numFrameBuffers) {
    io.ramPorts(ramId).M := ramMasterRegs(ramId)
  }
}

object EthRxController extends App {
  chiselMain(Array[String]("--backend", "v", "--targetDir", "generated/" + this.getClass.getSimpleName.dropRight(1)),
    () => Module(new EthRxController(2, 64)))
}

    ramMasterRegs(ramId).Cmd := RegNext(Mux(ramId.U === wrRamIdReg && ocpSelRam, OcpCmd.WR, Mux(ocpRamRdEn, OcpCmd.RD, OcpCmd.IDLE)))
    ramMasterRegs(ramId).Data := RegNext(ethByteReg)


// See README.md for license details.

package ethcontroller.design

import Chisel._
import ethcontroller.interfaces.MIIChannel
import ethcontroller.protocols.EthernetConstants
import ocp._

/**
  * The TX channel of the Ethernet controller manages both
  * the TX MII channel and implements FIFO access to the connected buffers
  *
  * @param numFrameBuffers the numbers of buffers used
  * @param timeStampWidth  the timestamp bit-width
  */
class EthTxController(numFrameBuffers: Int, timeStampWidth: Int) extends Module {
  val io = new Bundle() {
    val ocp = new OcpCoreSlavePort(32, 32)
    val ramPorts = Vec.fill(numFrameBuffers) {
      new OcpCoreMasterPort(32, 32)
    }
    val miiChannel = new MIIChannel()
    val rtcTimestamp = UInt(INPUT, width = timeStampWidth)
  }

  /**
    * Constants and types defs
    */
  val constRamIdWidth = log2Ceil(numFrameBuffers)
  val constRamAddrWidth = log2Ceil(EthernetConstants.constEthFrameLength)
  val stWait :: stTxPreamble :: stTxFrame :: stTxFCS :: stIFG :: stEoF :: Nil = Enum(UInt(), 6)
  val miiTx = Module(new MIITx())

  /**
    * Registers
    */
  val stateReg = Reg(init = stWait)
  val ocpMasterReg = Reg(next = io.ocp.M)
  val wrRamIdReg = Reg(init = UInt(0, width = constRamIdWidth))
  val rdRamIdReg = Reg(init = UInt(0, width = constRamIdWidth))
  val byteCntReg = Reg(init = UInt(0, width = constRamAddrWidth))
  val ethRamRdEn = Reg(init = false.B)
  val ramMasterRegs = Vec.fill(numFrameBuffers) {
    Reg(new OcpCoreMasterSignals(constRamAddrWidth, 32))
  }
  val ocpDataReg = Reg(init = Bits(0, width = 32))
  val ocpRespReg = Reg(init = OcpResp.NULL)

  val ramDataReg = Reg(init = Bits(0, width = 32))
  val ramRespReg = Reg(init = OcpResp.NULL)

  val txEnReg = Reg(init = true.B)
  val txFrameSizeReg = Reg(init = UInt(0, width = constRamAddrWidth)) //bytes
  val fifoCountReg = Reg(init = UInt(0, width = constRamIdWidth + 1))
  val fifoEmptyReg = Reg(init = true.B)
  val fifoFullReg = Reg(init = false.B)
  val fifoPopReg = Reg(init = false.B)
  val fifoPushReg = Reg(init = false.B)
  val macRxFilterReg = Reg(init = UInt(0, width = EthernetConstants.constEthMacLength))

  val ethTxCtrlRegMap = Map(
    "txEn" -> Map("Reg" -> txEnReg, "Addr" -> Bits("h00")), //0x0
    "frameSize" -> Map("Reg" -> txFrameSizeReg, "Addr" -> Bits("h04")), //0x4
    "fifoCount" -> Map("Reg" -> fifoCountReg, "Addr" -> Bits("h08")), //0x8
    "fifoEmpty" -> Map("Reg" -> fifoEmptyReg, "Addr" -> Bits("h0C")), //0xC
    "fifoFull" -> Map("Reg" -> fifoFullReg, "Addr" -> Bits("h10")), //0x10
    "fifoPop" -> Map("Reg" -> fifoPopReg, "Addr" -> Bits("h14")), //0x14
    "fifoPush" -> Map("Reg" -> fifoPushReg, "Addr" -> Bits("h18")), //0x18
    "macFilter" -> Map("Reg" -> macRxFilterReg, "Addr" -> Bits("h1C")) //0x1C
  )

  val preambleReg = Reg(init = UInt(EthernetConstants.constSFD, width = 64))
  val fcsReg = Reg(init = UInt(0, width = 16))

  val miiByteDataReg = Reg(init = UInt(0, width = 8))
  val miiByteLoadReg = Reg(init = false.B)
  val miiIsReady = Wire(init = false.B)

  /**
    * Fifo Management
    */
  fifoEmptyReg := fifoCountReg === 0.U
  fifoFullReg := fifoCountReg === numFrameBuffers.U

  val ocpSelRam = ~ocpMasterReg.Addr(constRamAddrWidth) && ocpMasterReg.Cmd =/= OcpCmd.IDLE
  val validRamPop = fifoPopReg === true.B && !fifoEmptyReg
  val validRamPush = fifoPushReg === true.B && !fifoFullReg

  //Reset regs to default values
  fifoPushReg := false.B
  fifoPopReg := false.B
  miiByteLoadReg := false.B
  //State Machine
  switch(stateReg) {
    is(stWait) { //wait for available data in FIFORam
      when(!fifoEmptyReg) {
        stateReg := stTxPreamble
      }
    }
    is(stTxPreamble) {
      when(miiIsReady & ~miiByteLoadReg) { //wait until miiIsReady and then countup the PREAMBLE
        when(byteCntReg < 8.U) {
          miiByteLoadReg := true.B
          byteCntReg := byteCntReg + 1.U
        }.otherwise {
          byteCntReg := 0.U
          ethRamRdEn := true.B
          stateReg := stTxFrame
        }
      }
    }
    is(stTxFrame) {
      when(ramRespReg === OcpResp.DVA) {
        when(miiIsReady) {
          when(byteCntReg < txFrameSizeReg) {
            byteCntReg := byteCntReg + 1.U
          }.otherwise {
            byteCntReg := 0.U
            stateReg := stTxFrame
          }
        }
      }
    }
    is(stTxFCS) {
      when(miiIsReady) {
        when(byteCntReg < 4.U) {
          byteCntReg := byteCntReg + 1.U
        }.otherwise {
          byteCntReg := 0.U
          ethRamRdEn := false.B
          stateReg := stEoF
        }
      }
    }
    is(stEoF) {
      stateReg := stIFG
    }
    is(stIFG) {
      when(byteCntReg < 24.U) {
        byteCntReg := byteCntReg + 1.U
      }.otherwise {
        byteCntReg := 0.U
        stateReg := stWait
        fifoPopReg := true.B
      }
    }
  }

  when(validRamPush) {
    wrRamIdReg := wrRamIdReg + 1.U
    fifoCountReg := fifoCountReg + 1.U
  }

  when(validRamPop) {
    rdRamIdReg := rdRamIdReg + 1.U
    fifoCountReg := fifoCountReg - 1.U
  }

  /**
    * Multiplexing MII byte source
    */
  miiByteDataReg := RegNext(Mux(stateReg === stTxFCS, preambleReg(8.U + 8.U * byteCntReg - 1.U, 8.U * byteCntReg),
    Mux(stateReg === stTxFCS, fcsReg(8.U + 8.U * byteCntReg - 1.U, 8.U * byteCntReg), ramDataReg)))

  /**
    * Ram master-port Control
    */
  for (ramId <- 0 until numFrameBuffers) {
    ramMasterRegs(ramId).Cmd := Mux(ramId.U === wrRamIdReg && ocpSelRam, ocpMasterReg.Cmd, Mux(ethRamRdEn, OcpCmd.RD, OcpCmd.IDLE))
    ramMasterRegs(ramId).Addr := Mux(ramId.U === wrRamIdReg && ocpSelRam, ocpMasterReg.Addr(constRamAddrWidth - 1, 2), byteCntReg(constRamAddrWidth - 1, 2))
    ramMasterRegs(ramId).ByteEn := Mux(ramId.U === wrRamIdReg && ocpSelRam, ocpMasterReg.ByteEn, UIntToOH(byteCntReg(1, 0), width = 4))
    ramMasterRegs(ramId).Data := ocpMasterReg.Data
  }

  /**
    * Ram slave-port Control
    */
  ramDataReg := io.ramPorts(rdRamIdReg).S.Data
  ramRespReg := io.ramPorts(rdRamIdReg).S.Resp

  /**
    * Regs Master Control
    */
  ocpRespReg := OcpResp.NULL
  when(ocpSelRam) {
    ocpDataReg := io.ramPorts(wrRamIdReg).S.Data
    ocpRespReg := Mux(!fifoFullReg, io.ramPorts(wrRamIdReg).S.Resp, OcpResp.FAIL)
  }.otherwise {
    when(ocpMasterReg.Cmd === OcpCmd.RD) {
      ocpRespReg := OcpResp.DVA
      when(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("txEn")("Addr")) {
        ocpDataReg := ethTxCtrlRegMap("txEn")("Reg")
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("frameSize")("Addr")) {
        ocpDataReg := ethTxCtrlRegMap("frameSize")("Reg")
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoCount")("Addr")) {
        ocpDataReg := ethTxCtrlRegMap("fifoCount")("Reg")

        ocpDataReg := ethTxCtrlRegMap("fifoPop")("Reg")
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoPush")("Addr")) {
        ocpDataReg := ethTxCtrlRegMap("fifoPush")("Reg")
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("macFilter")("Addr")) {
        ocpDataReg := ethTxCtrlRegMap("macFilter")("Reg")
      }
    }.elsewhen(ocpMasterReg.Cmd === OcpCmd.WR) {
      when(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("txEn")("Addr")) {
        ethTxCtrlRegMap("txEn")("Reg") := orR(ocpMasterReg.Data)
        ocpRespReg := OcpResp.DVA
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("frameSize")("Addr")) {
        ethTxCtrlRegMap("frameSize")("Reg") := ocpDataReg
        ocpRespReg := OcpResp.DVA
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoCount")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoEmpty")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoFull")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoPop")("Addr")) {
        ocpRespReg := OcpResp.ERR
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoPush")("Addr")) {
        ethTxCtrlRegMap("fifoPush")("Reg") := orR(ocpMasterReg.Data) //set on write and reset in the next clock cycle
        ocpRespReg := OcpResp.DVA //is the responsibility of the master to check first the count ?
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("macFilter")("Addr")) {
        ethTxCtrlRegMap("macFilter")("Reg") := ocpMasterReg.Data
        ocpRespReg := OcpResp.DVA
      }
    }.otherwise {
      ocpRespReg := OcpResp.NULL
    }
  }

  /**
    * IO Plumbing
    */
  io.ocp.S.Data := ocpDataReg
  io.ocp.S.Resp := ocpRespReg
  miiIsReady := miiTx.io.ready
  miiTx.io.macDataDv := miiByteLoadReg
  miiTx.io.macData := miiByteDataReg
  miiTx.io.miiChannel <> io.miiChannel
  for (ramId <- 0 until numFrameBuffers) {
    io.ramPorts(ramId).M := ramMasterRegs(ramId)
  }
}

object EthTxController extends App {
  chiselMain(Array[String]("--backend", "v", "--targetDir", "generated/" + this.getClass.getSimpleName.dropRight(1)),
    () => Module(new EthTxController(2, 64)))
}

      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoEmpty")("Addr")) {
        ocpDataReg := ethTxCtrlRegMap("fifoEmpty")("Reg")
      }.elsewhen(ocpMasterReg.Addr(5, 0) === ethTxCtrlRegMap("fifoFull")("Addr")) {


// See README.md for license details.

package ethcontroller.design

import Chisel._
import ethcontroller.interfaces.MIIChannel
import ethcontroller.protocols.EthernetConstants
import ethcontroller.utils.{Deserializer, ExtClockSampler}

/**
  * The RX MII channel manages the reception of bytes from the PHY
  */
class MIIRx extends Module {
  val io = new Bundle() {
    val miiChannel = new MIIChannel().asInput
    val rxEn = Bool(INPUT)
    val phyErr = Bool(OUTPUT)
    val ethSof = Bool(OUTPUT)
    val ethEof = Bool(OUTPUT)
    val ethByte = Bits(OUTPUT, width = 8)
    val ethByteDv = Bool(OUTPUT)
  }

  /**
    * Registers
    */
  val sofReg = Reg(init = Bool(false))
  val eofReg = Reg(init = Bool(false))
  val validPHYDataReg = Reg(init = Bool(false))

  /**
    * Sampling clock and line of MII
    */
  val miiClkSampler = Module(new ExtClockSampler(true, 2))
  miiClkSampler.io.extClk := io.miiChannel.clk
  val miiDvSyncedReg = ShiftRegister(io.miiChannel.dv, 2)
  val miiDataSyncedReg = ShiftRegister(io.miiChannel.data, 2)
  val miiErrSyncedReg = ShiftRegister(io.miiChannel.err, 2)

  /**
    * Flags
    */
  val phyNA = io.miiChannel.col & ~io.miiChannel.crs
  val phyError = phyNA || miiErrSyncedReg
  val validPHYData = miiDvSyncedReg & ~miiErrSyncedReg & ~phyNA
  validPHYDataReg := validPHYData
  val risingMIIEdge = miiClkSampler.io.sampledClk

  /**
    * Buffering PHY bytes
    */
  val deserializePHYByte = Module(new Deserializer(false, 4, 8))
  deserializePHYByte.io.en := io.rxEn & risingMIIEdge & validPHYData
  deserializePHYByte.io.clr := phyError || eofReg
  deserializePHYByte.io.shiftIn := miiDataSyncedReg
  val byteReg = Reg(init = Bits(0, width = 8), next = deserializePHYByte.io.shiftOut)
  val wrByteReg = Reg(init = Bool(false), next = deserializePHYByte.io.done)

  /**
    * Buffer & re-order PHY double-words
    */
  val deserializePHYBuffer = Module(new Deserializer(true, 8, 64))
  deserializePHYBuffer.io.en := deserializePHYByte.io.done
  deserializePHYBuffer.io.clr := phyError || eofReg
  deserializePHYBuffer.io.shiftIn := deserializePHYByte.io.shiftOut
  val regBuffer = Reg(init = Bits(0, width = 64), next = deserializePHYBuffer.io.shiftOut)
  val regBufferDv = Reg(init = Bool(false), next = deserializePHYBuffer.io.done)

  /**
    * Ethernet frame detection logic
    */
  sofReg := false.B
  when(regBuffer === EthernetConstants.constSFD && regBufferDv) {
    sofReg := true.B
  }

  eofReg := false.B
  when(validPHYDataReg && ~validPHYData) {

  }

  /**
    * I/O plumbing
    */
  io.phyErr := phyNA
  io.ethSof := sofReg
  io.ethEof := eofReg
  io.ethByte := byteReg
  io.ethByteDv := wrByteReg
}


package ethcontroller.design

import Chisel._
import ethcontroller.interfaces.MIIChannel
import ethcontroller.utils.{ExtClockSampler, Serializer}

/**
  * The TX MII channel manages the reception of bytes from the PHY
  */
class MIITx extends Module {
  val io = new Bundle() {
    val miiChannel = new MIIChannel()
    val macDataDv = Bool(INPUT)
    val macData = Bits(INPUT, width = 8)
    val ready = Bool(OUTPUT)
    val phyErr = Bool(OUTPUT)
  }

  /**
    * Registers and components
    */
  val transmittingReg = Reg(init = false.B)

  /**
    * Sampling clock and line of MII
    */
  val miiClkSampler = Module(new ExtClockSampler(true, 2))
  miiClkSampler.io.extClk := io.miiChannel.clk
  val miiErrSyncedReg = ShiftRegister(io.miiChannel.err, 2)
  val miiDataReg = Reg(init = UInt(0, width = 4))
  val miiDvReg = Reg(init = false.B)

  /**
    * Flags
    */
  val phyNA = io.miiChannel.col & ~io.miiChannel.crs
  val phyError = phyNA || miiErrSyncedReg
  val miiClkEdge = miiClkSampler.io.sampledClk

  /**
    * Serialize Output to Nibble
    */
  val serializeByteToNibble = Module(new Serializer(false, 8, 4))
  serializeByteToNibble.io.load := io.macDataDv
  serializeByteToNibble.io.shiftIn := io.macData
  serializeByteToNibble.io.en := miiClkEdge

  /**
    * Place data on the falling MII clock edge so that they are available on the rising edge
    */
  when(miiClkEdge) {
    //(De-)Assert MII data valid
    when(transmittingReg) {
      miiDvReg := true.B
    }.elsewhen(~transmittingReg) {
      miiDvReg := false.B
    }
    //Data nibble
    miiDataReg := serializeByteToNibble.io.shiftOut
  }

  /**
    * I/O plumbing
    */
  io.ready := ~transmittingReg || ~serializeByteToNibble.io.dv
  io.miiChannel.data := miiDataReg
  io.miiChannel.dv := miiDvReg
  io.phyErr := phyNA

}

  when(~transmittingReg && serializeByteToNibble.io.dv) {
    transmittingReg := true.B
  }.elsewhen(~serializeByteToNibble.io.dv && serializeByteToNibble.io.done) {


// See README.md for license details.

package ethcontroller.interfaces

import Chisel.{Bits, Bool, Bundle, INPUT, OUTPUT}

class MIIChannel extends Bundle {
  /** Clock from the PHY */

  /** Received nibble data */
  val data = Bits(OUTPUT, width = 4)
  /** Signal could not be decoded to data */
  val err = Bool(INPUT)
  /** Carrier-sense signal */
  val crs = Bool(INPUT)
  /** Collision detection signal */
  val col = Bool(INPUT)
}

  val clk = Bool(INPUT)
  /** Received data valid */


// See README.md for license details.

package ethcontroller.interfaces

import Chisel.{Bool, Bundle, INPUT, OUTPUT}

class PHYChannel extends Bundle {
  /** Management data clock to the PHY */
  val mdc = Bool(OUTPUT)
  /** Management output data */
  val mdo = Bool(OUTPUT)

  /** Management input data */
  val mdi = Bool(INPUT)


package ethcontroller.protocols

import Chisel._

object EthernetConstants {

  // Constants
  val constEthFrameLength = 1518 * 8 //bits
  val constEthMacLength = 6 * 8 //bits
  val constFCSLength = 4 * 8 //bits
  val constSFD = Bits("h55555555555555D5", width = 64)
  val constIFG = Bits("h0000000000000000", width = 64)

  val constVLANt2 = Bits("h88A8", width = 16)
  val constVLANt3 = Bits("h9100", width = 16)
  val constMPLSt1 = Bits("h8847", width = 16)
  val constMPLSt2 = Bits("h8848", width = 16)
  val constIPv4t = Bits("h0800", width = 16)
  val constIPv6t = Bits("h86DD", width = 16)
  val constPTP2t = Bits("h88F7", width = 16)
  val constPTP4t1 = Bits("h013F", width = 16)
  val constPTP4t2 = Bits("h0140", width = 16)
  val constPTPGeneralPort = 319.U
  val constPTPEventPort = 320.U
  val constPTPSyncType = Bits("h00")
  val constPTPFollowType = Bits("h08")
  val constPTPDlyReqType = Bits("h01")
  val constPTPDlyRplyType = Bits("h09")

}

val constVLANt1 = Bits("h8100", width = 16)


// See README.md for license details.

package ethcontroller.protocols

/**
  * Mockup Ethernet frame
  * Nibbles in the frame are in LSB-first per byte order
  */
abstract class EthernetFrame {

  val PREAMBLE_END = 0xD5
  val IP_UDP_PROTOCOL = 0x11
  val PTP_FOLLOW_UP_TYPE = 0x8

  //Vars
  val preamble: Array[Byte]
  val dstMac: Array[Byte]
  val srcMac: Array[Byte]
  val ethType: Array[Byte]
  val data: Array[Byte]

  //Special cases
  val ipHeader: Array[Byte]
  val udpHeader: Array[Byte]
  val ptpHeader: Array[Byte]
  val ptpBody: Array[Byte]
  val ptpSuffix: Array[Byte]
  val fcs: Array[Byte]
  val igp: Array[Byte]

  //Getters for nibbles
  def preambleNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(preamble, msbFirst = false)

  def dstMacNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(dstMac, msbFirst = false)

  def srcMacNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(srcMac, msbFirst = false)

  def dataNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(data, msbFirst = false)

  def ipHeaderNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(ipHeader, msbFirst = false)

  def udpHeaderNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(udpHeader, msbFirst = false)

  def ptpHeaderNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(ptpHeader, msbFirst = false)

  def ptpBodyNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(ptpBody, msbFirst = false)

  def ptpSuffixNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(ptpSuffix, msbFirst = false)

  def fcsNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(fcs, msbFirst = false)

  def igpNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(igp, msbFirst = false)
}

def ethTypeNibbles: Array[Int] = EthernetUtils.dataBytesToNibbles(ethType, msbFirst = false)


// See README.md for license details.

package ethcontroller.protocols

object EthernetUtils {
  /**
    * Converts a byte to an array of nibbles
    *
    * @param byte     gets broken down to nibbles
    * @param msbFirst controls the order of the nibbles
    * @return an array of two nibbles
    */
  def byteToNibble(byte: Int, msbFirst: Boolean): Array[Int] = {
    val nibbles = new Array[Int](2)
    nibbles(0) = byte & 0x0F
    nibbles(1) = (byte & 0xF0) >> 4
    if (msbFirst) {
      nibbles.reverse
    } else {
      nibbles
    }
  }

  /**
    * Converts an array of bytes to an array of nibbles
    *
    * @param bytes    the data to be broken down to nibbles
    * @param msbFirst controls the order of the nibbles per byte
    * @return an array of nibbles size bytes.size*2
    */
  def dataBytesToNibbles(bytes: Array[Byte], msbFirst: Boolean): Array[Int] = {
    val nibbles = new Array[Int](bytes.length * 2)
    var i = 0
    for (byte <- bytes) {
      val tempByteNibbles = byteToNibble(byte, msbFirst)
      nibbles(i) = tempByteNibbles(0)
      nibbles(i + 1) = tempByteNibbles(1)
      i = i + 2
    }
    nibbles
  }

  /**
    * Lazy way for converting Integer values in array to bytes
    *
    * @param xs Integer Array values
    * @return Array of bytes
    */

def toBytes(xs: Int*) = xs.map(_.toByte).toArray


// See README.md for license details.

package ethcontroller.utils

import Chisel._

/**
  * Deserialize an M-bit input to an N-bit output based on a specified order
  *
  * @param inputWidth  the bit width of the serial input
  * @param outputWidth the bit width of the parallel
  * @param msbFirst    the serial input order
  */
class Deserializer(msbFirst: Boolean = false, inputWidth: Int = 4, outputWidth: Int = 8) extends Module {
  val io = new Bundle() {
    val en = Bool(INPUT)
    val clr = Bool(INPUT)
    val shiftIn = Bits(INPUT, width = inputWidth)
    val shiftOut = Bits(OUTPUT, width = outputWidth)
    val done = Bool(OUTPUT)
  }

  val shiftReg = Reg(init = Bits(0, width = outputWidth))

  // Shift-register
  when(io.clr) {
    shiftReg := 0.U
  }.elsewhen(io.en) {
    if (msbFirst) {
      shiftReg(inputWidth - 1, 0) := io.shiftIn
      shiftReg(outputWidth - 1, inputWidth) := shiftReg(outputWidth - inputWidth - 1, 0)

  val countReg = Reg(init = UInt(outputWidth / inputWidth - 1, width = log2Floor(outputWidth / inputWidth) + 1))
  val doneReg = Reg(init = Bool(false))

  // Shift Counter
  when(io.clr) {
    countReg := (outputWidth / inputWidth - 1).U
    doneReg := false.B
  }.elsewhen(io.en) {
    when(countReg === 0.U) {
      countReg := (outputWidth / inputWidth - 1).U
      doneReg := true.B
    }.otherwise {
      countReg := countReg - 1.U
      doneReg := false.B
    }
  }.elsewhen(doneReg) {
    doneReg := false.B
  }

  io.shiftOut := shiftReg
  io.done := doneReg
}

object Deserializer extends App {
  chiselMain(Array[String]("--backend", "v", "--targetDir", "generated/" + this.getClass.getSimpleName.dropRight(1)),
    () => Module(new Deserializer(false, 4, 8)))
}

    } else {
      shiftReg(outputWidth - inputWidth - 1, 0) := shiftReg(outputWidth - 1, inputWidth)
      shiftReg(outputWidth - 1, outputWidth - inputWidth) := io.shiftIn


// See README.md for license details.

package ethcontroller.utils

import Chisel._

/**
  * Synchronizes external clock using a flip-flop synchronizer chain and samples the
  * requested external clock edge
  *
  * @param sampleRisingEdge if true it samples the rising edge, else the falling edge
  * @param syncChainDepth   the number of FF synchronizers to use
  */
class ExtClockSampler(sampleRisingEdge: Boolean = true, syncChainDepth: Int = 2) extends Module {
  val io = new Bundle() {
    val extClk = Bool(INPUT)
    val sampledClk = Bool(OUTPUT)
  }

  val extClkEdge = Wire(init = false.B) //this will be our sampled edge

  val extClkSyncedReg = ShiftRegister(io.extClk, syncChainDepth)
  val extClkSyncedOldReg = Reg(next = extClkSyncedReg)

  if (sampleRisingEdge) {
    extClkEdge := extClkSyncedReg & ~extClkSyncedOldReg //check for a rising edge
  } else {
    extClkEdge := ~extClkSyncedReg & extClkSyncedOldReg //or check for a falling edge


// See README.md for license details.

package ethcontroller.utils

import Chisel._

/**
  * Serializes an M-bit input to an N-bit output based on a specified order
  * after an initial load
  *
  * @param inputWidth  the bit width of the parallel input
  * @param outputWidth the bit width of the serial output
  * @param msbFirst    the serial output order
  */
class Serializer(msbFirst: Boolean = false, inputWidth: Int = 8, outputWidth: Int = 4) extends Module {
  val io = new Bundle() {
    val load = Bool(INPUT)
    val en = Bool(INPUT)
    val shiftIn = Bits(INPUT, width = inputWidth)
    val shiftOut = Bits(OUTPUT, width = outputWidth)
    val dv = Bool(OUTPUT)
    val done = Bool(OUTPUT)
  }

  val constShiftStages = inputWidth / outputWidth
  val shiftReg = Reg(init = Bits(0, width = inputWidth))
  val busyReg = Reg(init = Bool(false))
  val doneReg = Reg(init = Bool(false))
  val countReg = Reg(init = UInt(constShiftStages - 1, width = log2Floor(constShiftStages) + 1))
  val selHi = UInt()
  val selLo = UInt()

  // Shift-register
  when(~busyReg && io.load) {
    shiftReg := io.shiftIn
    busyReg := true.B
    doneReg := false.B
  }.elsewhen(io.en && busyReg) {
    when(countReg === 0.U) {
      countReg := (constShiftStages - 1).U
      doneReg := true.B

    selHi := countReg * outputWidth.U + outputWidth.U - 1.U
    selLo := countReg * outputWidth.U
  } else {
    selHi := (constShiftStages.U - 1.U - countReg) * outputWidth.U + outputWidth.U - 1.U
    selLo := (constShiftStages.U - 1.U - countReg) * outputWidth.U
  }

  /**
    * I/O plumbing
    */
  io.shiftOut := shiftReg(selHi, selLo)
  io.done := doneReg
  io.dv := busyReg
}

object Serializer extends App {
  chiselMain(Array[String]("--backend", "v", "--targetDir", "generated/" + this.getClass.getSimpleName.dropRight(1)),
    () => Module(new Serializer(false, 8, 4)))
}

    }.otherwise {
      countReg := countReg - 1.U
      doneReg := false.B
    }
  }.elsewhen(doneReg) {


/*
 * Arbiter for OCP burst slaves.
 * Pseudo round robin arbitration. Each turn for a non-requesting master costs 1 clock cycle.
 *
 * Author: Martin Schoeberl (martin@jopdesign.com)
 *
 */

package ocp

import Chisel._

class Arbiter(cnt: Int, addrWidth: Int, dataWidth: Int, burstLen: Int) extends Module {
  // MS: I'm always confused from which direction the name shall be
  // probably the other way round...
  val io = IO(new Bundle {
    val master = Vec.fill(cnt) {
      new OcpBurstSlavePort(addrWidth, dataWidth, burstLen)
    }
    val slave = new OcpBurstMasterPort(addrWidth, dataWidth, burstLen)
  })

  val turnReg = Reg(init = UInt(0, log2Up(cnt)))
  val burstCntReg = Reg(init = UInt(0, log2Up(burstLen)))

  val sIdle :: sRead :: sWrite :: Nil = Enum(UInt(), 3)
  val stateReg = Reg(init = sIdle)

  // buffer signals from master to cut critical paths
  val masterBuffer = Vec(io.master.map { m =>
    val port = new OcpBurstSlavePort(addrWidth, dataWidth, burstLen)
    val bus = Module(new OcpBurstBus(addrWidth, dataWidth, burstLen))
    m <> bus.io.slave
    new OcpBurstBuffer(bus.io.master, port)
    port
  })

  val master = masterBuffer(turnReg).M

  when(stateReg === sIdle) {
    when(master.Cmd =/= OcpCmd.IDLE) {
      when(master.Cmd === OcpCmd.RD) {
        stateReg := sRead
      }
      when(master.Cmd === OcpCmd.WR) {

    }
      .otherwise {
        turnReg := Mux(turnReg === UInt(cnt - 1), UInt(0), turnReg + UInt(1))
      }
  }
  when(stateReg === sWrite) {
    // Just wait on the DVA after the write
    when(io.slave.S.Resp === OcpResp.DVA) {
      turnReg := Mux(turnReg === UInt(cnt - 1), UInt(0), turnReg + UInt(1))
      stateReg := sIdle
    }
  }
  when(stateReg === sRead) {
    // For read we have to count the DVAs
    when(io.slave.S.Resp === OcpResp.DVA) {
      burstCntReg := burstCntReg + UInt(1)
      when(burstCntReg === UInt(burstLen) - UInt(1)) {
        turnReg := Mux(turnReg === UInt(cnt - 1), UInt(0), turnReg + UInt(1))
        stateReg := sIdle
      }
    }
  }

  io.slave.M := master

  for (i <- 0 to cnt - 1) {
    masterBuffer(i).S.CmdAccept := Bits(0)
    masterBuffer(i).S.DataAccept := Bits(0)
    masterBuffer(i).S.Resp := OcpResp.NULL
    // we forward the data to all masters
    masterBuffer(i).S.Data := io.slave.S.Data
  }
  masterBuffer(turnReg).S := io.slave.S

  // The response of the SSRAM comes a little bit late
}

object ArbiterMain {
  def main(args: Array[String]): Unit = {

    val chiselArgs = args.slice(4, args.length)
    val cnt = args(0)
    val addrWidth = args(1)
    val dataWidth = args(2)
    val burstLen = args(3)

    chiselMain(chiselArgs, () => Module(new Arbiter(cnt.toInt, addrWidth.toInt, dataWidth.toInt, burstLen.toInt)))
  }
}

        stateReg := sWrite
        burstCntReg := UInt(0)


/*
 * Arbiter for OCP burst slaves.
 * TDM arbitration. Each turn for a non-requesting master costs 16+4+2 clock cycle.
 *
 * Author: Martin Schoeberl (martin@jopdesign.com) David Chong (davidchong99@gmail.com)
 *
 */

package ocp

import Chisel._

class NodeTdmArbiter(cnt: Int, addrWidth: Int, dataWidth: Int, burstLen: Int, ctrlDelay: Int) extends Module {
  // MS: I'm always confused from which direction the name shall be
  // probably the other way round...
  val io = IO(new Bundle {
    val master = new OcpBurstSlavePort(addrWidth, dataWidth, burstLen)
    val slave = new OcpBurstMasterPort(addrWidth, dataWidth, burstLen)
    val node = UInt(INPUT, 6)
  })
  debug(io.master)
  debug(io.slave)
  debug(io.node)

  // MS: have all generated constants at one place

  val cntReg = Reg(init = UInt(0, log2Up(cnt * (burstLen + ctrlDelay + 1))))
  // slot length = burst size + 1
  val burstCntReg = Reg(init = UInt(0, log2Up(burstLen)))
  val period = cnt * (burstLen + ctrlDelay + 1)
  val slotLen = burstLen + ctrlDelay + 1
  val numPipe = 3

  val wrPipeDelay = burstLen + ctrlDelay + numPipe
  val wrCntReg = Reg(init = UInt(0, log2Up(wrPipeDelay)))

  val rdPipeDelay = burstLen + ctrlDelay + numPipe
  val rdCntReg = Reg(init = UInt(0, log2Up(rdPipeDelay)))

  // MS: merge rdCntReg and wrCntReg and let it count till slot length

  val cpuSlot = Vec.fill(cnt) {
    Reg(init = UInt(0, width = 1))
  }

  val sIdle :: sRead :: sWrite :: Nil = Enum(UInt(), 3)
  val stateReg = Reg(init = sIdle)

  debug(cntReg)
  for (i <- (0 until cnt))
    debug(cpuSlot(i))

  debug(stateReg)
  debug(wrCntReg)
  debug(rdCntReg)

  cntReg := Mux(cntReg === UInt(period - 1), UInt(0), cntReg + UInt(1))

    cpuSlot(i) := slotTable(i)
  }

  // Initialize master data to zero when cpuSlot is not enabled
  io.slave.M.Addr := Bits(0)
  io.slave.M.Cmd := Bits(0)
  io.slave.M.DataByteEn := Bits(0)
  io.slave.M.DataValid := Bits(0)
  io.slave.M.Data := Bits(0)

  // Initialize slave data to zero
  io.master.S.Data := Bits(0)
  io.master.S.Resp := OcpResp.NULL
  io.master.S.CmdAccept := Bits(0)
  io.master.S.DataAccept := Bits(0)

  // FSM for TDM Arbiter
  when(stateReg === sIdle) {
    when(cpuSlot(io.node) === UInt(1)) {
      val master = io.master.M
      //io.slave.M := master

      when(master.Cmd =/= OcpCmd.IDLE) {
        when(master.Cmd === OcpCmd.RD) {
          io.slave.M := master
          stateReg := sRead
          io.master.S.CmdAccept := UInt(1)
          rdCntReg := UInt(0)
        }
        when(master.Cmd === OcpCmd.WR) {
          io.slave.M := master
          stateReg := sWrite
          io.master.S.CmdAccept := UInt(1)
          io.master.S.DataAccept := UInt(1)
          wrCntReg := UInt(0)
        }
      }
    }
  }

  when(stateReg === sWrite) {
    io.slave.M := io.master.M
    io.master.S.DataAccept := UInt(1)
    // MS: why not counting just up to the slot length
    // Then we can avoid >= and use =
    wrCntReg := Mux(wrCntReg === UInt(wrPipeDelay), UInt(0), wrCntReg + UInt(1))

    // Sends ZEROs after the burst is done
    when(wrCntReg >= UInt(burstLen - 1)) {
      io.slave.M.Cmd := Bits(0)
      io.slave.M.Addr := Bits(0)
      io.slave.M.Data := Bits(0)
      io.slave.M.DataValid := Bits(0)
      io.slave.M.DataByteEn := Bits(0)
    }

    // Turn off the DataValid after a burst of 4
    when(wrCntReg >= UInt(burstLen - 1)) {
      io.master.S.DataAccept := UInt(0)
    }

    // Forward Rsp/DVA back to node
    // Ms: not hard coded constants in the source
    when(wrCntReg === UInt(4)) {
      io.master.S.Resp := OcpResp.DVA
    }
    // Wait on DVA
    when(io.master.S.Resp === OcpResp.DVA) {
      stateReg := sIdle
    }
  }

  when(stateReg === sRead) {
    io.slave.M := io.master.M
    rdCntReg := Mux(rdCntReg === UInt(rdPipeDelay + burstLen), UInt(0), rdCntReg + UInt(1))

    // Sends ZEROs after the burst is done
    // MS: This should also (as in write) be just the slot length.
    when(rdCntReg >= UInt(burstLen - 1)) {
      io.slave.M.Cmd := Bits(0)
      io.slave.M.Addr := Bits(0)
      io.slave.M.Data := Bits(0)
      io.slave.M.DataValid := Bits(0)
      io.slave.M.DataByteEn := Bits(0)
    }

    // rdCntReg starts 1 clock cycle after the arrival of the 1st data
    // MS: rdCntReg is used for two different purposes -- fix it
    // The following shall also include number of pipeline stages on the return path
    when(rdCntReg >= UInt(ctrlDelay + numPipe)) {
      io.master.S.Data := io.slave.S.Data
      io.master.S.Resp := io.slave.S.Resp
    }

    when(io.master.S.Resp === OcpResp.DVA) {
      burstCntReg := burstCntReg + UInt(1)
      when(burstCntReg === UInt(burstLen) - UInt(1)) {
        stateReg := sIdle
      }
    }
  }

  debug(io.slave.M)

  //io.master.S := io.slave.S

}

/* Mux for all arbiters' outputs */
class MemMuxIntf(nr: Int, addrWidth: Int, dataWidth: Int, burstLen: Int) extends Module {
  val io = new Bundle {
    val master = Vec.fill(nr) {
      new OcpBurstSlavePort(addrWidth, dataWidth, burstLen)
    }
    val slave = new OcpBurstMasterPort(addrWidth, dataWidth, burstLen)
  }
  debug(io.master)
  debug(io.slave)

  // MS: would like pipeline number configurable

  // 1st stage pipeline registers for inputs
  val mCmd_p1_Reg = Vec.fill(nr) {
    Reg(init = UInt(0, width = 3))
  }
  val mAddr_p1_Reg = Vec.fill(nr) {
    Reg(UInt(width = addrWidth))
  }
  val mData_p1_Reg = Vec.fill(nr) {
    Reg(UInt(width = dataWidth))
  }
  val mDataByteEn_p1_Reg = Vec.fill(nr) {
    Reg(UInt(width = dataWidth / 8))
  }
  val mDataValid_p1_Reg = Vec.fill(nr) {
    Reg(UInt(width = 1))
  }

  // 2st stage pipeline registers for inputs
  // MS: what about using the whole bundle as a single signal?
  // val mMasterReg = Reg(init=OcpBurstMasterSignals(...))
  val mCmd_p2_Reg = Reg(init = UInt(0, width = 3))
  val mAddr_p2_Reg = Reg(UInt(width = addrWidth))
  val mData_p2_Reg = Reg(UInt(width = dataWidth))
  val mDataByteEn_p2_Reg = Reg(UInt(width = dataWidth / 8))
  val mDataValid_p2_Reg = Reg(UInt(width = 1))

  // Pipeline registers default to 0
  mCmd_p1_Reg := Bits(0)
  mAddr_p1_Reg := Bits(0)
  mData_p1_Reg := Bits(0)
  mDataByteEn_p1_Reg := Bits(0)
  mDataValid_p1_Reg := Bits(0)

  // 1st stage pipeline of the input
  for (i <- 0 until nr) {
    mCmd_p1_Reg(i) := io.master(i).M.Cmd
  }

  for (i <- 0 until nr) {
    mAddr_p1_Reg(i) := io.master(i).M.Addr
  }

  for (i <- 0 until nr) {
    mData_p1_Reg(i) := io.master(i).M.Data
  }

  for (i <- 0 until nr) {
    mDataByteEn_p1_Reg(i) := io.master(i).M.DataByteEn
  }

  for (i <- 0 until nr) {
    mDataValid_p1_Reg(i) := io.master(i).M.DataValid
  }

  // OR gate of all inputs (2nd stage pipeline)

  mCmd_p2_Reg := mCmd_p1_Reg.reduce(_ | _)
  mAddr_p2_Reg := mAddr_p1_Reg.reduce(_ | _)
  mData_p2_Reg := mData_p1_Reg.reduce(_ | _)
  mDataByteEn_p2_Reg := mDataByteEn_p1_Reg.reduce(_ | _)
  mDataValid_p2_Reg := mDataValid_p1_Reg.reduce(_ | _)

  // Transfer data from input pipeline registers to output
  io.slave.M.Addr := mAddr_p2_Reg
  io.slave.M.Cmd := mCmd_p2_Reg
  io.slave.M.DataByteEn := mDataByteEn_p2_Reg
  io.slave.M.DataValid := mDataValid_p2_Reg
  io.slave.M.Data := mData_p2_Reg

  // 1st stage pipeline registers for output
  val sResp_p1_Reg = Reg(next = io.slave.S.Resp)
  val sData_p1_Reg = Reg(next = io.slave.S.Data)

  // Forward response to all arbiters
  for (i <- 0 until nr) {
    io.master(i).S.Data := sData_p1_Reg
    io.master(i).S.Resp := sResp_p1_Reg
  }

}

object NodeTdmArbiterMain {
  def main(args: Array[String]): Unit = {

    val chiselArgs = args.slice(5, args.length)
    val cnt = args(0)
    val addrWidth = args(1)
    val dataWidth = args(2)
    val burstLen = args(3)
    val ctrlDelay = args(4)

    chiselMain(chiselArgs, () => Module(new NodeTdmArbiter(cnt.toInt, addrWidth.toInt, dataWidth.toInt, burstLen.toInt, ctrlDelay.toInt)))
  }
}

  def slotTable(i: Int): UInt = {
    (cntReg === UInt(i * slotLen)).toUInt
  }


/*
 * Generic definitions for OCP
 *
 * Authors: Wolfgang Puffitsch (wpuffitsch@gmail.com)
 *
 */

package ocp

import Chisel._

// Constants for MCmd

  val IDLE = Bits("b000")
  val WR = Bits("b001")
  val RD = Bits("b010")
  //  val RDEX = Bits("b011")
  //  val RDL  = Bits("b100")
  //  val WRNP = Bits("b101")
  //  val WRC  = Bits("b110")
  //  val BCST = Bits("b111")
}

// Constants for SResp
object OcpResp {
  val NULL = Bits("b00")
  val DVA = Bits("b01")
  val FAIL = Bits("b10")
  val ERR = Bits("b11")
}

// MS: would like fields (e.g. data) to start with lower case.
// Just classes start with upper case.

// Signals generated by master
class OcpMasterSignals(addrWidth: Int, dataWidth: Int) extends Bundle() {
  val Cmd = Bits(width = 3)
  val Addr = Bits(width = addrWidth)
  val Data = Bits(width = dataWidth)

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpMasterSignals(addrWidth, dataWidth)
    res.asInstanceOf[this.type]
  }
}

// Signals generated by slave
class OcpSlaveSignals(dataWidth: Int) extends Bundle() {
  val Resp = Bits(width = 2)
  val Data = Bits(width = dataWidth)

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpSlaveSignals(dataWidth)
    res.asInstanceOf[this.type]
  }
}


/*
 * Definitions for OCP ports that support Bursts
 *
 * Authors: Wolfgang Puffitsch (wpuffitsch@gmail.com)
 *
 */

package ocp

import Chisel._

// Burst masters provide handshake signals
class OcpBurstMasterSignals(addrWidth: Int, dataWidth: Int)
  extends OcpMasterSignals(addrWidth, dataWidth) {
  val DataValid = Bits(width = 1)
  val DataByteEn = Bits(width = dataWidth / 8)

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpBurstMasterSignals(addrWidth, dataWidth)
    res.asInstanceOf[this.type]
  }
}

// Burst slaves provide handshake signal
class OcpBurstSlaveSignals(dataWidth: Int)
  extends OcpSlaveSignals(dataWidth) {
  val CmdAccept = Bits(width = 1)
  val DataAccept = Bits(width = 1)

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpBurstSlaveSignals(dataWidth)
    res.asInstanceOf[this.type]
  }
}

// Master port
class OcpBurstMasterPort(addrWidth: Int, dataWidth: Int, burstLen: Int) extends Bundle() {
  val burstLength = burstLen
  // Clk is implicit in Chisel
  val M = new OcpBurstMasterSignals(addrWidth, dataWidth).asOutput
  val S = new OcpBurstSlaveSignals(dataWidth).asInput
}

// Slave port is reverse of master port
class OcpBurstSlavePort(addrWidth: Int, dataWidth: Int, burstLen: Int) extends Bundle() {
  val burstLength = burstLen
  // Clk is implicit in Chisel
  val M = new OcpBurstMasterSignals(addrWidth, dataWidth).asInput
  val S = new OcpBurstSlaveSignals(dataWidth).asOutput

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpBurstSlavePort(addrWidth, dataWidth, burstLen)
    res.asInstanceOf[this.type]
  }

}

// Bridge between word-oriented port and burst port
class OcpBurstBridge(master: OcpCacheMasterPort, slave: OcpBurstSlavePort) {
  val addrWidth = master.M.Addr.getWidth
  val dataWidth = master.M.Data.getWidth
  val burstLength = slave.burstLength
  val burstAddrBits = log2Up(burstLength)

  // State of transmission
  val idle :: read :: readResp :: write :: Nil = Enum(Bits(), 4)
  val state = Reg(init = idle)
  val burstCnt = Reg(init = UInt(0, burstAddrBits))
  val cmdPos = Reg(Bits(width = burstAddrBits))

  // Register signals that come from master
  val masterReg = Reg(init = master.M)

  // Register to delay response
  val slaveReg = Reg(master.S)

  when(state =/= write && (masterReg.Cmd === OcpCmd.IDLE || slave.S.CmdAccept === Bits(1))) {
    masterReg := master.M
  }

  // Default values
  slave.M.Cmd := masterReg.Cmd
  slave.M.Addr := Cat(masterReg.Addr(addrWidth - 1, burstAddrBits + log2Up(dataWidth / 8)),
    Fill(burstAddrBits + log2Up(dataWidth / 8), Bits(0)))
  slave.M.Data := Bits(0)
  slave.M.DataByteEn := Bits(0)
  slave.M.DataValid := Bits(0)
  master.S := slave.S

  // Read burst
  when(state === read) {
    when(slave.S.Resp =/= OcpResp.NULL) {
      when(burstCnt === cmdPos) {
        slaveReg := slave.S
      }
      when(burstCnt === UInt(burstLength - 1)) {
        state := readResp
      }
      burstCnt := burstCnt + UInt(1)
    }
    master.S.Resp := OcpResp.NULL
    master.S.Data := Bits(0)
  }
  when(state === readResp) {
    state := idle
    master.S := slaveReg
  }

  // Write burst
  when(state === write) {
    masterReg.Cmd := OcpCmd.IDLE
    slave.M.DataValid := Bits(1)
    when(burstCnt === cmdPos) {
      slave.M.Data := masterReg.Data
      slave.M.DataByteEn := masterReg.ByteEn
    }
    when(burstCnt === UInt(burstLength - 1)) {
      state := idle
    }
    when(slave.S.DataAccept === Bits(1)) {
      burstCnt := burstCnt + UInt(1)
    }
  }

  // Start new transaction
  when(master.M.Cmd === OcpCmd.RD) {
    state := read
    cmdPos := master.M.Addr(burstAddrBits + log2Up(dataWidth / 8) - 1, log2Up(dataWidth / 8))
  }
  when(master.M.Cmd === OcpCmd.WR) {
    state := write
    cmdPos := master.M.Addr(burstAddrBits + log2Up(dataWidth / 8) - 1, log2Up(dataWidth / 8))
  }
}

// Join two OcpBurst ports, assume no collisions between requests
class OcpBurstJoin(left: OcpBurstMasterPort, right: OcpBurstMasterPort,
                   joined: OcpBurstSlavePort, selectLeft: Bool = Bool()) {

  val selRightReg = Reg(Bool())
  val selRight = Mux(left.M.Cmd =/= OcpCmd.IDLE, Bool(false),
    Mux(right.M.Cmd =/= OcpCmd.IDLE, Bool(true),
      selRightReg))

  joined.M := left.M
  when(selRight) {
    joined.M := right.M
  }
  joined.M.Cmd := right.M.Cmd | left.M.Cmd

  right.S := joined.S
  left.S := joined.S

  when(selRightReg) {
    left.S.Resp := OcpResp.NULL
  }
    .otherwise {
      right.S.Resp := OcpResp.NULL
    }

  selRightReg := selRight

  selectLeft := !selRight
}

// Join two OcpBurst ports, left port has priority in case of colliding requests
class OcpBurstPriorityJoin(left: OcpBurstMasterPort, right: OcpBurstMasterPort,
                           joined: OcpBurstSlavePort, selectLeft: Bool = Bool()) {

  val selLeft = left.M.Cmd =/= OcpCmd.IDLE
  val selRight = right.M.Cmd =/= OcpCmd.IDLE

  val leftPendingReg = Reg(init = Bool(false))
  val rightPendingReg = Reg(init = Bool(false))

  val pendingRespReg = Reg(init = UInt(0))

  // default port forwarding
  joined.M := Mux(rightPendingReg, right.M, left.M)

  // pass back data to masters
  right.S := joined.S
  left.S := joined.S
  // suppress responses when not serving a request
  when(!rightPendingReg) {
    right.S.Resp := OcpResp.NULL
  }
  when(!leftPendingReg) {
    left.S.Resp := OcpResp.NULL
  }

  // do not accept commands while another request is being served
  left.S.CmdAccept := Mux(rightPendingReg, Bits(0), joined.S.CmdAccept)
  left.S.DataAccept := Mux(rightPendingReg, Bits(0), joined.S.DataAccept)
  right.S.CmdAccept := Mux(leftPendingReg, Bits(0), joined.S.CmdAccept)
  right.S.DataAccept := Mux(leftPendingReg, Bits(0), joined.S.DataAccept)

  // forward requests from left port
  when(selLeft) {
    when(!rightPendingReg) {
      joined.M := left.M
      pendingRespReg := Mux(left.M.Cmd === OcpCmd.WR, UInt(1), UInt(left.burstLength))
      leftPendingReg := Bool(true)
      right.S.CmdAccept := Bits(0)
      right.S.DataAccept := Bits(0)
    }
  }
  // forward requests from right port
  when(selRight) {
    when(!selLeft && !leftPendingReg) {
      joined.M := right.M
      pendingRespReg := Mux(right.M.Cmd === OcpCmd.WR, UInt(1), UInt(right.burstLength))
      rightPendingReg := Bool(true)
    }
  }

  // count responses, clear pending flags at end of requests
  when(joined.S.Resp =/= OcpResp.NULL) {
    pendingRespReg := pendingRespReg - UInt(1)
    when(pendingRespReg === UInt(1)) {
      when(leftPendingReg) {
        leftPendingReg := Bool(false)
      }
      when(rightPendingReg) {
        rightPendingReg := Bool(false)
      }
    }
  }

  selectLeft := selLeft
}

// Provide a "bus" with a master port and a slave port to simplify plumbing
class OcpBurstBus(addrWidth: Int, dataWidth: Int, burstLen: Int) extends Module {
  val io = new Bundle {
    val master = new OcpBurstMasterPort(addrWidth, dataWidth, burstLen)
    val slave = new OcpBurstSlavePort(addrWidth, dataWidth, burstLen)
  }
  io.master <> io.slave
}

// Buffer a burst for pipelining
class OcpBurstBuffer(master: OcpBurstMasterPort, slave: OcpBurstSlavePort) {

  val MBuffer = Vec.fill(master.burstLength) {
    Reg(init = master.M)
  }

  val free = MBuffer(0).Cmd === OcpCmd.IDLE
  when(free || slave.S.CmdAccept === Bits(1)) {

  master.S.CmdAccept := free
  master.S.DataAccept := free
}

    for (i <- 0 until master.burstLength - 1) {
      MBuffer(i) := MBuffer(i + 1)
    }
    MBuffer(master.burstLength - 1) := master.M
  }


/*
 * Definitions for OCP ports for split cache
 *
 * Authors: Wolfgang Puffitsch (wpuffitsch@gmail.com)
 *
 */

package ocp

import Chisel._

object OcpCache {
  val STACK_CACHE = Bits("b00")
  val DATA_CACHE = Bits("b10")
  val UNCACHED = Bits("b11")
}

// Cache masters provide address space signal
class OcpCacheMasterSignals(addrWidth: Int, dataWidth: Int)
  extends OcpCoreMasterSignals(addrWidth, dataWidth) {
  val AddrSpace = Bits(width = 2)

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpCacheMasterSignals(addrWidth, dataWidth)
    res.asInstanceOf[this.type]
  }
}

// Master port
class OcpCacheMasterPort(addrWidth: Int, dataWidth: Int) extends Bundle() {
  // Clk is implicit in Chisel
  val M = new OcpCacheMasterSignals(addrWidth, dataWidth).asOutput
  val S = new OcpSlaveSignals(dataWidth).asInput
}

// Slave port is reverse of master port

    val slave = new OcpCacheSlavePort(addrWidth, dataWidth)
    val master = new OcpCacheMasterPort(addrWidth, dataWidth)
  }
  io.master <> io.slave
}

class OcpCacheSlavePort(addrWidth: Int, dataWidth: Int) extends Bundle() {
  // Clk is implicit in Chisel
  val M = new OcpCacheMasterSignals(addrWidth, dataWidth).asInput
  val S = new OcpSlaveSignals(dataWidth).asOutput
}


/*
 * Definitions for OCP as generated by the Patmos pipeline
 *
 * Authors: Wolfgang Puffitsch (wpuffitsch@gmail.com)
 *
 */

package ocp

import Chisel._

// Masters include a byte-enable signal
class OcpCoreMasterSignals(addrWidth: Int, dataWidth: Int)
  extends OcpMasterSignals(addrWidth, dataWidth) {
  val ByteEn = Bits(width = dataWidth / 8)

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpCoreMasterSignals(addrWidth, dataWidth)
    res.asInstanceOf[this.type]
  }
}

// Master port
class OcpCoreMasterPort(addrWidth: Int, dataWidth: Int) extends Bundle() {
  val M = new OcpCoreMasterSignals(addrWidth, dataWidth).asOutput
  val S = new OcpSlaveSignals(dataWidth).asInput

class OcpCoreSlavePort(addrWidth: Int, dataWidth: Int) extends Bundle() {
  val M = new OcpCoreMasterSignals(addrWidth, dataWidth).asInput
  val S = new OcpSlaveSignals(dataWidth).asOutput

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpCoreSlavePort(addrWidth, dataWidth)
    res.asInstanceOf[this.type]
  }

}

// Provide a "bus" with a master port and a slave port to simplify plumbing
class OcpCoreBus(addrWidth: Int, dataWidth: Int) extends Module {
  val io = new Bundle {
    val slave = new OcpCoreSlavePort(addrWidth, dataWidth)
    val master = new OcpCoreMasterPort(addrWidth, dataWidth)
  }
  io.master <> io.slave
}

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpCoreMasterPort(addrWidth, dataWidth)
    res.asInstanceOf[this.type]


/*
 * Definitions for Patmos' OCP ports for general I/O
 *
 * Authors: Wolfgang Puffitsch (wpuffitsch@gmail.com)
 *
 */

package ocp

import Chisel._

// Masters include a RespAccept signal
class OcpIOMasterSignals(addrWidth: Int, dataWidth: Int)
  extends OcpCoreMasterSignals(addrWidth, dataWidth) {
  val RespAccept = Bits(width = 1)

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpIOMasterSignals(addrWidth, dataWidth)
    res.asInstanceOf[this.type]
  }
}

// Slaves include a CmdAccept signal
class OcpIOSlaveSignals(dataWidth: Int)
  extends OcpSlaveSignals(dataWidth) {
  val CmdAccept = Bits(width = 1)

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpIOSlaveSignals(dataWidth)
    res.asInstanceOf[this.type]
  }
}

// Master port
class OcpIOMasterPort(addrWidth: Int, dataWidth: Int) extends Bundle() {
  // Clk is implicit in Chisel
  val M = new OcpIOMasterSignals(addrWidth, dataWidth).asOutput
  val S = new OcpIOSlaveSignals(dataWidth).asInput
}

// Slave port is reverse of master port
class OcpIOSlavePort(addrWidth: Int, dataWidth: Int) extends Bundle() {
  // Clk is implicit in Chisel
  val M = new OcpIOMasterSignals(addrWidth, dataWidth).asInput
  val S = new OcpIOSlaveSignals(dataWidth).asOutput
}

// Bridge between ports that do/do not support CmdAccept
class OcpIOBridge(master: OcpCoreMasterPort, slave: OcpIOSlavePort) {
  // Register signals that come from master
  val masterReg = Reg(init = master.M)

  slave.M.RespAccept := Bits("b1")

  // Forward slave signals to master
  master.S <> slave.S
}

// Bridge between ports that do/do not support CmdAccept
// An alternative version, being on the safer side for reset.
// Inserts one cycle delay for the command register (could be improved)
//   including the earlier reaction on CmdAccep
//   adds than combinational paths
class OcpIOBridgeAlt(master: OcpCoreMasterPort, slave: OcpIOSlavePort) {

  val masterReg = Reg(init = master.M) // What is the reset value of this bundle?
  val busyReg = Reg(init = Bool(false))

  when(!busyReg) {
    masterReg := master.M
  }
  when(master.M.Cmd === OcpCmd.RD || master.M.Cmd === OcpCmd.WR) {
    busyReg := Bool(true)
  }
  when(busyReg && slave.S.CmdAccept === Bits(1)) {
    busyReg := Bool(false)
    masterReg.Cmd := OcpCmd.IDLE
  }

  // Forward master signals to slave, always accept responses
  slave.M := masterReg
  slave.M.RespAccept := Bits("b1")

  // Forward slave signals to master
  master.S <> slave.S
}

// Provide a "bus" with a master port and a slave port to simplify plumbing
class OcpIOBus(addrWidth: Int, dataWidth: Int) extends Module {
  val io = new Bundle {
    val slave = new OcpIOSlavePort(addrWidth, dataWidth)
    val master = new OcpIOMasterPort(addrWidth, dataWidth)
  }
  io.master <> io.slave
}

  when(masterReg.Cmd === OcpCmd.IDLE || slave.S.CmdAccept === Bits(1)) {
    masterReg := master.M
  }


/*
 * Definitions for OCP port for the network interface
 *
 * Authors: Wolfgang Puffitsch (wpuffitsch@gmail.com)
 *
 */

package ocp

import Chisel._

// Cache masters provide address space signal
class OcpNISlaveSignals(dataWidth: Int)
  extends OcpIOSlaveSignals(dataWidth) {
  val Reset_n = Bits(width = 1)
  val Flag = Bits(width = 2)

// Master port
class OcpNIMasterPort(addrWidth: Int, dataWidth: Int) extends Bundle() {
  // Clk is implicit in Chisel
  val M = new OcpIOMasterSignals(addrWidth, dataWidth).asOutput
  val S = new OcpNISlaveSignals(dataWidth).asInput
}

// Slave port is reverse of master port
class OcpNISlavePort(addrWidth: Int, dataWidth: Int) extends Bundle() {
  // Clk is implicit in Chisel
  val M = new OcpIOMasterSignals(addrWidth, dataWidth).asInput
  val S = new OcpNISlaveSignals(dataWidth).asOutput
}

  // This does not really clone, but Data.clone doesn't either
  override def clone() = {
    val res = new OcpNISlaveSignals(dataWidth)


/*
 * Test for usability of OCP definitions
 *
 * Authors: Wolfgang Puffitsch (wpuffitsch@gmail.com)
 *
 */

package ocp

import Chisel._

class OcpMaster() extends Module {
  val io = IO(new OcpCacheMasterPort(8, 32))

  val cnt = Reg(UInt(), init = UInt(0))
  cnt := cnt + UInt(1, 32)

  io.M.Cmd := OcpCmd.IDLE
  io.M.Addr := cnt(15, 8)
  io.M.Data := cnt
  io.M.ByteEn := cnt(7, 4)

  when(cnt(3, 0) === Bits("b1111")) {
    io.M.Cmd := OcpCmd.WR
  }
}

class OcpSlave() extends Module {
  val io = IO(new OcpBurstSlavePort(8, 32, 4))

  val M = Reg(next = io.M)

  val data = Reg(UInt(), init = UInt(0))
  data := data + UInt(1, 32)

  val cnt = Reg(UInt(), init = UInt(0))

  io.S.Resp := OcpResp.NULL
  io.S.Data := data
  when(M.Cmd =/= OcpCmd.IDLE) {
    cnt := UInt(4)
  }

  val io = IO(new OcpBurstSlavePort(8, 32, 4))

  val master = Module(new OcpMaster())
  val slave = Module(new OcpSlave())
  val bridge = new OcpBurstBridge(master.io, slave.io)

  io <> slave.io
}

object OcpTestMain {
  def main(args: Array[String]): Unit = {
    chiselMain(args, () => Module(new Ocp()))
  }
}

  when(cnt =/= UInt(0)) {
    cnt := cnt - UInt(1)
    io.S.Resp := OcpResp.DVA
  }


/*
 * Arbiter for OCP burst slaves.
 * Pseudo round robin arbitration. Each turn for a non-requesting master costs 1 clock cycle.
 *
 * Author: Martin Schoeberl (martin@jopdesign.com) David Chong (davidchong99@gmail.com)
 *
 */

package ocp

import Chisel._

class TdmArbiter(cnt: Int, addrWidth: Int, dataWidth: Int, burstLen: Int) extends Module {
  // MS: I'm always confused from which direction the name shall be
  // probably the other way round...
  val io = IO(new Bundle {
    val master = Vec.fill(cnt) {
      new OcpBurstSlavePort(addrWidth, dataWidth, burstLen)
    }
    val slave = new OcpBurstMasterPort(addrWidth, dataWidth, burstLen)
  })
  debug(io.master)
  debug(io.slave)

  val cntReg = Reg(init = UInt(0, log2Up(cnt * (burstLen + 2))))
  // slot length = burst size + 2
  val burstCntReg = Reg(init = UInt(0, log2Up(burstLen)))
  val period = cnt * (burstLen + 2)
  val slotLen = burstLen + 2
  val cpuSlot = Vec.fill(cnt) {
    Reg(init = UInt(0, width = 1))
  }

  val sIdle :: sRead :: sWrite :: Nil = Enum(UInt(), 3)
  val stateReg = Vec.fill(cnt) {
    Reg(init = sIdle)
  }

  debug(cntReg)
  debug(cpuSlot(0))
  debug(cpuSlot(1))
  debug(cpuSlot(2))
  debug(stateReg(0))
  debug(stateReg(1))
  debug(stateReg(2))

  cntReg := Mux(cntReg === UInt(period - 1), UInt(0), cntReg + UInt(1))

  // Generater the slot Table for the whole period
  def slotTable(i: Int): UInt = {
    (cntReg === UInt(i * slotLen)).toUInt
  }

  for (i <- 0 until cnt) {
    cpuSlot(i) := slotTable(i)
  }

  // Initialize data to zero when cpuSlot is not enabled
  io.slave.M.Addr := Bits(0)
  io.slave.M.Cmd := Bits(0)
  io.slave.M.DataByteEn := Bits(0)
  io.slave.M.DataValid := Bits(0)
  io.slave.M.Data := Bits(0)

  // Initialize slave data to zero
  for (i <- 0 to cnt - 1) {
    io.master(i).S.CmdAccept := Bits(0)
    io.master(i).S.DataAccept := Bits(0)
    io.master(i).S.Resp := OcpResp.NULL
    io.master(i).S.Data := Bits(0)
  }

  // Temporarily assigned to master 0
  //val masterIdReg = Reg(init = UInt(0, log2Up(cnt)))

  for (i <- 0 to cnt - 1) {

    when(stateReg(i) === sIdle) {
      when(cpuSlot(i) === UInt(1)) {

        when(io.master(i).M.Cmd =/= OcpCmd.IDLE) {
          when(io.master(i).M.Cmd === OcpCmd.RD) {
            io.slave.M := io.master(i).M
            io.master(i).S := io.slave.S
            stateReg(i) := sRead
          }
          when(io.master(i).M.Cmd === OcpCmd.WR) {
            io.slave.M := io.master(i).M
            io.master(i).S := io.slave.S
            stateReg(i) := sWrite
            burstCntReg := UInt(0)
          }
        }
      }
    }

    when(stateReg(i) === sWrite) {
      io.slave.M := io.master(i).M
      io.master(i).S := io.slave.S
      // Wait on DVA

    when(stateReg(i) === sRead) {
      io.slave.M := io.master(i).M
      io.master(i).S := io.slave.S
      when(io.slave.S.Resp === OcpResp.DVA) {
        burstCntReg := burstCntReg + UInt(1)
        when(burstCntReg === UInt(burstLen) - UInt(1)) {
          stateReg := sIdle
        }
      }
    }
  }

  //io.slave.M := io.master(masterIdReg).M
  debug(io.slave.M)

}

object TdmArbiterMain {
  def main(args: Array[String]): Unit = {

    val chiselArgs = args.slice(4, args.length)
    val cnt = args(0)
    val addrWidth = args(1)
    val dataWidth = args(2)
    val burstLen = args(3)

    chiselMain(chiselArgs, () => Module(new TdmArbiter(cnt.toInt, addrWidth.toInt, dataWidth.toInt, burstLen.toInt)))
  }
}

      when(io.slave.S.Resp === OcpResp.DVA) {
        stateReg(i) := sIdle
      }


/*
 * Wrapper for NodeTdmArbiter
 *
 * Author: Martin Schoeberl (martin@jopdesign.com) David Chong (davidchong99@gmail.com)
 *
 */

/*
 * Switching between different arbiters in Aegean:
 * Change 'Arbiter' in aegeanGen.py and in aegeanCode.py to 'TdmArbiterWrapper'.
 * Three places all together.
 */

package ocp

import Chisel._

class TdmArbiterWrapper(cnt: Int, addrWidth: Int, dataWidth: Int, burstLen: Int) extends Module {
  // MS: I'm always confused from which direction the name shall be
  // probably the other way round...
  val io = IO(new Bundle {

  val memMux = Module(new MemMuxIntf(cnt, addrWidth, dataWidth, burstLen))

  for (i <- 0 until cnt) {
    val nodeID = UInt(i, width = 6)
    val arb = Module(new ocp.NodeTdmArbiter(cnt, addrWidth, dataWidth, burstLen, 16))
    arb.io.master <> io.master(i)
    arb.io.node := nodeID

    memMux.io.master(i) <> arb.io.slave
  }

  io.slave <> memMux.io.slave
}

object TdmArbiterWrapperMain {
  def main(args: Array[String]): Unit = {

    val chiselArgs = args.slice(4, args.length)
    val cnt = args(0)
    val addrWidth = args(1)
    val dataWidth = args(2)
    val burstLen = args(3)

    chiselMain(chiselArgs, () => Module(new TdmArbiterWrapper(cnt.toInt, addrWidth.toInt, dataWidth.toInt, burstLen.toInt)))
  }
}

    val master = Vec.fill(cnt) {
      new OcpBurstSlavePort(addrWidth, dataWidth, burstLen)
    }


// See README.md for license details.

package ethcontroller.design

import Chisel._
import ethcontroller.protocols.{EthernetFrame, EthernetTesting, EthernetUtils}

import scala.language.postfixOps
import scala.sys.process._

class EthRxControllerTester(dut: EthRxController, frame: EthernetFrame, injectNoFrames: Int) extends Tester(dut) {

  def initPHY2MII(clockDiv: Int): Unit = {
    poke(dut.io.miiChannel.col, 0)
    poke(dut.io.miiChannel.crs, 0)
    poke(dut.io.miiChannel.clk, 0)
    step(clockDiv)
  }

  def txPHY2MIIData(nibble: Int, clockDiv: Int): Unit = {
    poke(dut.io.miiChannel.dv, 1)
    poke(dut.io.miiChannel.data, nibble)
    poke(dut.io.miiChannel.clk, 0)
    step(clockDiv)
    poke(dut.io.miiChannel.clk, 1)
    step(clockDiv)
  }

  def stopTxPHY2MII(clockDiv: Int): Unit = {
    poke(dut.io.miiChannel.dv, 0)
    poke(dut.io.miiChannel.clk, 0)
    step(clockDiv)
    poke(dut.io.miiChannel.clk, 1)
    step(clockDiv)
  }

  def peekEthRxStatus(): Unit = {
    peek(dut.stateReg)
    peek(dut.ethByteReg)
    peek(dut.ethByteDvReg)
    peek(dut.rdRamIdReg)
    peek(dut.wrRamIdReg)
    peek(dut.fifoCountReg)
    peek(dut.fifoEmptyReg)
    peek(dut.fifoFullReg)
    peek(dut.rxFrameSizeReg)
  }

  def testSingleFrameReception(frame: EthernetFrame, initTime: Long): Unit = {
    initPHY2MII(4)
    println("Testing preamble")
    for (nibble <- frame.preambleNibbles) {
      txPHY2MIIData(nibble, 6)
      peek(dut.miiRx.deserializePHYByte.doneReg)
      peek(dut.miiRx.deserializePHYByte.shiftReg)
      peek(dut.miiRx.regBuffer)
    }
    println("Testing MAC")

        txPHY2MIIData(nibble, 6)
        peekEthRxStatus()
      }
    }
    println("Testing EoF")
    stopTxPHY2MII(6)
    peekEthRxStatus()
  }

  /**
    * Testing EthernetRxController
    */
  println("Test Starting...")
  var time: Long = 0x53C38A1000000000L
  for (i <- 0 until injectNoFrames) {
    println("...")
    println("TEST FRAME ITERATION #" + i + " at t = " + time.toHexString)
    testSingleFrameReception(frame, time)
    println("END TEST FRAME ITERATION #" + i + " at t = " + time.toHexString)
    step(1)
    step(1)
    println("...")
    println("...")
  }
}

object EthRxControllerTester extends App {
  private val pathToVCD = "generated/" + this.getClass.getSimpleName.dropRight(1)
  private val nameOfVCD = this.getClass.getSimpleName.dropRight(7) + ".vcd"

  //Chisel3
  //  iotesters.Driver.execute(Array("--target-dir", "generated", "--fint-write-vcd"), () => new EthRxController(4, 64)) {
  //    // iotesters.Driver.execute(Array("--target-dir", "generated", "--fint-write-vcd", "--wave-form-file-name", "generated/BubbleFifo.vcd"), () => new BubbleFifo(8, 4)) {
  //    c => new EthRxControllerTester(c, EthernetTesting.mockupPTPEthFrameOverIpUDP, 3)
  //  }

  //Chisel 2
  chiselMainTest(Array("--genHarness", "--test", "--backend", "c",
    "--compile", "--vcd", "--targetDir", pathToVCD),
    () => Module(new EthRxController(4, 64))) {
    dut => new EthRxControllerTester(dut, EthernetTesting.mockupPTPEthFrameOverIpUDP, 5)
  }

  "gtkwave " + pathToVCD + "/" + nameOfVCD + " " + pathToVCD + "/" + "view.sav" !
}

    for (nibble <- frame.dstMacNibbles ++ frame.srcMacNibbles ++ frame.ethTypeNibbles) {
      txPHY2MIIData(nibble, 6)
      peekEthRxStatus()
    }