From 1ca38c6c958f39612308dd5998423b0e5729d2e4 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 10 Apr 2017 20:39:05 -0700 Subject: [PATCH] Add IntermediateBackend for generating verilog testbenches --- .../chisel3/iotesters/ChiselPokeSpec.scala | 16 ++- .../iotesters/IntermediateBackend.scala | 108 ++++++++++++++++++ .../chisel3/iotesters/TesterOptions.scala | 7 +- 3 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 src/main/scala/chisel3/iotesters/IntermediateBackend.scala diff --git a/src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala b/src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala index e78cbb89..a16639e2 100644 --- a/src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala +++ b/src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala @@ -10,6 +10,11 @@ import chisel3.iotesters._ sealed trait TesterBackend { def create[T <: Module](dutGen: () => T, options: TesterOptionsManager): (T, Backend) } +case object IntermediateBackend extends TesterBackend { + override def create[T <: Module](dutGen: () => T, options: TesterOptionsManager): (T, Backend) = { + setupIntermediateBackend(dutGen, options) + } +} case object FirrtlInterpreterBackend extends TesterBackend { override def create[T <: Module](dutGen: () => T, options: TesterOptionsManager): (T, Backend) = { setupFirrtlTerpBackend(dutGen, options) @@ -40,9 +45,10 @@ trait ChiselPokeTesterUtils extends Assertions { // Map-based Bundle expect/pokes currently not supported because those don't compile-time check def expect(ref: Bits, 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)") + backend.expect(ref, value, msg) + // 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. @@ -50,8 +56,8 @@ trait ChiselPokeTesterUtils extends Assertions { def poke(ref: Bits, value: BigInt) { assert(!ref.isLit, s"(attempted to poke literal ${ref.instanceName})") backend.poke(ref, value, None) - val verifyVal = backend.peek(ref, None) - assert(verifyVal == value, s"(poke failed on ${ref.instanceName} <= $value, read back $verifyVal)") + // val verifyVal = backend.peek(ref, None) + // assert(verifyVal == value, s"(poke failed on ${ref.instanceName} <= $value, read back $verifyVal)") } /** Steps the circuit by the specified number of clock cycles. diff --git a/src/main/scala/chisel3/iotesters/IntermediateBackend.scala b/src/main/scala/chisel3/iotesters/IntermediateBackend.scala new file mode 100644 index 00000000..4b134339 --- /dev/null +++ b/src/main/scala/chisel3/iotesters/IntermediateBackend.scala @@ -0,0 +1,108 @@ +// See LICENSE for license details. +package chisel3.iotesters + +import java.io.PrintStream + +import chisel3._ +import chisel3.internal.InstanceId + +sealed trait Statement { + def serialize: String +} + +case class PokeStatement(signal: String, value: BigInt) extends Statement { + def serialize = s"poke $signal : $value" +} +case class ExpectStatement(signal: String, value: BigInt, msg: String) extends Statement { + val msgReplaced = msg.replace("\n", ":") + def serialize = s"expect $signal : $value : $msgReplaced" +} +case class StepStatement(n: Int) extends Statement { + def serialize = s"step $n" +} +case class ResetStatement(n: Int) extends Statement { + def serialize = s"reset $n" +} + +class IntermediateBackend( + dut: Module, + optionsManager: TesterOptionsManager = new TesterOptionsManager) + extends Backend(_seed = System.currentTimeMillis()) +{ + + val portNames = getDataNames("io", dut.io).toMap + + val statements = scala.collection.mutable.Queue[Statement]() + + def poke(signal: InstanceId, value: BigInt, off: Option[Int]) + (implicit logger: PrintStream, verbose: Boolean, base: Int): Unit = { + signal match { + case port: Bits => + val name = portNames(port) + poke(name, value) + case _ => + } + } + + def peek(signal: InstanceId, off: Option[Int]) + (implicit logger: PrintStream, verbose: Boolean, base: Int): BigInt = { + throw new Exception("Peek not supported!") + } + + def poke(path: String, value: BigInt) + (implicit logger: PrintStream, verbose: Boolean, base: Int): Unit = { + statements += PokeStatement(path, value) + } + + def peek(path: String) + (implicit logger: PrintStream, verbose: Boolean, base: Int): BigInt = { + throw new Exception("Peek not supported!") + } + + def expect(signal: InstanceId, expected: BigInt, msg: => String) + (implicit logger: PrintStream, verbose: Boolean, base: Int): Boolean = { + signal match { + case port: Bits => + val name = portNames(port) + expect(name, expected, msg) + case _ => + false + } + } + + def expect(path: String, expected: BigInt, msg: => String) + (implicit logger: PrintStream, verbose: Boolean, base: Int): Boolean = { + statements += ExpectStatement(path, expected, msg) + true + } + + def step(n: Int)(implicit logger: PrintStream): Unit = { + statements += StepStatement(n) + } + + def reset(n: Int): Unit = { + statements += ResetStatement(n) + } + + def finish(implicit logger: PrintStream): Unit = { + optionsManager.testerOptions.intermediateReportFunc(statements) + } + +} + +private[iotesters] object setupIntermediateBackend +{ + def apply[T <: chisel3.Module]( + dutGen: () => T, + optionsManager: TesterOptionsManager = new TesterOptionsManager): (T, Backend) = + { + chisel3.Driver.execute(optionsManager, dutGen) match { + case ChiselExecutionSuccess(Some(circuit), firrtlText, Some(firrtlExecutionResult)) => + val dut = getTopModule(circuit).asInstanceOf[T] + (dut, new IntermediateBackend(dut, optionsManager = optionsManager)) + case _ => + throw new Exception("Problem with compilation") + } + } +} + diff --git a/src/main/scala/chisel3/iotesters/TesterOptions.scala b/src/main/scala/chisel3/iotesters/TesterOptions.scala index e37c6eef..32fd9047 100644 --- a/src/main/scala/chisel3/iotesters/TesterOptions.scala +++ b/src/main/scala/chisel3/iotesters/TesterOptions.scala @@ -3,6 +3,7 @@ package chisel3.iotesters import java.io.File +import scala.collection.mutable.Queue import chisel3.HasChiselExecutionOptions import firrtl.{HasFirrtlOptions, ComposableOptions, ExecutionOptionsManager} @@ -22,7 +23,9 @@ case class TesterOptions( testCmd: Seq[String] = Seq.empty, backendName: String = "firrtl", logFileName: String = "", - waveform: Option[File] = None) extends ComposableOptions + waveform: Option[File] = None, + intermediateReportFunc: Queue[Statement] => Unit = _.foreach { s => println(s.serialize) } + ) extends ComposableOptions trait HasTesterOptions { self: ExecutionOptionsManager => @@ -34,7 +37,7 @@ trait HasTesterOptions { parser.opt[String]("backend-name").valueName("") .abbr("tbn") .validate { x => - if (Array("firrtl", "verilator", "vcs").contains(x.toLowerCase)) parser.success + if (Array("firrtl", "verilator", "vcs", "intermediate").contains(x.toLowerCase)) parser.success else parser.failure(s"$x not a legal backend name") } .foreach { x => testerOptions = testerOptions.copy(backendName = x) }