aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/EmissionOption.scala12
-rw-r--r--src/main/scala/firrtl/Emitter.scala60
-rw-r--r--src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala35
-rw-r--r--src/main/scala/firrtl/passes/LowerTypes.scala19
-rw-r--r--src/test/scala/firrtlTests/MemoryInitSpec.scala203
-rw-r--r--src/test/scala/firrtlTests/execution/VerilogExecution.scala6
6 files changed, 319 insertions, 16 deletions
diff --git a/src/main/scala/firrtl/EmissionOption.scala b/src/main/scala/firrtl/EmissionOption.scala
index 11c16e2c..91db1f53 100644
--- a/src/main/scala/firrtl/EmissionOption.scala
+++ b/src/main/scala/firrtl/EmissionOption.scala
@@ -9,6 +9,18 @@ package firrtl
*/
trait EmissionOption
+/** Emission customization options for memories */
+trait MemoryEmissionOption extends EmissionOption {
+ def initValue: MemoryInitValue = MemoryRandomInit
+}
+
+sealed trait MemoryInitValue
+case object MemoryRandomInit extends MemoryInitValue
+case class MemoryScalarInit(value: BigInt) extends MemoryInitValue
+case class MemoryArrayInit(values: Seq[BigInt]) extends MemoryInitValue
+
+/** default Emitter behavior for memories */
+case object MemoryEmissionOptionDefault extends MemoryEmissionOption
/** Emission customization options for registers */
trait RegisterEmissionOption extends EmissionOption {
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index 60099137..3b065bf4 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -475,12 +475,16 @@ class VerilogEmitter extends SeqTransform with Emitter {
*/
private[firrtl] class EmissionOptions(annotations: AnnotationSeq) {
// Private so that we can present an immutable API
+ private val memoryEmissionOption = new EmissionOptionMap[MemoryEmissionOption](MemoryEmissionOptionDefault)
private val registerEmissionOption = new EmissionOptionMap[RegisterEmissionOption](RegisterEmissionOptionDefault)
private val wireEmissionOption = new EmissionOptionMap[WireEmissionOption](WireEmissionOptionDefault)
private val portEmissionOption = new EmissionOptionMap[PortEmissionOption](PortEmissionOptionDefault)
private val nodeEmissionOption = new EmissionOptionMap[NodeEmissionOption](NodeEmissionOptionDefault)
private val connectEmissionOption = new EmissionOptionMap[ConnectEmissionOption](ConnectEmissionOptionDefault)
+ def getMemoryEmissionOption(target: ReferenceTarget): MemoryEmissionOption =
+ memoryEmissionOption(target)
+
def getRegisterEmissionOption(target: ReferenceTarget): RegisterEmissionOption =
registerEmissionOption(target)
@@ -500,6 +504,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
case m : SingleTargetAnnotation[ReferenceTarget] @unchecked with EmissionOption => m
}
// using multiple foreach instead of a single partial function as an Annotation can gather multiple EmissionOptions for simplicity
+ emissionAnnos.foreach { case a :MemoryEmissionOption => memoryEmissionOption += ((a.target,a)) case _ => }
emissionAnnos.foreach { case a :RegisterEmissionOption => registerEmissionOption += ((a.target,a)) case _ => }
emissionAnnos.foreach { case a :WireEmissionOption => wireEmissionOption += ((a.target,a)) case _ => }
emissionAnnos.foreach { case a :PortEmissionOption => portEmissionOption += ((a.target,a)) case _ => }
@@ -586,6 +591,8 @@ class VerilogEmitter extends SeqTransform with Emitter {
// reset. To fix this, we need extra initial block logic to reset async reset registers again
// post-randomize.
val asyncInitials = ArrayBuffer[Seq[Any]]()
+ // memories need to be initialized even when randomization is disabled
+ val memoryInitials = ArrayBuffer[Seq[Any]]()
val simulates = ArrayBuffer[Seq[Any]]()
def bigIntToVLit(bi: BigInt): String =
@@ -751,15 +758,45 @@ class VerilogEmitter extends SeqTransform with Emitter {
}
}
- def initialize_mem(s: DefMemory): Unit = {
+ def initialize_mem(s: DefMemory, opt: MemoryEmissionOption): Unit = {
if (s.depth > maxMemSize) {
maxMemSize = s.depth
}
- val index = wref("initvar", s.dataType)
- val rstring = rand_string(s.dataType, "RANDOMIZE_MEM_INIT")
- ifdefInitials("RANDOMIZE_MEM_INIT") += Seq("for (initvar = 0; initvar < ", bigIntToVLit(s.depth), "; initvar = initvar+1)")
- ifdefInitials("RANDOMIZE_MEM_INIT") += Seq(tab, WSubAccess(wref(s.name, s.dataType), index, s.dataType, SinkFlow),
- " = ", rstring, ";")
+
+ val dataWidth = bitWidth(s.dataType)
+ val maxDataValue = (BigInt(1) << dataWidth.toInt) - 1
+
+ def checkValueRange(value: BigInt, at: String): Unit = {
+ if(value < 0) throw EmitterException(s"Memory ${at} cannot be initialized with negative value: $value")
+ if(value > maxDataValue) throw EmitterException(s"Memory ${at} cannot be initialized with value: $value. Too large (> $maxDataValue)!")
+ }
+
+ opt.initValue match {
+ case MemoryArrayInit(values) =>
+ if(values.length != s.depth) throw EmitterException(
+ s"Memory ${s.name} of depth ${s.depth} cannot be initialized with an array of length ${values.length}!"
+ )
+ val memName = LowerTypes.loweredName(wref(s.name, s.dataType))
+ values.zipWithIndex.foreach { case (value, addr) =>
+ checkValueRange(value, s"${s.name}[$addr]")
+ val access = s"$memName[${bigIntToVLit(addr)}]"
+ memoryInitials += Seq(access, " = ", bigIntToVLit(value), ";")
+ }
+ case MemoryScalarInit(value) =>
+ checkValueRange(value, s.name)
+ // note: s.dataType is the incorrect type for initvar, but it is ignored in the serialization
+ val index = wref("initvar", s.dataType)
+ memoryInitials += Seq("for (initvar = 0; initvar < ", bigIntToVLit(s.depth), "; initvar = initvar+1)")
+ memoryInitials += Seq(tab, WSubAccess(wref(s.name, s.dataType), index, s.dataType, SinkFlow),
+ " = ", bigIntToVLit(value), ";")
+ case MemoryRandomInit =>
+ // note: s.dataType is the incorrect type for initvar, but it is ignored in the serialization
+ val index = wref("initvar", s.dataType)
+ val rstring = rand_string(s.dataType, "RANDOMIZE_MEM_INIT")
+ ifdefInitials("RANDOMIZE_MEM_INIT") += Seq("for (initvar = 0; initvar < ", bigIntToVLit(s.depth), "; initvar = initvar+1)")
+ ifdefInitials("RANDOMIZE_MEM_INIT") += Seq(tab, WSubAccess(wref(s.name, s.dataType), index, s.dataType, SinkFlow),
+ " = ", rstring, ";")
+ }
}
def simulate(clk: Expression, en: Expression, s: Seq[Any], cond: Option[String], info: Info) = {
@@ -914,12 +951,13 @@ class VerilogEmitter extends SeqTransform with Emitter {
}
instdeclares += Seq(");")
case sx: DefMemory =>
+ val options = emissionOptions.getMemoryEmissionOption(moduleTarget.ref(sx.name))
val fullSize = sx.depth * (sx.dataType match {
case GroundType(IntWidth(width)) => width
})
val decl = if (fullSize > (1 << 29)) "reg /* sparse */" else "reg"
declareVectorType(decl, sx.name, sx.dataType, sx.depth, sx.info)
- initialize_mem(sx)
+ initialize_mem(sx, options)
if (sx.readLatency != 0 || sx.writeLatency != 1)
throw EmitterException("All memories should be transformed into " +
"blackboxes or combinational by previous passses")
@@ -1014,7 +1052,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
emit(Seq(tab, "end"))
}
- if (initials.nonEmpty || ifdefInitials.nonEmpty) {
+ if (initials.nonEmpty || ifdefInitials.nonEmpty || memoryInitials.nonEmpty) {
emit(Seq("// Register and memory initialization"))
emit(Seq("`ifdef RANDOMIZE_GARBAGE_ASSIGN"))
emit(Seq("`define RANDOMIZE"))
@@ -1031,7 +1069,8 @@ class VerilogEmitter extends SeqTransform with Emitter {
emit(Seq("`ifndef RANDOM"))
emit(Seq("`define RANDOM $random"))
emit(Seq("`endif"))
- emit(Seq("`ifdef RANDOMIZE_MEM_INIT"))
+ // the initvar is also used to initialize memories to constants
+ if(memoryInitials.isEmpty) emit(Seq("`ifdef RANDOMIZE_MEM_INIT"))
// Since simulators don't actually support memories larger than 2^31 - 1, there is no reason
// to change Verilog emission in the common case. Instead, we only emit a larger initvar
// where necessary
@@ -1042,7 +1081,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
val width = maxMemSize.bitLength - 1 // minus one because [width-1:0] has a width of "width"
emit(Seq(s" reg [$width:0] initvar;"))
}
- emit(Seq("`endif"))
+ if(memoryInitials.isEmpty) emit(Seq("`endif"))
emit(Seq("`ifndef SYNTHESIS"))
// User-defined macro of code to run before an initial block
emit(Seq("`ifdef FIRRTL_BEFORE_INITIAL"))
@@ -1072,6 +1111,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
for (x <- initials) emit(Seq(tab, x))
for (x <- asyncInitials) emit(Seq(tab, x))
emit(Seq(" `endif // RANDOMIZE"))
+ for(x <- memoryInitials) emit(Seq(tab, x))
emit(Seq("end // initial"))
// User-defined macro of code to run after an initial block
emit(Seq("`ifdef FIRRTL_AFTER_INITIAL"))
diff --git a/src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala b/src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala
new file mode 100644
index 00000000..44a8e3b5
--- /dev/null
+++ b/src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala
@@ -0,0 +1,35 @@
+// See LICENSE for license details.
+
+package firrtl.annotations
+
+import firrtl.{MemoryArrayInit, MemoryEmissionOption, MemoryInitValue, MemoryRandomInit, MemoryScalarInit}
+
+/**
+ * Represents the initial value of the annotated memory.
+ * While not supported on normal ASIC flows, it can be useful for simulation and FPGA flows.
+ * This annotation is consumed by the verilog emitter.
+ */
+sealed trait MemoryInitAnnotation extends SingleTargetAnnotation[ReferenceTarget] with MemoryEmissionOption {
+ def isRandomInit: Boolean
+}
+
+/** Randomly initialize the `target` memory. This is the same as the default behavior. */
+case class MemoryRandomInitAnnotation(target: ReferenceTarget) extends MemoryInitAnnotation {
+ override def duplicate(n: ReferenceTarget): Annotation = copy(n)
+ override def initValue: MemoryInitValue = MemoryRandomInit
+ override def isRandomInit: Boolean = true
+}
+
+/** Initialize all entries of the `target` memory with the scalar `value`. */
+case class MemoryScalarInitAnnotation(target: ReferenceTarget, value: BigInt) extends MemoryInitAnnotation {
+ override def duplicate(n: ReferenceTarget): Annotation = copy(n)
+ override def initValue: MemoryInitValue = MemoryScalarInit(value)
+ override def isRandomInit: Boolean = false
+}
+
+/** Initialize the `target` memory with the array of `values` which must be the same size as the memory depth. */
+case class MemoryArrayInitAnnotation(target: ReferenceTarget, values: Seq[BigInt]) extends MemoryInitAnnotation {
+ override def duplicate(n: ReferenceTarget): Annotation = copy(n)
+ override def initValue: MemoryInitValue = MemoryArrayInit(values)
+ override def isRandomInit: Boolean = false
+} \ No newline at end of file
diff --git a/src/main/scala/firrtl/passes/LowerTypes.scala b/src/main/scala/firrtl/passes/LowerTypes.scala
index 4a87ff8b..ccfe5828 100644
--- a/src/main/scala/firrtl/passes/LowerTypes.scala
+++ b/src/main/scala/firrtl/passes/LowerTypes.scala
@@ -8,6 +8,7 @@ import firrtl.ir._
import firrtl.Utils._
import MemPortUtils.memType
import firrtl.Mappers._
+import firrtl.annotations.MemoryInitAnnotation
/** Removes all aggregate types from a [[firrtl.ir.Circuit]]
*
@@ -166,9 +167,9 @@ object LowerTypes extends Transform with DependencyAPIMigration {
case e @ (_: UIntLiteral | _: SIntLiteral) => e
}
def lowerTypesStmt(memDataTypeMap: MemDataTypeMap,
- minfo: Info, mname: String, renames: RenameMap)(s: Statement): Statement = {
+ minfo: Info, mname: String, renames: RenameMap, initializedMems: Set[(String, String)])(s: Statement): Statement = {
val info = get_info(s) match {case NoInfo => minfo case x => x}
- s map lowerTypesStmt(memDataTypeMap, info, mname, renames) match {
+ s map lowerTypesStmt(memDataTypeMap, info, mname, renames, initializedMems) match {
case s: DefWire => s.tpe match {
case _: GroundType => s
case _ =>
@@ -210,6 +211,10 @@ object LowerTypes extends Transform with DependencyAPIMigration {
sx.dataType match {
case _: GroundType => sx
case _ =>
+ // right now only ground type memories can be initialized
+ if(initializedMems.contains((mname, sx.name))) {
+ error(s"Cannot initialize memory of non ground type ${sx.dataType.serialize}")(info, mname)
+ }
// Rename ports
val seen: mutable.Set[String] = mutable.Set[String]()
create_exps(sx.name, memType(sx)) foreach { e =>
@@ -264,7 +269,7 @@ object LowerTypes extends Transform with DependencyAPIMigration {
}
}
- def lowerTypes(renames: RenameMap)(m: DefModule): DefModule = {
+ def lowerTypes(renames: RenameMap, initializedMems: Set[(String, String)])(m: DefModule): DefModule = {
val memDataTypeMap = new MemDataTypeMap
renames.setModule(m.name)
// Lower Ports
@@ -280,15 +285,19 @@ object LowerTypes extends Transform with DependencyAPIMigration {
case m: ExtModule =>
m copy (ports = portsx)
case m: Module =>
- m copy (ports = portsx) map lowerTypesStmt(memDataTypeMap, m.info, m.name, renames)
+ m copy (ports = portsx) map lowerTypesStmt(memDataTypeMap, m.info, m.name, renames, initializedMems)
}
}
def execute(state: CircuitState): CircuitState = {
+ // remember which memories need to be initialized, for these memories, lowering non-ground types is not supported
+ val initializedMems = state.annotations.collect{
+ case m : MemoryInitAnnotation if !m.isRandomInit =>
+ (m.target.encapsulatingModule, m.target.ref) }.toSet
val c = state.circuit
val renames = RenameMap()
renames.setCircuit(c.main)
- val result = c copy (modules = c.modules map lowerTypes(renames))
+ val result = c copy (modules = c.modules map lowerTypes(renames, initializedMems))
CircuitState(result, outputForm, state.annotations, Some(renames))
}
}
diff --git a/src/test/scala/firrtlTests/MemoryInitSpec.scala b/src/test/scala/firrtlTests/MemoryInitSpec.scala
new file mode 100644
index 00000000..0826746b
--- /dev/null
+++ b/src/test/scala/firrtlTests/MemoryInitSpec.scala
@@ -0,0 +1,203 @@
+// See LICENSE for license details.
+
+package firrtlTests
+
+import firrtl._
+import firrtl.annotations._
+import firrtl.testutils.FirrtlCheckers.containLine
+import firrtl.testutils.FirrtlFlatSpec
+import firrtlTests.execution._
+
+class MemInitSpec extends FirrtlFlatSpec {
+ def input(tpe: String): String =
+ s"""
+ |circuit MemTest:
+ | module MemTest:
+ | input clock : Clock
+ | input rAddr : UInt<5>
+ | input rEnable : UInt<1>
+ | input wAddr : UInt<5>
+ | input wData : $tpe
+ | input wEnable : UInt<1>
+ | output rData : $tpe
+ |
+ | mem m:
+ | data-type => $tpe
+ | depth => 32
+ | reader => r
+ | writer => w
+ | read-latency => 1
+ | write-latency => 1
+ | read-under-write => new
+ |
+ | m.r.clk <= clock
+ | m.r.addr <= rAddr
+ | m.r.en <= rEnable
+ | rData <= m.r.data
+ |
+ | m.w.clk <= clock
+ | m.w.addr <= wAddr
+ | m.w.en <= wEnable
+ | m.w.data <= wData
+ | m.w.mask is invalid
+ |
+ |""".stripMargin
+
+ val mRef = CircuitTarget("MemTest").module("MemTest").ref("m")
+ def compile(annos: AnnotationSeq, tpe: String = "UInt<32>"): CircuitState = {
+ (new VerilogCompiler).compileAndEmit(CircuitState(parse(input(tpe)), ChirrtlForm, annos))
+ }
+
+ "NoAnnotation" should "create a randomized initialization" in {
+ val annos = Seq()
+ val result = compile(annos)
+ result should containLine (" m[initvar] = _RAND_0[31:0];")
+ }
+
+ "MemoryRandomInitAnnotation" should "create a randomized initialization" in {
+ val annos = Seq(MemoryRandomInitAnnotation(mRef))
+ val result = compile(annos)
+ result should containLine (" m[initvar] = _RAND_0[31:0];")
+ }
+
+ "MemoryScalarInitAnnotation w/ 0" should "create an initialization with all zeros" in {
+ val annos = Seq(MemoryScalarInitAnnotation(mRef, 0))
+ val result = compile(annos)
+ result should containLine(" m[initvar] = 0;")
+ }
+
+ Seq(1, 3, 30, 400, 12345).foreach { value =>
+ s"MemoryScalarInitAnnotation w/ $value" should
+ s"create an initialization with all values set to $value" in {
+ val annos = Seq(MemoryScalarInitAnnotation(mRef, value))
+ val result = compile(annos)
+ result should containLine(s" m[initvar] = $value;")
+ }
+ }
+
+ "MemoryArrayInitAnnotation" should "initialize all addresses" in {
+ val values = Seq.tabulate(32)(ii => 2 * ii + 5).map(BigInt(_))
+ val annos = Seq(MemoryArrayInitAnnotation(mRef, values))
+ val result = compile(annos)
+ values.zipWithIndex.foreach { case (value, addr) =>
+ result should containLine(s" m[$addr] = $value;")
+ }
+ }
+
+ "MemoryScalarInitAnnotation" should "fail for a negative value" in {
+ assertThrows[EmitterException] {
+ compile(Seq(MemoryScalarInitAnnotation(mRef, -1)))
+ }
+ }
+
+ "MemoryScalarInitAnnotation" should "fail for a value that is too large" in {
+ assertThrows[EmitterException] {
+ compile(Seq(MemoryScalarInitAnnotation(mRef, BigInt(1) << 32)))
+ }
+ }
+
+ "MemoryArrayInitAnnotation" should "fail for a negative value" in {
+ assertThrows[EmitterException] {
+ val values = Seq.tabulate(32)(_ => BigInt(-1))
+ compile(Seq(MemoryArrayInitAnnotation(mRef, values)))
+ }
+ }
+
+ "MemoryArrayInitAnnotation" should "fail for a value that is too large" in {
+ assertThrows[EmitterException] {
+ val values = Seq.tabulate(32)(_ => BigInt(1) << 32)
+ compile(Seq(MemoryArrayInitAnnotation(mRef, values)))
+ }
+ }
+
+ "MemoryArrayInitAnnotation" should "fail if the number of values is too small" in {
+ assertThrows[EmitterException] {
+ val values = Seq.tabulate(31)(_ => BigInt(1))
+ compile(Seq(MemoryArrayInitAnnotation(mRef, values)))
+ }
+ }
+
+ "MemoryArrayInitAnnotation" should "fail if the number of values is too large" in {
+ assertThrows[EmitterException] {
+ val values = Seq.tabulate(33)(_ => BigInt(1))
+ compile(Seq(MemoryArrayInitAnnotation(mRef, values)))
+ }
+ }
+
+ "MemoryScalarInitAnnotation on Memory with Vector type" should "fail" in {
+ val caught = intercept[Exception] {
+ val annos = Seq(MemoryScalarInitAnnotation(mRef, 0))
+ compile(annos, "UInt<32>[2]")
+ }
+ assert(caught.getMessage.endsWith("[module MemTest] Cannot initialize memory of non ground type UInt<32>[2]"))
+ }
+
+ "MemoryScalarInitAnnotation on Memory with Bundle type" should "fail" in {
+ val caught = intercept[Exception] {
+ val annos = Seq(MemoryScalarInitAnnotation(mRef, 0))
+ compile(annos, "{real: SInt<10>, imag: SInt<10>}")
+ }
+ assert(caught.getMessage.endsWith("[module MemTest] Cannot initialize memory of non ground type { real : SInt<10>, imag : SInt<10>}"))
+ }
+
+ private def jsonAnno(name: String, suffix: String): String =
+ s"""[{"class": "firrtl.annotations.$name", "target": "~MemTest|MemTest>m"$suffix}]"""
+
+ "MemoryRandomInitAnnotation" should "load from JSON" in {
+ val json = jsonAnno("MemoryRandomInitAnnotation", "")
+ val annos = JsonProtocol.deserialize(json)
+ assert(annos == Seq(MemoryRandomInitAnnotation(mRef)))
+ }
+
+ "MemoryScalarInitAnnotation" should "load from JSON" in {
+ val json = jsonAnno("MemoryScalarInitAnnotation", """, "value": 1234567890""")
+ val annos = JsonProtocol.deserialize(json)
+ assert(annos == Seq(MemoryScalarInitAnnotation(mRef, 1234567890)))
+ }
+
+ "MemoryArrayInitAnnotation" should "load from JSON" in {
+ val json = jsonAnno("MemoryArrayInitAnnotation", """, "values": [10000000000, 20000000000]""")
+ val annos = JsonProtocol.deserialize(json)
+ val largeSeq = Seq(BigInt("10000000000"), BigInt("20000000000"))
+ assert(annos == Seq(MemoryArrayInitAnnotation(mRef, largeSeq)))
+ }
+
+}
+
+abstract class MemInitExecutionSpec(values: Seq[Int], init: ReferenceTarget => Annotation)
+ extends SimpleExecutionTest with VerilogExecution {
+ override val body: String =
+ s"""
+ |mem m:
+ | data-type => UInt<32>
+ | depth => ${values.length}
+ | reader => r
+ | read-latency => 1
+ | write-latency => 1
+ | read-under-write => new
+ |m.r.clk <= clock
+ |m.r.en <= UInt<1>(1)
+ |""".stripMargin
+
+ val mRef = CircuitTarget("dut").module("dut").ref("m")
+ override val customAnnotations: AnnotationSeq = Seq(init(mRef))
+
+ override def commands: Seq[SimpleTestCommand] = (Seq(-1) ++ values).zipWithIndex.map { case (value, addr) =>
+ if(value == -1) { Seq(Poke("m.r.addr", addr)) }
+ else if(addr >= values.length) { Seq(Expect("m.r.data", value)) }
+ else { Seq(Poke("m.r.addr", addr), Expect("m.r.data", value)) }
+ }.flatMap(_ ++ Seq(Step(1)))
+}
+
+class MemScalarInit0ExecutionSpec extends MemInitExecutionSpec(
+ Seq.tabulate(31)(_ => 0), r => MemoryScalarInitAnnotation(r, 0)
+) {}
+
+class MemScalarInit17ExecutionSpec extends MemInitExecutionSpec(
+ Seq.tabulate(31)(_ => 17), r => MemoryScalarInitAnnotation(r, 17)
+) {}
+
+class MemArrayInitExecutionSpec extends MemInitExecutionSpec(
+ Seq.tabulate(31)(ii => ii * 5 + 7),
+ r => MemoryArrayInitAnnotation(r, Seq.tabulate(31)(ii => ii * 5 + 7).map(BigInt(_)))
+) {}
diff --git a/src/test/scala/firrtlTests/execution/VerilogExecution.scala b/src/test/scala/firrtlTests/execution/VerilogExecution.scala
index f80a5ee6..bf3d1461 100644
--- a/src/test/scala/firrtlTests/execution/VerilogExecution.scala
+++ b/src/test/scala/firrtlTests/execution/VerilogExecution.scala
@@ -13,11 +13,15 @@ import firrtl.options.TargetDirAnnotation
*/
trait VerilogExecution extends TestExecution {
this: SimpleExecutionTest =>
+
+ /** can be overwritten to mix-in custom annotations */
+ val customAnnotations: AnnotationSeq = Seq()
+
def runEmittedDUT(c: Circuit, testDir: File): Unit = {
// Run FIRRTL, emit Verilog file
val cAnno = FirrtlCircuitAnnotation(c)
val tdAnno = TargetDirAnnotation(testDir.getAbsolutePath)
- (new FirrtlStage).run(AnnotationSeq(Seq(cAnno, tdAnno)))
+ (new FirrtlStage).run(AnnotationSeq(Seq(cAnno, tdAnno) ++ customAnnotations))
// Copy harness resource to test directory
val harness = new File(testDir, s"top.cpp")