aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project/plugins.sbt2
-rw-r--r--src/main/scala/firrtl/annotations/Annotation.scala20
-rw-r--r--src/main/scala/firrtl/passes/Inline.scala8
-rw-r--r--src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala138
-rw-r--r--src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala22
-rw-r--r--src/main/scala/firrtl/passes/wiring/WiringTransform.scala4
-rw-r--r--src/test/scala/firrtlTests/InlineInstancesTests.scala21
-rw-r--r--src/test/scala/firrtlTests/ReplSeqMemTests.scala61
-rw-r--r--src/test/scala/firrtlTests/annotationTests/AnnotationSpec.scala35
9 files changed, 290 insertions, 21 deletions
diff --git a/project/plugins.sbt b/project/plugins.sbt
index f9f4d9ca..ac0e6d61 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -24,7 +24,7 @@ addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.0")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.2")
-addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.9.1")
+addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.9.2")
addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.7")
diff --git a/src/main/scala/firrtl/annotations/Annotation.scala b/src/main/scala/firrtl/annotations/Annotation.scala
index b5c9c7e0..08555a84 100644
--- a/src/main/scala/firrtl/annotations/Annotation.scala
+++ b/src/main/scala/firrtl/annotations/Annotation.scala
@@ -5,6 +5,8 @@ package annotations
import firrtl.options.StageUtils
+import scala.collection.Traversable
+
case class AnnotationException(message: String) extends Exception(message)
/** Base type of auxiliary information */
@@ -23,18 +25,19 @@ trait Annotation extends Product {
* @param ls
* @return
*/
- private def extractComponents(ls: scala.collection.Traversable[_]): Seq[Target] = {
- ls.collect {
+ private def extractComponents(ls: Traversable[_]): Traversable[Target] = {
+ ls.flatMap {
case c: Target => Seq(c)
- case o: Product => extractComponents(o.productIterator.toIterable)
case x: scala.collection.Traversable[_] => extractComponents(x)
- }.foldRight(Seq.empty[Target])((seq, c) => c ++ seq)
+ case o: Product => extractComponents(o.productIterator.toIterable)
+ case _ => Seq()
+ }
}
/** Returns all [[firrtl.annotations.Target Target]] members in this annotation
* @return
*/
- def getTargets: Seq[Target] = extractComponents(productIterator.toSeq)
+ def getTargets: Seq[Target] = extractComponents(productIterator.toIterable).toSeq
}
/** If an Annotation does not target any [[Named]] thing in the circuit, then all updates just
@@ -42,12 +45,17 @@ trait Annotation extends Product {
*/
trait NoTargetAnnotation extends Annotation {
def update(renames: RenameMap): Seq[NoTargetAnnotation] = Seq(this)
+
+ override def getTargets: Seq[Target] = Seq.empty
}
/** An Annotation that targets a single [[Named]] thing */
trait SingleTargetAnnotation[T <: Named] extends Annotation {
val target: T
+ // we can implement getTargets more efficiently since we know that we have exactly one target
+ override def getTargets: Seq[Target] = Seq(target)
+
/** Create another instance of this Annotation */
def duplicate(n: T): Annotation
@@ -100,6 +108,8 @@ trait MultiTargetAnnotation extends Annotation {
*/
def targets: Seq[Seq[Target]]
+ override def getTargets: Seq[Target] = targets.flatten
+
/** Create another instance of this Annotation
*
* The inner Seqs correspond to the renames of the inner Seqs of targets
diff --git a/src/main/scala/firrtl/passes/Inline.scala b/src/main/scala/firrtl/passes/Inline.scala
index 912acf8e..78b3ce36 100644
--- a/src/main/scala/firrtl/passes/Inline.scala
+++ b/src/main/scala/firrtl/passes/Inline.scala
@@ -179,7 +179,7 @@ class InlineInstances extends Transform with DependencyAPIMigration with Registe
/** Add a prefix to all declarations updating a [[Namespace]] and appending to a [[RenameMap]] */
def appendNamePrefix(
- currentModule: IsModule,
+ currentModule: InstanceTarget,
nextModule: IsModule,
prefix: String,
ns: Namespace,
@@ -197,8 +197,13 @@ class InlineInstances extends Transform with DependencyAPIMigration with Registe
}
ofModuleOpt match {
case None =>
+ renameMap.record(currentModule.ofModuleTarget.ref(name), nextModule.ref(prefix + name))
renameMap.record(currentModule.ref(name), nextModule.ref(prefix + name))
case Some(ofModule) =>
+ renameMap.record(
+ currentModule.ofModuleTarget.instOf(name, ofModule),
+ nextModule.instOf(prefix + name, ofModule)
+ )
renameMap.record(currentModule.instOf(name, ofModule), nextModule.instOf(prefix + name, ofModule))
}
renames(name) = prefix + name
@@ -348,7 +353,6 @@ class InlineInstances extends Transform with DependencyAPIMigration with Registe
.map(appendRefPrefix(inlineTarget, prefixMap))
renames.record(inlineTarget, currentModule)
-
renamedBody
case sx =>
sx
diff --git a/src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala b/src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala
index 5cc1e0bf..f88e7e39 100644
--- a/src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala
+++ b/src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala
@@ -4,7 +4,9 @@ package firrtl
package passes
package memlib
+import firrtl.annotations.{CircuitName, ModuleName}
import firrtl.stage.Forms
+import firrtl.transforms.BlackBoxInlineAnno
class DumpMemoryAnnotations extends Transform with DependencyAPIMigration {
@@ -13,16 +15,146 @@ class DumpMemoryAnnotations extends Transform with DependencyAPIMigration {
override def optionalPrerequisiteOf = Forms.MidEmitters
override def invalidates(a: Transform) = false
+ private def vlsiMemGen(annotatedMemory: DefAnnotatedMemory, genBlackBox: Boolean): (String, String) = {
+ // MaskedWritePort => WritePort with masked = true
+ case class Port(prefix: String, portType: MemPort)
+
+ val masked = annotatedMemory.maskGran.isDefined
+
+ val name = annotatedMemory.name
+ val width = {
+ val width = bitWidth(annotatedMemory.dataType)
+ require(width <= Int.MaxValue)
+ width.toInt
+ }
+ val depth = annotatedMemory.depth.toInt
+ val maskGran = if (masked) {
+ val maskGran = annotatedMemory.maskGran.get
+ require(maskGran <= Int.MaxValue)
+ maskGran.toInt
+ } else width
+ val maskSeg = width / maskGran
+ val ports = annotatedMemory.readers.indices.map(number => Port(s"R${number}_", ReadPort)) ++
+ annotatedMemory.writers.indices.map(number => Port(s"W${number}_", WritePort)) ++
+ annotatedMemory.readwriters.indices.map(number => Port(s"RW${number}_", ReadWritePort))
+
+ val addrWidth = math.max(math.ceil(math.log(depth) / math.log(2)).toInt, 1)
+ val readPorts = ports.filter(port => port.portType == ReadPort || port.portType == ReadWritePort)
+
+ val portSpec = ports.flatMap(port =>
+ Seq(s"input ${port.prefix}clk", s"input [${addrWidth - 1}:0] ${port.prefix}addr", s"input ${port.prefix}en") ++
+ ((port, masked) match {
+ case (Port(prefix, ReadPort), _) => Seq(s"output [${width - 1}:0] ${prefix}data")
+ case (Port(prefix, WritePort), false) => Seq(s"input [${width - 1}:0] ${prefix}data")
+ case (Port(prefix, WritePort), true) =>
+ Seq(s"input [${width - 1}:0] ${prefix}data", s"input [${maskSeg - 1}:0] ${prefix}mask")
+ case (Port(prefix, ReadWritePort), false) =>
+ Seq(
+ s"input ${prefix}wmode",
+ s"input [${width - 1}:0] ${prefix}wdata",
+ s"output [${width - 1}:0] ${prefix}rdata"
+ )
+ case (Port(prefix, ReadWritePort), true) =>
+ Seq(
+ s"input ${prefix}wmode",
+ s"input [${maskSeg - 1}:0] ${prefix}wmask",
+ s"input [${width - 1}:0] ${prefix}wdata",
+ s"output [${width - 1}:0] ${prefix}rdata"
+ )
+ })
+ )
+
+ val decl = readPorts.flatMap(port =>
+ Seq(s"reg reg_${port.prefix}ren;", s"reg [${addrWidth - 1}:0] reg_${port.prefix}addr;")
+ ) ++ Seq(
+ s"reg [${width - 1}:0] ram [${depth - 1}:0];",
+ "`ifdef RANDOMIZE_MEM_INIT",
+ " integer initvar;",
+ " initial begin",
+ " #`RANDOMIZE_DELAY begin end",
+ s" for (initvar = 0; initvar < $depth; initvar = initvar+1)",
+ s" ram[initvar] = {${(width - 1) / 32 + 1} {$$random}};"
+ ) ++ readPorts.map(port => s" reg_${port.prefix}addr = {${(addrWidth - 1) / 32 + 1} {$$random}};") ++ Seq(
+ " end",
+ "`endif"
+ )
+
+ val sequential = {
+ def genReadSequential(en: String, prefix: String): Seq[String] = Seq(
+ s"always @(posedge ${prefix}clk)",
+ s" reg_${prefix}ren <= $en;",
+ s"always @(posedge ${prefix}clk)",
+ s" if ($en) reg_${prefix}addr <= ${prefix}addr;"
+ )
+ def genWriteSequential(en: String, prefix: String, inputData: String, maskName: String): Seq[String] = Seq(
+ s"always @(posedge ${prefix}clk)",
+ s" if ($en) begin"
+ ) ++ (0 until maskSeg).map { i =>
+ val ifMask = if (masked) s"if (${prefix}${maskName}[$i]) " else ""
+ val ram_range = s"${(i + 1) * maskGran - 1}:${i * maskGran}"
+ s" ${ifMask}ram[${prefix}addr][$ram_range] <= ${prefix}$inputData[$ram_range];"
+ } ++ Seq(" end")
+ ports.flatMap(port =>
+ port.portType match {
+ case ReadPort => genReadSequential(port.prefix + "en", port.prefix)
+ case WritePort => genWriteSequential(port.prefix + "en", port.prefix, "data", "mask")
+ case ReadWritePort =>
+ genReadSequential(s"${port.prefix}en && !${port.prefix}wmode", port.prefix) ++
+ genWriteSequential(s"${port.prefix}en && ${port.prefix}wmode", port.prefix, "wdata", "wmask")
+ }
+ )
+ }
+
+ val combinational = readPorts.flatMap { port =>
+ val data = port.prefix + (if (port.portType == ReadWritePort) "rdata" else "data")
+ Seq(
+ "`ifdef RANDOMIZE_GARBAGE_ASSIGN",
+ s"reg [${((width - 1) / 32 + 1) * 32 - 1}:0] ${port.prefix}random;",
+ "`ifdef RANDOMIZE_MEM_INIT",
+ " initial begin",
+ " #`RANDOMIZE_DELAY begin end",
+ s" ${port.prefix}random = {${Seq.fill((width - 1) / 32 + 1)("$random").mkString(", ")}};",
+ s" reg_${port.prefix}ren = ${port.prefix}random[0];",
+ " end",
+ "`endif",
+ s"always @(posedge ${port.prefix}clk) ${port.prefix}random <= {${Seq.fill((width - 1) / 32 + 1)("$random").mkString(", ")}};",
+ s"assign $data = reg_${port.prefix}ren ? ram[reg_${port.prefix}addr] : ${port.prefix}random[${width - 1}:0];",
+ "`else",
+ s"assign $data = ram[reg_${port.prefix}addr];",
+ "`endif"
+ )
+ }
+
+ val body = if (genBlackBox) "" else s"""
+ | ${decl.mkString("\n ")}
+ | ${sequential.mkString("\n ")}
+ | ${combinational.mkString("\n ")}""".stripMargin
+
+ (name, s"""// name:$name depth:$depth width:$width masked:$masked maskGran:$maskGran maskSeg:$maskSeg
+ |module $name(
+ | ${portSpec.mkString(",\n ")}
+ |);
+ |
+ |$body
+ |
+ |endmodule""".stripMargin)
+ }
+
def execute(state: CircuitState): CircuitState = {
state.copy(annotations = state.annotations.flatMap {
// convert and remove AnnotatedMemoriesAnnotation to CustomFileEmission
case AnnotatedMemoriesAnnotation(annotatedMemories) =>
state.annotations.collect {
case a: MemLibOutConfigFileAnnotation =>
- a.copy(annotatedMemories = annotatedMemories)
- // todo convert xxx to verilogs here.
- }
+ Seq(a.copy(annotatedMemories = annotatedMemories))
+ case GenVerilogMemBehaviorModelAnno(genBlackBox) =>
+ annotatedMemories.map(vlsiMemGen(_, genBlackBox)).map {
+ case (name, content) =>
+ BlackBoxInlineAnno(ModuleName(name, CircuitName(state.circuit.main)), name + ".v", content)
+ }
+ }.flatten
case MemLibOutConfigFileAnnotation(_, Nil) => Nil
+ case GenVerilogMemBehaviorModelAnno(_) => Nil
case a => Seq(a)
})
}
diff --git a/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala b/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala
index f9df27a7..c7b0fbcd 100644
--- a/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala
+++ b/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala
@@ -6,9 +6,10 @@ package memlib
import firrtl.Utils.error
import firrtl._
import firrtl.annotations._
-import firrtl.options.{CustomFileEmission, HasShellOptions, ShellOption}
+import firrtl.options.{CustomFileEmission, Dependency, HasShellOptions, ShellOption}
import firrtl.passes.wiring._
import firrtl.stage.{Forms, RunFirrtlTransformAnnotation}
+import firrtl.transforms.BlackBoxSourceHelper
import java.io.{CharArrayWriter, PrintWriter}
@@ -46,6 +47,8 @@ object PassConfigUtil {
case class ReplSeqMemAnnotation(inputFileName: String, outputConfig: String) extends NoTargetAnnotation
+case class GenVerilogMemBehaviorModelAnno(genBlackBox: Boolean) extends NoTargetAnnotation
+
/** Generate conf file for a sequence of [[DefAnnotatedMemory]]
* @note file already has its suffix adding by `--replSeqMem`
*/
@@ -131,6 +134,20 @@ class ReplSeqMem extends SeqTransform with HasShellOptions with DependencyAPIMig
helpText = "Blackbox and emit a configuration file for each sequential memory",
shortOption = Some("frsq"),
helpValueName = Some("-c:<circuit>:-i:<file>:-o:<file>")
+ ),
+ new ShellOption[String](
+ longOption = "gen-mem-verilog",
+ toAnnotationSeq = (a: String) =>
+ Seq(
+ a match {
+ case "blackbox" => GenVerilogMemBehaviorModelAnno(genBlackBox = true)
+ case _ => GenVerilogMemBehaviorModelAnno(genBlackBox = false)
+ },
+ RunFirrtlTransformAnnotation(new ReplSeqMem)
+ ),
+ helpText = "Blackbox and emit a Verilog behavior model for each sequential memory",
+ shortOption = Some("gmv"),
+ helpValueName = Some("<blackbox|full>")
)
)
@@ -144,6 +161,7 @@ class ReplSeqMem extends SeqTransform with HasShellOptions with DependencyAPIMig
new ResolveMemoryReference,
new ReplaceMemMacros,
new WiringTransform,
- new DumpMemoryAnnotations
+ new DumpMemoryAnnotations,
+ new BlackBoxSourceHelper
)
}
diff --git a/src/main/scala/firrtl/passes/wiring/WiringTransform.scala b/src/main/scala/firrtl/passes/wiring/WiringTransform.scala
index 86afe520..d8b8eed9 100644
--- a/src/main/scala/firrtl/passes/wiring/WiringTransform.scala
+++ b/src/main/scala/firrtl/passes/wiring/WiringTransform.scala
@@ -40,7 +40,9 @@ class WiringTransform extends Transform with DependencyAPIMigration {
override def prerequisites = Forms.MidForm
override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Forms.MidEmitters
+ override def optionalPrerequisiteOf = Forms.MidEmitters ++
+ // once wire targets are turned into nodes, our logic to wire them up no longer works
+ Seq(Dependency[firrtl.transforms.RemoveWires])
private val invalidates = Forms.VerilogOptimized.toSet -- Forms.MinimalHighForm
override def invalidates(a: Transform): Boolean = invalidates(Dependency.fromTransform(a))
diff --git a/src/test/scala/firrtlTests/InlineInstancesTests.scala b/src/test/scala/firrtlTests/InlineInstancesTests.scala
index cc7257d2..b54db7cc 100644
--- a/src/test/scala/firrtlTests/InlineInstancesTests.scala
+++ b/src/test/scala/firrtlTests/InlineInstancesTests.scala
@@ -380,8 +380,12 @@ class InlineInstancesTests extends LowTransformSpec {
execute(input, check, Seq(inline("Inline")))
}
- case class DummyAnno(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] {
- override def duplicate(n: ReferenceTarget): Annotation = DummyAnno(n)
+ case class DummyAnno(targets: CompleteTarget*) extends Annotation {
+ override def update(renames: RenameMap): Seq[Annotation] = {
+ Seq(DummyAnno(targets.flatMap { t =>
+ renames.get(t).getOrElse(Seq(t))
+ }: _*))
+ }
}
"annotations" should "be renamed" in {
val input =
@@ -573,6 +577,9 @@ class InlineInstancesTests extends LowTransformSpec {
val nestedNotInlined = inlined.instOf("bar", "NestedNoInline")
val innerNestedInlined = nestedNotInlined.instOf("foo", "NestedInline")
+ val inlineModuleTarget = top.copy(module = "Inline")
+ val nestedInlineModuleTarget = top.copy(module = "NestedInline")
+
executeWithAnnos(
input,
check,
@@ -586,7 +593,10 @@ class InlineInstancesTests extends LowTransformSpec {
DummyAnno(nestedNotInlined.ref("a")),
DummyAnno(nestedNotInlined.ref("b")),
DummyAnno(innerNestedInlined.ref("a")),
- DummyAnno(innerNestedInlined.ref("b"))
+ DummyAnno(innerNestedInlined.ref("b")),
+ DummyAnno(inlineModuleTarget.instOf("bar", "NestedNoInline")),
+ DummyAnno(inlineModuleTarget.ref("a"), inlineModuleTarget.ref("b")),
+ DummyAnno(nestedInlineModuleTarget.ref("a"))
),
Seq(
DummyAnno(top.ref("i_a")),
@@ -596,7 +606,10 @@ class InlineInstancesTests extends LowTransformSpec {
DummyAnno(top.instOf("i_bar", "NestedNoInline").ref("a")),
DummyAnno(top.instOf("i_bar", "NestedNoInline").ref("b")),
DummyAnno(top.instOf("i_bar", "NestedNoInline").ref("foo_a")),
- DummyAnno(top.instOf("i_bar", "NestedNoInline").ref("foo_b"))
+ DummyAnno(top.instOf("i_bar", "NestedNoInline").ref("foo_b")),
+ DummyAnno(top.instOf("i_bar", "NestedNoInline")),
+ DummyAnno(top.ref("i_a"), top.ref("i_b")),
+ DummyAnno(top.ref("i_foo_a"), top.copy(module = "NestedNoInline").ref("foo_a"))
)
)
}
diff --git a/src/test/scala/firrtlTests/ReplSeqMemTests.scala b/src/test/scala/firrtlTests/ReplSeqMemTests.scala
index 4e00cb3a..cb6dd7a6 100644
--- a/src/test/scala/firrtlTests/ReplSeqMemTests.scala
+++ b/src/test/scala/firrtlTests/ReplSeqMemTests.scala
@@ -10,6 +10,8 @@ import firrtl.passes.memlib._
import firrtl.testutils.FirrtlCheckers._
import firrtl.testutils._
import firrtl.transforms._
+import firrtl.util.BackendCompilationUtilities.loggingProcessLogger
+import scala.sys.process._
class ReplSeqMemSpec extends SimpleTransformSpec {
def emitter = new LowFirrtlEmitter
@@ -45,6 +47,23 @@ class ReplSeqMemSpec extends SimpleTransformSpec {
)
}
+ def checkGenMemVerilog(input: String, mems: Set[MemConf], additionalAnnos: Annotation*): Unit = {
+ Seq(true, false).foreach { genBlackBox =>
+ val annos = Seq(GenVerilogMemBehaviorModelAnno(genBlackBox = genBlackBox)) ++ additionalAnnos
+ val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos))
+ // Check correctness of firrtl
+ parse(res.getEmittedCircuit.value)
+ // Check the emitted Verilog
+ mems.foreach { mem =>
+ val file = new java.io.File(mem.name + ".v")
+ require(file.exists(), s"${file.getName} should be emitted!")
+ val cmd = Seq("verilator", "--lint-only", file.getAbsolutePath)
+ assert(cmd.!(loggingProcessLogger) == 0, "Generated Verilog is not valid.")
+ file.delete()
+ }
+ }
+ }
+
"ReplSeqMem" should "generate blackbox wrappers for mems of bundle type" in {
val input = """
circuit Top :
@@ -77,6 +96,8 @@ circuit Top :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem" should "not infinite loop if control signals are derived from registered versions of themselves" in {
@@ -102,6 +123,8 @@ circuit Top :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem" should "not fail with FixedPoint types " in {
@@ -130,6 +153,8 @@ circuit CustomMemory :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem" should "not fail with Signed types " in {
@@ -158,6 +183,8 @@ circuit CustomMemory :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem Utility -- getConnectOrigin" should
@@ -232,9 +259,11 @@ circuit CustomMemory :
MemConf("mem_1_ext", 7, 16, Map(WritePort -> 1, ReadPort -> 1), None)
)
val confLoc = "ReplSeqMemTests.confTEMP"
+ val noDedupMemAnnotation =
+ NoDedupMemAnnotation(ComponentName("mem_0", ModuleName("CustomMemory", CircuitName("CustomMemory"))))
val annos = Seq(
ReplSeqMemAnnotation.parse("-c:CustomMemory:-o:" + confLoc),
- NoDedupMemAnnotation(ComponentName("mem_0", ModuleName("CustomMemory", CircuitName("CustomMemory"))))
+ noDedupMemAnnotation
)
val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos))
// Check correctness of firrtl
@@ -247,6 +276,8 @@ circuit CustomMemory :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems, noDedupMemAnnotation)
}
"ReplSeqMem" should "only not de-duplicate memories with the nodedupe annotation " in {
@@ -279,9 +310,11 @@ circuit CustomMemory :
MemConf("mem_1_ext", 7, 16, Map(WritePort -> 1, ReadPort -> 1), None)
)
val confLoc = "ReplSeqMemTests.confTEMP"
+ val noDedupMemAnnotation =
+ NoDedupMemAnnotation(ComponentName("mem_1", ModuleName("CustomMemory", CircuitName("CustomMemory"))))
val annos = Seq(
ReplSeqMemAnnotation.parse("-c:CustomMemory:-o:" + confLoc),
- NoDedupMemAnnotation(ComponentName("mem_1", ModuleName("CustomMemory", CircuitName("CustomMemory"))))
+ noDedupMemAnnotation
)
val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos))
// Check correctness of firrtl
@@ -294,6 +327,8 @@ circuit CustomMemory :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems, noDedupMemAnnotation)
}
"ReplSeqMem" should "dedup mems with the same instance name as other mems (in other modules) marked NoDedup" in {
@@ -337,9 +372,11 @@ circuit CustomMemory :
MemConf("mem_0_0_ext", 7, 16, Map(WritePort -> 1, ReadPort -> 1), None)
)
val confLoc = "ReplSeqMemTests.confTEMP"
+ val noDedupMemAnnotation =
+ NoDedupMemAnnotation(ComponentName("mem_0", ModuleName("ChildMemory", CircuitName("CustomMemory"))))
val annos = Seq(
ReplSeqMemAnnotation.parse("-c:CustomMemory:-o:" + confLoc),
- NoDedupMemAnnotation(ComponentName("mem_0", ModuleName("ChildMemory", CircuitName("CustomMemory"))))
+ noDedupMemAnnotation
)
val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos))
// Check correctness of firrtl
@@ -356,6 +393,8 @@ circuit CustomMemory :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems, noDedupMemAnnotation)
}
"ReplSeqMem" should "de-duplicate memories without an annotation " in {
@@ -391,6 +430,8 @@ circuit CustomMemory :
}
require(numExtMods == 1)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem" should "not have a mask if there is none" in {
@@ -416,6 +457,8 @@ circuit CustomMemory :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem" should "not conjoin enable signal with mask condition" in {
@@ -446,6 +489,8 @@ circuit CustomMemory :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem" should "not conjoin enable signal with wmask condition (RW Port)" in {
@@ -480,6 +525,8 @@ circuit CustomMemory :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem" should "produce an empty conf file with no SeqMems" in {
@@ -501,6 +548,8 @@ circuit NoMemsHere :
// Check the emitted conf
checkMemConf(res, mems)
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, mems)
}
"ReplSeqMem" should "throw an exception when encountering masks with variable granularity" in {
@@ -529,6 +578,10 @@ circuit Top :
val annos = Seq(ReplSeqMemAnnotation.parse("-c:Top:-o:" + confLoc))
val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos))
}
+
+ intercept[ReplaceMemMacros.UnsupportedBlackboxMemoryException] {
+ checkGenMemVerilog(input, Set.empty)
+ }
}
"ReplSeqMem" should "not run a buggy Uniquify" in {
@@ -551,6 +604,8 @@ circuit Top :
// Just check that it doesn't crash
compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos))
(new java.io.File(confLoc)).delete()
+
+ checkGenMemVerilog(input, Set.empty)
}
}
diff --git a/src/test/scala/firrtlTests/annotationTests/AnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/AnnotationSpec.scala
new file mode 100644
index 00000000..2729b4dc
--- /dev/null
+++ b/src/test/scala/firrtlTests/annotationTests/AnnotationSpec.scala
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package firrtlTests.annotationTests
+
+import firrtl.RenameMap
+import firrtl.annotations._
+import firrtl.testutils.FirrtlFlatSpec
+
+object AnnotationSpec {
+ case class TestAnno(pairs: List[(String, ReferenceTarget)]) extends Annotation {
+ def update(renames: RenameMap): Seq[Annotation] = {
+ val pairsx = pairs.flatMap {
+ case (n, t) =>
+ val ts = renames
+ .get(t)
+ .map(_.map(_.asInstanceOf[ReferenceTarget]))
+ .getOrElse(Seq(t))
+ ts.map(n -> _)
+ }
+ Seq(TestAnno(pairsx))
+ }
+ }
+}
+
+class AnnotationSpec extends FirrtlFlatSpec {
+ import AnnotationSpec._
+
+ behavior.of("Annotation.getTargets")
+
+ it should "not stack overflow" in {
+ val ref = CircuitTarget("Top").module("Foo").ref("vec")
+ val anno = TestAnno((0 until 10000).map(i => (i.toString, ref.index(i))).toList)
+ anno.getTargets should be(anno.pairs.map(_._2))
+ }
+}