Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IntermediateBackend for generating verilog testbenches #138

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 11 additions & 5 deletions src/main/scala/chisel3/iotesters/ChiselPokeSpec.scala
Expand Up @@ -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)
Expand Down Expand Up @@ -40,18 +45,19 @@ 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.
*/
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.
Expand Down
108 changes: 108 additions & 0 deletions 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")
}
}
}

7 changes: 5 additions & 2 deletions src/main/scala/chisel3/iotesters/TesterOptions.scala
Expand Up @@ -3,6 +3,7 @@
package chisel3.iotesters

import java.io.File
import scala.collection.mutable.Queue

import chisel3.HasChiselExecutionOptions
import firrtl.{HasFirrtlOptions, ComposableOptions, ExecutionOptionsManager}
Expand All @@ -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 =>
Expand All @@ -34,7 +37,7 @@ trait HasTesterOptions {
parser.opt[String]("backend-name").valueName("<firrtl|verilator|vcs>")
.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) }
Expand Down