Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Create a new Pokeable typeclass that describes elements which can be
Browse files Browse the repository at this point in the history
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
hngenc committed Nov 20, 2018
1 parent db18036 commit fb9912a
Showing 9 changed files with 297 additions and 65 deletions.
26 changes: 14 additions & 12 deletions src/main/scala/chisel3/iotesters/AdvTester.scala
Original file line number Diff line number Diff line change
@@ -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))
}
}
12 changes: 6 additions & 6 deletions src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala
Original file line number Diff line number Diff line change
@@ -44,15 +44,15 @@ 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)")
}

/** 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)
}

6 changes: 3 additions & 3 deletions src/main/scala/chisel3/iotesters/FirrtlTerpBackend.scala
Original file line number Diff line number Diff line change
@@ -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
90 changes: 54 additions & 36 deletions src/main/scala/chisel3/iotesters/PeekPokeTester.scala
Original file line number Diff line number Diff line change
@@ -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,27 +211,27 @@ 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
* @param indexPrefix an array of Bundle name prefixes
* @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,15 +266,15 @@ 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
good
} 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 = {
Loading

0 comments on commit fb9912a

Please sign in to comment.