From fb9912ad41b4ba3b6919cfcc9a0f95fa84f3cd04 Mon Sep 17 00:00:00 2001 From: Hasan Genc Date: Mon, 19 Nov 2018 16:17:03 -0800 Subject: [PATCH] * Create a new Pokeable typeclass that describes elements which can be poked, peeked, or expected from * Use the new Pokeable typeclass to extend PeekPokeTester and AdvTester to support enums * Fix AdvTesterSpec. Previously, AdvTesterSpec would pass even on incorrect results, because the "assert" function in AdvTester will only check an assertion if an error message is provided --- .../scala/chisel3/iotesters/AdvTester.scala | 26 +-- .../chisel3/iotesters/ChiselPokeSpec.scala | 12 +- .../chisel3/iotesters/FirrtlTerpBackend.scala | 6 +- .../chisel3/iotesters/PeekPokeTester.scala | 90 +++++---- .../scala/chisel3/iotesters/Pokeable.scala | 22 +++ .../chisel3/iotesters/TreadleBackend.scala | 8 +- .../scala/chisel3/iotesters/EnumSpec.scala | 179 ++++++++++++++++++ src/test/scala/examples/AdvTesterSpec.scala | 4 +- .../scala/examples/PeekPokeBundleSpec.scala | 15 +- 9 files changed, 297 insertions(+), 65 deletions(-) create mode 100644 src/main/scala/chisel3/iotesters/Pokeable.scala create mode 100644 src/test/scala/chisel3/iotesters/EnumSpec.scala diff --git a/src/main/scala/chisel3/iotesters/AdvTester.scala b/src/main/scala/chisel3/iotesters/AdvTester.scala index bb675455..ea85b129 100644 --- a/src/main/scala/chisel3/iotesters/AdvTester.scala +++ b/src/main/scala/chisel3/iotesters/AdvTester.scala @@ -11,8 +11,8 @@ import java.io.{PrintWriter, StringWriter} // Provides a template to define advanced tester transactions trait AdvTests extends PeekPokeTests { def cycles: Long - def wire_poke(port: Bits, target: BigInt): Unit - def reg_poke(port: Bits, target: BigInt): Unit + def wire_poke[T <: Element: Pokeable](port: T, target: BigInt): Unit + def reg_poke[T <: Element: Pokeable](port: T, target: BigInt): Unit def takestep(work: => Unit = {}): Unit def takesteps(n: Int)(work: =>Unit = {}): Unit def until(pred: =>Boolean, maxCycles: Long = 0L)(work: =>Unit): Boolean @@ -42,19 +42,21 @@ abstract class AdvTester[+T <: Module](dut: T, // This section of code lets testers easily emulate have registers right before dut inputs // This testing style conforms with the general ASPIRE testbench style // Also, to ensure difference enforced, poke 'deprecated' and replaced with wire_poke - def wire_poke(port: Bits, target: BigInt) = super.poke(port, target) + def wire_poke[T <: Element: Pokeable](port: T, target: BigInt) = super.poke(port, target) - override def poke(port: Bits, target: BigInt) { + override def poke[T <: Element: Pokeable](port: T, target: BigInt) { require(false, "poke hidden for AdvTester, use wire_poke or reg_poke") } - private val registered_bits_updates = new scala.collection.mutable.HashMap[Bits,BigInt]() + private val registered_bits_updates = new scala.collection.mutable.HashMap[Element,BigInt]() private def do_registered_updates() = { - registered_bits_updates.foreach( kv => wire_poke(kv._1,kv._2) ) + registered_bits_updates.foreach{case (key, value) => key match { + case Pokeable(p) => wire_poke(p, value) + }} registered_bits_updates.clear } - def reg_poke(port: Bits, target: BigInt) { registered_bits_updates(port) = target } + def reg_poke[T <: Element: Pokeable](port: T, target: BigInt) { registered_bits_updates(port) = target } // This function replaces step in the advanced tester and makes sure all tester features are clocked in the appropriate order def takestep(work: => Unit = {}): Unit = { @@ -134,7 +136,7 @@ abstract class AdvTester[+T <: Module](dut: T, } object IrrevocableSink { - def apply[T<:Bits](socket: ReadyValidIO[T]) = + def apply[T<:Element: Pokeable](socket: ReadyValidIO[T]) = new IrrevocableSink(socket, (socket_bits: T) => peek(socket_bits)) } @@ -145,7 +147,7 @@ abstract class AdvTester[+T <: Module](dut: T, } object DecoupledSink { - def apply[T<:Bits](socket: ReadyValidIO[T]) = + def apply[T<:Element: Pokeable](socket: ReadyValidIO[T]) = new DecoupledSink(socket, (socket_bits: T) => peek(socket_bits)) } @@ -164,7 +166,7 @@ abstract class AdvTester[+T <: Module](dut: T, preprocessors += this } object ValidSink { - def apply[T<:Bits](socket: ValidIO[T]) = + def apply[T<:Element: Pokeable](socket: ValidIO[T]) = new ValidSink(socket, (socket_bits: T) => peek(socket_bits)) } @@ -195,7 +197,7 @@ abstract class AdvTester[+T <: Module](dut: T, postprocessors += this } object DecoupledSource { - def apply[T<:Bits](socket: DecoupledIO[T]) = + def apply[T<:Element: Pokeable](socket: DecoupledIO[T]) = new DecoupledSource(socket, (socket_bits: T, in: BigInt) => reg_poke(socket_bits, in)) } @@ -223,7 +225,7 @@ abstract class AdvTester[+T <: Module](dut: T, postprocessors += this } object ValidSource { - def apply[T<:Bits](socket: ValidIO[T]) = + def apply[T<:Element: Pokeable](socket: ValidIO[T]) = new ValidSource(socket, (socket_bits: T, in: BigInt) => reg_poke(socket_bits, in)) } } diff --git a/src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala b/src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala index baeb72d9..2c11361b 100644 --- a/src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala +++ b/src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala @@ -44,7 +44,7 @@ trait ChiselPokeTesterUtils extends Assertions { // TODO: statically-typed Bundle constructors // Map-based Bundle expect/pokes currently not supported because those don't compile-time check - def expect(ref: Bits, value: BigInt, msg: String="") { + def expect[T <: Element: Pokeable](ref: T, value: BigInt, msg: String="") { val actualValue = backend.peek(ref, None) val postfix = if (msg != "") s": $msg" else "" assert(actualValue == value, s"(cycle $currCycle: expected ${ref.instanceName} == $value, got $actualValue$postfix)") @@ -52,7 +52,7 @@ trait ChiselPokeTesterUtils extends Assertions { /** Write a value into the circuit. */ - def poke(ref: Bits, value: BigInt) { + def poke[T <: Element](ref: T, value: BigInt) { assert(!ref.isLit, s"(attempted to poke literal ${ref.instanceName})") backend.poke(ref, value, None) val verifyVal = backend.peek(ref, None) @@ -87,7 +87,7 @@ trait ChiselPokeTesterUtils extends Assertions { // Dynamic testbenches may be a specialized option later. /** Internal: read a value into the circuit. */ - private[iotesters] def peek(ref: Bits): BigInt = { + private[iotesters] def peek[T <: Element: Pokeable](ref: T): BigInt = { backend.peek(ref, None) } } @@ -129,7 +129,7 @@ trait PokeTester extends ChiselPokeTesterUtils { trait ImplicitPokeTester extends ChiselPokeTesterUtils { /** Pokes a value into the circuit. */ - def poke(ref: Bits, value: BigInt)(implicit t: InnerTester) { + def poke[T <: Element: Pokeable](ref: T, value: BigInt)(implicit t: InnerTester) { t.poke(ref, value) } @@ -140,12 +140,12 @@ trait ImplicitPokeTester extends ChiselPokeTesterUtils { // Wrapper for check when no explicit message is passed in. // Scala doesn't allow multiple overloaded functions with default arguments. - def check(ref: Bits, value: BigInt)(implicit t: InnerTester) { + def check[T <: Element: Pokeable](ref: T, value: BigInt)(implicit t: InnerTester) { check(ref, value, "") } /** Asserts that the node's simulation value is equal to the given value. */ - def check(ref: Bits, value: BigInt, msg: String)(implicit t: InnerTester) { + def check[T <: Element: Pokeable](ref: T, value: BigInt, msg: String)(implicit t: InnerTester) { t.expect(ref, value, msg) } diff --git a/src/main/scala/chisel3/iotesters/FirrtlTerpBackend.scala b/src/main/scala/chisel3/iotesters/FirrtlTerpBackend.scala index 3c3ea108..d13acab0 100644 --- a/src/main/scala/chisel3/iotesters/FirrtlTerpBackend.scala +++ b/src/main/scala/chisel3/iotesters/FirrtlTerpBackend.scala @@ -23,7 +23,7 @@ private[iotesters] class FirrtlTerpBackend( def poke(signal: InstanceId, value: BigInt, off: Option[Int]) (implicit logger: TestErrorLog, verbose: Boolean, base: Int): Unit = { signal match { - case port: Bits => + case port: Element => val name = portNames(port) interpretiveTester.poke(name, value) if (verbose) logger info s" POKE $name <- ${bigIntToStr(value, base)}" @@ -45,7 +45,7 @@ private[iotesters] class FirrtlTerpBackend( def peek(signal: InstanceId, off: Option[Int]) (implicit logger: TestErrorLog, verbose: Boolean, base: Int): BigInt = { signal match { - case port: Bits => + case port: Element => val name = portNames(port) val result = interpretiveTester.peek(name) if (verbose) logger info s" PEEK $name -> ${bigIntToStr(result, base)}" @@ -63,7 +63,7 @@ private[iotesters] class FirrtlTerpBackend( def expect(signal: InstanceId, expected: BigInt, msg: => String) (implicit logger: TestErrorLog, verbose: Boolean, base: Int) : Boolean = { signal match { - case port: Bits => + case port: Element => val name = portNames(port) val got = interpretiveTester.peek(name) val good = got == expected diff --git a/src/main/scala/chisel3/iotesters/PeekPokeTester.scala b/src/main/scala/chisel3/iotesters/PeekPokeTester.scala index 1ba7dd51..01019070 100644 --- a/src/main/scala/chisel3/iotesters/PeekPokeTester.scala +++ b/src/main/scala/chisel3/iotesters/PeekPokeTester.scala @@ -21,18 +21,18 @@ trait PeekPokeTests { implicit def int(x: Boolean): BigInt implicit def int(x: Int): BigInt implicit def int(x: Long): BigInt - implicit def int(x: Bits): BigInt + implicit def int[T <: Element: Pokeable](x: T): BigInt def println(msg: String = ""): Unit def reset(n: Int): Unit def step(n: Int): Unit def poke(path: String, x: BigInt): Unit def peek(path: String): BigInt - def poke(signal: Bits, x: BigInt): Unit - def pokeAt[T <: Bits](signal: Mem[T], x: BigInt, off: Int): Unit - def peek(signal: Bits): BigInt - def peekAt[T <: Bits](signal: Mem[T], off: Int): BigInt + def poke[T <: Element: Pokeable](signal: T, x: BigInt): Unit + def pokeAt[T <: Element: Pokeable](signal: Mem[T], x: BigInt, off: Int): Unit + def peek[T <: Element: Pokeable](signal: T): BigInt + def peekAt[T <: Element: Pokeable](signal: Mem[T], off: Int): BigInt def expect(good: Boolean, msg: => String): Boolean - def expect(signal: Bits, expected: BigInt, msg: => String = ""): Boolean + def expect[T <: Element: Pokeable](signal: T, expected: BigInt, msg: => String = ""): Boolean def finish: Boolean } @@ -95,8 +95,8 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( /** Convert a Boolean to BigInt */ implicit def int(x: Boolean): BigInt = if (x) 1 else 0 - /** Convert Bits to BigInt */ - implicit def int(x: Bits): BigInt = x.litValue() + /** Convert Pokeables to BigInt */ + implicit def int[T <: Element: Pokeable](x: T): BigInt = x.litValue() /** * Convert an Int to unsigned (effectively 32-bit) BigInt @@ -135,16 +135,16 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( def peek(path: String) = backend.peek(path) - def poke(signal: Bits, value: BigInt): Unit = { + def poke[T <: Element: Pokeable](signal: T, value: BigInt): Unit = { if (!signal.isLit) backend.poke(signal, value, None) // TODO: Warn if signal.isLit } - def poke(signal: Bits, value: Int) { + def poke[T <: Element: Pokeable](signal: T, value: Int) { poke(signal, BigInt(value)) } - def poke(signal: Bits, value: Long) { + def poke[T <: Element: Pokeable](signal: T, value: Long) { poke(signal, BigInt(value)) } @@ -156,13 +156,13 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( /** Locate a specific bundle element, given a name path. * TODO: Handle Vecs * - * @param path - list of element names (presumably bundles) terminating in a non-bundle (i.e., Bits) element. + * @param path - js (presumably bundles) terminating in a non-bundle (e.g., Bits) element. * @param bundle - bundle containing the element - * @return the element (as Bits) + * @return the element (as Element) */ - private def getBundleElement(path: List[String], bundle: ListMap[String, Data]): Bits = { + private def getBundleElement(path: List[String], bundle: ListMap[String, Data]): Element = { (path, bundle(path.head)) match { - case (head :: Nil, element: Bits) => element + case (head :: Nil, element: Element) => element case (head :: tail, b: Bundle) => getBundleElement(tail, b.elements) case _ => throw new Exception(s"peek/poke bundle element mismatch $path") } @@ -178,19 +178,27 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( for ( (key, value) <- map) { val subKeys = key.split('.').toList val element = getBundleElement(subKeys, circuitElements) - poke(element, value) + element match { + case Pokeable(e) => poke(e, value) + case _ => throw new Exception(s"Cannot poke type ${element.getClass}") + } } } def poke(signal: Aggregate, value: IndexedSeq[BigInt]): Unit = { - (extractElementBits(signal) zip value.reverse).foreach(x => poke(x._1.asInstanceOf[Bits], x._2)) + (extractElementBits(signal) zip value.reverse).foreach{ case (elem, value) => + elem match { + case Pokeable(e) => poke(e, value) + case _ => throw new Exception(s"Cannot poke type ${elem.getClass}") + } + } } - def pokeAt[TT <: Bits](data: Mem[TT], value: BigInt, off: Int): Unit = { + def pokeAt[TT <: Element: Pokeable](data: Mem[TT], value: BigInt, off: Int): Unit = { backend.poke(data, value, Some(off)) } - def peek(signal: Bits):BigInt = { + def peek[T <: Element: Pokeable](signal: T):BigInt = { if (!signal.isLit) backend.peek(signal, None) else signal.litValue() } @@ -203,10 +211,10 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( } def peek(signal: Aggregate): Seq[BigInt] = { - extractElementBits(signal) map (x => backend.peek(x.asInstanceOf[Bits], None)) + extractElementBits(signal) map (x => backend.peek(x.asInstanceOf[Element], None)) } - /** Populate a map of names ("dotted Bundles) to Bits. + /** Populate a map of names ("dotted Bundles) to Elements. * TODO: Deal with Vecs * * @param map the map to be constructed @@ -214,16 +222,16 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( * @param signalName the signal to be added to the map * @param signalData the signal object to be added to the map */ - private def setBundleElement(map: mutable.LinkedHashMap[String, Bits], indexPrefix: ArrayBuffer[String], signalName: String, signalData: Data): Unit = { + private def setBundleElement(map: mutable.LinkedHashMap[String, Element], indexPrefix: ArrayBuffer[String], signalName: String, signalData: Data): Unit = { indexPrefix += signalName signalData match { case bundle: Bundle => for ((name, value) <- bundle.elements) { setBundleElement(map, indexPrefix, name, value) } - case bits: Bits => + case elem: Element => val index = indexPrefix.mkString(".") - map(index) = bits + map(index) = elem } indexPrefix.remove(indexPrefix.size - 1) } @@ -234,20 +242,21 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( * @return a map of signal names ("dotted" Bundle) to BigInt values. */ def peek(signal: Bundle): mutable.LinkedHashMap[String, BigInt] = { - val bitsMap = mutable.LinkedHashMap[String, Bits]() + val elemMap = mutable.LinkedHashMap[String, Element]() val index = ArrayBuffer[String]() - // Populate the Bits map. + // Populate the Element map. for ((elementName, elementValue) <- signal.elements) { - setBundleElement(bitsMap, index, elementName, elementValue) + setBundleElement(elemMap, index, elementName, elementValue) } val bigIntMap = mutable.LinkedHashMap[String, BigInt]() - for ((name, bits) <- bitsMap) { - bigIntMap(name) = peek(bits) + elemMap.foreach { + case (name, Pokeable(e)) => bigIntMap(name) = peek(e) } + bigIntMap } - def peekAt[TT <: Bits](data: Mem[TT], off: Int): BigInt = { + def peekAt[TT <: Element: Pokeable](data: Mem[TT], off: Int): BigInt = { backend.peek(data, Some(off)) } @@ -257,7 +266,7 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( good } - def expect(signal: Bits, expected: BigInt, msg: => String = ""): Boolean = { + def expect[T <: Element: Pokeable](signal: T, expected: BigInt, msg: => String = ""): Boolean = { if (!signal.isLit) { val good = backend.expect(signal, expected, msg) if (!good) fail @@ -265,7 +274,7 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( } else expect(signal.litValue() == expected, s"${signal.litValue()} == $expected") } - def expect(signal: Bits, expected: Int, msg: => String): Boolean = { + def expect[T <: Element: Pokeable](signal: T, expected: Int, msg: => String): Boolean = { expect(signal, BigInt(expected), msg) } @@ -276,7 +285,11 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( } def expect (signal: Aggregate, expected: IndexedSeq[BigInt]): Boolean = { - (extractElementBits(signal), expected.reverse).zipped.foldLeft(true) { (result, x) => result && expect(x._1.asInstanceOf[Bits], x._2)} + (extractElementBits(signal), expected.reverse).zipped.forall { case (elem, value) => + elem match { + case Pokeable(e) => expect(e, value) + } + } } /** Return true or false if an aggregate signal (Bundle) matches the expected map of values. @@ -287,12 +300,17 @@ abstract class PeekPokeTester[+T <: MultiIOModule]( * @return true if the specified values match, false otherwise. */ def expect (signal: Bundle, expected: Map[String, BigInt]): Boolean = { - val bitsMap = mutable.LinkedHashMap[String, Bits]() + val elemMap = mutable.LinkedHashMap[String, Element]() val index = ArrayBuffer[String]() for ((elementName, elementValue) <- signal.elements) { - setBundleElement(bitsMap, index, elementName, elementValue) + setBundleElement(elemMap, index, elementName, elementValue) + } + expected.forall { case (name, value) => + elemMap(name) match { + case Pokeable(e) => expect(e, value) + case default => throw new Exception(s"Cannot poke type ${default.getClass}") + } } - expected.forall{ case ((name, value)) => expect(bitsMap(name), value) } } def finish: Boolean = { diff --git a/src/main/scala/chisel3/iotesters/Pokeable.scala b/src/main/scala/chisel3/iotesters/Pokeable.scala new file mode 100644 index 00000000..da50f39f --- /dev/null +++ b/src/main/scala/chisel3/iotesters/Pokeable.scala @@ -0,0 +1,22 @@ +package chisel3.iotesters + +import chisel3.Bits +import chisel3.core.{Element, EnumType} +import scala.annotation.implicitNotFound + +// A typeclass that defines the types we can poke, peek, or expect from +@implicitNotFound("Cannot peek or poke elements of type ${T}") +trait Pokeable[-T] + +object Pokeable { + implicit object BitsPokeable extends Pokeable[Bits] + implicit object EnumPokeable extends Pokeable[EnumType] + + trait IsRuntimePokeable // A trait that is applied to elements that were proven to be pokeable at runtime (usually in match statements) + implicit object RuntimePokeable extends Pokeable[IsRuntimePokeable] + + def unapply(elem: Element): Option[Element with IsRuntimePokeable] = elem match { + case _: Bits | _: EnumType => Some(elem.asInstanceOf[Element with IsRuntimePokeable]) + case _ => None + } +} diff --git a/src/main/scala/chisel3/iotesters/TreadleBackend.scala b/src/main/scala/chisel3/iotesters/TreadleBackend.scala index b0ad5678..81260596 100644 --- a/src/main/scala/chisel3/iotesters/TreadleBackend.scala +++ b/src/main/scala/chisel3/iotesters/TreadleBackend.scala @@ -2,7 +2,7 @@ package chisel3.iotesters -import chisel3.{Bits, ChiselExecutionSuccess, Mem, assert} +import chisel3.{Element, ChiselExecutionSuccess, Mem, assert} import chisel3.experimental.MultiIOModule import chisel3.internal.InstanceId import firrtl.{FirrtlExecutionFailure, FirrtlExecutionSuccess} @@ -26,7 +26,7 @@ extends Backend(_seed = System.currentTimeMillis()) { def poke(signal: InstanceId, value: BigInt, off: Option[Int]) (implicit logger: TestErrorLog, verbose: Boolean, base: Int): Unit = { signal match { - case port: Bits => + case port: Element => val name = portNames(port) treadleTester.poke(name, value) if (verbose) logger info s" POKE $name <- ${bigIntToStr(value, base)}" @@ -48,7 +48,7 @@ extends Backend(_seed = System.currentTimeMillis()) { def peek(signal: InstanceId, off: Option[Int]) (implicit logger: TestErrorLog, verbose: Boolean, base: Int): BigInt = { signal match { - case port: Bits => + case port: Element => val name = portNames(port) val result = treadleTester.peek(name) if (verbose) logger info s" PEEK $name -> ${bigIntToStr(result, base)}" @@ -66,7 +66,7 @@ extends Backend(_seed = System.currentTimeMillis()) { def expect(signal: InstanceId, expected: BigInt, msg: => String) (implicit logger: TestErrorLog, verbose: Boolean, base: Int) : Boolean = { signal match { - case port: Bits => + case port: Element => val name = portNames(port) val got = treadleTester.peek(name) val good = got == expected diff --git a/src/test/scala/chisel3/iotesters/EnumSpec.scala b/src/test/scala/chisel3/iotesters/EnumSpec.scala new file mode 100644 index 00000000..ca3cf486 --- /dev/null +++ b/src/test/scala/chisel3/iotesters/EnumSpec.scala @@ -0,0 +1,179 @@ +package chisel3.iotesters + +import chisel3._ +import chisel3.util._ +import chisel3.experimental.ChiselEnum +import org.scalatest.{FlatSpec, Matchers} + +object MyEnum extends ChiselEnum { + val e0, e1, e3, e4 = Value +} + +// Passes an enum with one cycle delay +class EnumPassThrough extends Module { + val io = IO(new Bundle { + val in = Input(MyEnum()) + val out = Output(MyEnum()) + }) + + io.out := RegNext(io.in) +} + +// Passes a Vec of enums with one cycle delay +class EnumVecPassThrough(size: Int) extends Module { + val io = IO(new Bundle { + val in = Input(Vec(size, MyEnum())) + val out = Output(Vec(size, MyEnum())) + }) + + io.out <> RegNext(io.in) +} + +class EnumMem(val size: Int) extends Module { + val io = IO(new Bundle { + val addr = Input(UInt(log2Ceil(size).W)) + val read = Output(MyEnum()) + + val read_u = Output(UInt(32.W)) + val read_s = Output(SInt(32.W)) + }) + + val mem = Mem(size, MyEnum()) + io.read := mem(io.addr) + + val mem_u = Mem(size, UInt(32.W)) + val mem_s = Mem(size, SInt(32.W)) + + io.read_u := mem_u(io.addr) + io.read_s := mem_s(io.addr) +} + +class EnumPeekPokeTester(c: EnumPassThrough) extends PeekPokeTester(c) { + for (e <- MyEnum.all) { + poke(c.io.in, e) + step(1) + expect(c.io.out, e) + } +} + +class IncorrectEnumPeekPokeTester(c: EnumPassThrough) extends PeekPokeTester(c) { + for (e <- MyEnum.all) { + poke(c.io.in, e) + step(1) + expect(c.io.out, MyEnum.all.head) + } +} + +class EnumVecPeekPokeTester(c: EnumVecPassThrough) extends PeekPokeTester(c) { + // When poking Vecs directly, enums must be converted to their literal values. This is because there is currently no + // implicit conversion between IndexedSeq[EnumType] and IndexedSeq[BigInt]. + + poke(c.io.in, MyEnum.all.toIndexedSeq.map(_.litValue())) + step(1) + expect(c.io.out, MyEnum.all.toIndexedSeq.map(_.litValue())) +} + +class EnumMemPeekPokeTester(c: EnumMem) extends PeekPokeTester(c) { + for (i <- 0 until c.size) { + val e = MyEnum.all(i % MyEnum.all.size) + pokeAt(c.mem, e, i) + expect(peekAt(c.mem, i) == e.litValue, "Enum memory is not correct") + } + + for (i <- 0 until c.size) { + val e = MyEnum.all(i % MyEnum.all.size) + poke(c.io.addr, i) + step(1) + expect(c.io.read, e, "Enum memory is incorrect") + } +} + +class ReadyValidEnumShifter(val delay: Int) extends Module { + val io = IO(new Bundle { + val in = Flipped(DecoupledIO(MyEnum())) + val out = ValidIO(MyEnum()) + }) + + val cnt = RegInit(0.U(log2Ceil(delay+1).W)) + val req_fire = io.in.ready && io.in.valid + + cnt := 0.U + when (req_fire || (cnt > 0.U && cnt < delay.U)) { + cnt := cnt + 1.U + } + + io.out.bits := ShiftRegister(io.in.bits, delay) + io.out.valid := cnt >= delay.U + io.in.ready := cnt === 0.U +} + +class EnumAdvTester(c: ReadyValidEnumShifter) extends AdvTester(c) { + val enumShiftOutputHandler = new ValidSink(c.io.out, (outPort: MyEnum.Type) => { + peek(outPort) + }) + + val enumShiftInputDriver = new DecoupledSource(c.io.in, (inPort: MyEnum.Type, inValue: MyEnum.Type) => { + wire_poke(inPort, inValue) + }) + + for (e <- MyEnum.all) { + enumShiftInputDriver.inputs.enqueue(e) + enumShiftInputDriver.process() + eventually(enumShiftOutputHandler.outputs.nonEmpty, c.delay + 10) + val result = enumShiftOutputHandler.outputs.dequeue() + println(s"Result = $result") + assert(result == e.litValue(), "Enum output was not correct") + } +} + +class EnumSpec extends ChiselFlatSpec with Matchers { + def testPeekPoke(args: Array[String], skip_mem: Boolean = false) = { + iotesters.Driver.execute(args, () => new EnumPassThrough) { c => + new EnumPeekPokeTester(c) + } && + !iotesters.Driver.execute(args, () => new EnumPassThrough) { c => + new IncorrectEnumPeekPokeTester(c) + } && + iotesters.Driver.execute(args, () => new EnumVecPassThrough(256)) { c => + new EnumVecPeekPokeTester(c) + } && + (skip_mem || iotesters.Driver.execute(args, () => new EnumMem(256)) { c => + new EnumMemPeekPokeTester(c) + }) + } + + behavior of "Enum PeekPokeTesters" + + it should "work with a firrtl backend" in { + testPeekPoke(Array("--backend-name", "firrtl")) should be(true) + } + + it should "work with a treadle backend" in { + testPeekPoke(Array("--backend-name", "treadle")) should be(true) + } + + // pokeAt and peekAt seem to be broken when using Verilator, so we skip the memory tests + it should "work with a verilator backend" in { + testPeekPoke(Array("--backend-name", "verilator"), true) should be(true) + } + + behavior of "Enum AdvTester" + + it should "work with a firrtl backend" in { + iotesters.Driver.execute(Array("--backend-name", "firrtl"), () => new ReadyValidEnumShifter(4)) { c => + new EnumAdvTester(c) + } should be(true) + } + + it should "work with a treadle backend" in { + iotesters.Driver.execute(Array("--backend-name", "treadle"), () => new ReadyValidEnumShifter(4)) { c => + new EnumAdvTester(c) + } should be(true) + } + + it should "work with a verilator backend" in { + iotesters.Driver.execute(Array("--backend-name", "verilator"), () => new ReadyValidEnumShifter(4)) { c => + new EnumAdvTester(c) + } should be(true) + } +} diff --git a/src/test/scala/examples/AdvTesterSpec.scala b/src/test/scala/examples/AdvTesterSpec.scala index 703bda07..b48af08f 100644 --- a/src/test/scala/examples/AdvTesterSpec.scala +++ b/src/test/scala/examples/AdvTesterSpec.scala @@ -69,6 +69,7 @@ class GCDAdvTester(c: RealGCD3) extends AdvTester(c) { val gcdOutputHandler = new ValidSink(c.io.out, (outPort: UInt) => { peek(outPort) }) + val gcdInputDriver = new DecoupledSource(c.io.in, (inPorts: RealGCD3Input, inValues: TestGCD3Values) => { wire_poke(inPorts.a, inValues.a) wire_poke(inPorts.b, inValues.b) @@ -83,7 +84,8 @@ class GCDAdvTester(c: RealGCD3) extends AdvTester(c) { gcdInputDriver.process() eventually(gcdOutputHandler.outputs.size != 0, nCycles + 2) val result = gcdOutputHandler.outputs.dequeue() - assert(result == gcd_value) + println(s"result = $result") + assert(result == gcd_value, "gcd did not compute the correct value") } } diff --git a/src/test/scala/examples/PeekPokeBundleSpec.scala b/src/test/scala/examples/PeekPokeBundleSpec.scala index 5bce1190..7a1f4737 100644 --- a/src/test/scala/examples/PeekPokeBundleSpec.scala +++ b/src/test/scala/examples/PeekPokeBundleSpec.scala @@ -3,6 +3,7 @@ package examples import chisel3._ +import chisel3.experimental.ChiselEnum import chisel3.iotesters.PeekPokeTester import org.scalatest.{FlatSpec, Matchers} @@ -17,11 +18,16 @@ class PeekPokeBundleSpec extends FlatSpec with Matchers { override def cloneType: ABundle.this.type = new ABundle().asInstanceOf[ABundle.this.type] } - class MyBundle extends Bundle { + object MyEnum extends ChiselEnum { + val e0, e1 = Value + } + + class MyBundle extends Bundle { val aUInt4 = UInt(4.W) val aSInt5 = SInt(5.W) val aBundle = new ABundle() val aBottomBool = Bool() + val anEnum = MyEnum() // Since this bundle is defined within a class, we need an explicit cloneType method. override def cloneType: MyBundle.this.type = new MyBundle().asInstanceOf[MyBundle.this.type] @@ -45,7 +51,8 @@ class PeekPokeBundleSpec extends FlatSpec with Matchers { ("aUInt4" -> BigInt(3) ), ("aSInt5" -> BigInt(2) ), ("aBundle.aBool" -> BigInt(1) ), - ("aBottomBool" -> BigInt(0) ) + ("aBottomBool" -> BigInt(0) ), + ("anEnum" -> MyEnum.e1) ) poke(dut.io.in, myBundleMap.values.toArray) step(1) @@ -59,10 +66,12 @@ class PeekPokeBundleSpec extends FlatSpec with Matchers { ("aUInt4" -> BigInt(4) ), ("aSInt5" -> BigInt(5) ), ("aBundle.aBool" -> BigInt(0) ), - ("aBottomBool" -> BigInt(1) ) + ("aBottomBool" -> BigInt(1) ), + ("anEnum" -> MyEnum.e1) ) poke(dut.io.in, myBundleMap.toMap) step(1) + expect(dut.io.out, myBundleMap.toMap) }