summaryrefslogtreecommitdiff
path: root/src/test/scala/chiselTests/experimental/hierarchy
diff options
context:
space:
mode:
authorJack2022-07-30 22:41:15 +0000
committerJack2022-07-30 22:41:15 +0000
commit4cd44fa4dab370fcc5c20bcacc1fa0ee02327252 (patch)
tree05730be260feca0d2a870c4bb88325d36631a8fc /src/test/scala/chiselTests/experimental/hierarchy
parentfe9635ef21bad233945617a24ab16cfa4055f2d1 (diff)
parentbced77045c8fc5db37e40b159c49220929e15d46 (diff)
Merge branch '3.5.x' into 3.5-release
Diffstat (limited to 'src/test/scala/chiselTests/experimental/hierarchy')
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/SeparateElaborationSpec.scala495
1 files changed, 495 insertions, 0 deletions
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/SeparateElaborationSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/SeparateElaborationSpec.scala
new file mode 100644
index 00000000..25bbc474
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/SeparateElaborationSpec.scala
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental.hierarchy
+
+import chiselTests.ChiselFunSpec
+import chisel3._
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation, ChiselStage, DesignAnnotation}
+import chisel3.experimental.hierarchy.{Definition, Instance}
+import chisel3.experimental.hierarchy.ImportDefinitionAnnotation
+import firrtl.AnnotationSeq
+import firrtl.options.TargetDirAnnotation
+
+import scala.io.Source
+
+class SeparateElaborationSpec extends ChiselFunSpec with Utils {
+ import Examples._
+
+ /** Return a [[DesignAnnotation]] from a list of annotations. */
+ private def getDesignAnnotation[T <: RawModule](annos: AnnotationSeq): DesignAnnotation[T] = {
+ val designAnnos = annos.flatMap { a =>
+ a match {
+ case a: DesignAnnotation[T] => Some(a)
+ case _ => None
+ }
+ }
+ require(designAnnos.length == 1, s"Exactly one DesignAnnotation should exist, but found: $designAnnos.")
+ designAnnos.head
+ }
+
+ /** Elaborates [[AddOne]] and returns its [[Definition]]. */
+ private def getAddOneDefinition(testDir: String): Definition[AddOne] = {
+ val dutAnnos = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOne),
+ TargetDirAnnotation(testDir)
+ )
+ )
+
+ // Grab DUT definition to pass into testbench
+ getDesignAnnotation(dutAnnos).design.asInstanceOf[AddOne].toDefinition
+ }
+
+ /** Return [[Definition]]s of all modules in a circuit. */
+ private def allModulesToImportedDefs(annos: AnnotationSeq): Seq[ImportDefinitionAnnotation[_]] = {
+ annos.flatMap { a =>
+ a match {
+ case a: ChiselCircuitAnnotation =>
+ a.circuit.components.map { c => ImportDefinitionAnnotation(c.id.toDefinition) }
+ case _ => Seq.empty
+ }
+ }
+ }
+
+ describe("(0): Name conflicts") {
+ it("(0.a): should not occur between a Module and an Instance of a previously elaborated Definition.") {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutDef = getAddOneDefinition(testDir)
+
+ class Testbench(defn: Definition[AddOne]) extends Module {
+ val mod = Module(new AddOne)
+ val inst = Instance(defn)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ mod.in := 0.U
+ inst.in := 0.U
+ dontTouch(mod.out)
+ }
+
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef)),
+ TargetDirAnnotation(testDir),
+ ImportDefinitionAnnotation(dutDef)
+ )
+ )
+
+ val tb_rtl = Source.fromFile(s"$testDir/Testbench.v").getLines.mkString
+ tb_rtl should include("module AddOne_1(")
+ tb_rtl should include("AddOne_1 mod (")
+ (tb_rtl should not).include("module AddOne(")
+ tb_rtl should include("AddOne inst (")
+ }
+
+ it(
+ "(0.b): should not occur between an Instance of a Definition and an Instance of a previously elaborated Definition."
+ ) {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutDef = getAddOneDefinition(testDir)
+
+ class Testbench(defn: Definition[AddOne]) extends Module {
+ val inst0 = Instance(Definition(new AddOne))
+ val inst1 = Instance(defn)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ inst0.in := 0.U
+ inst1.in := 0.U
+ dontTouch(inst0.out)
+ }
+
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef)),
+ TargetDirAnnotation(testDir),
+ ImportDefinitionAnnotation(dutDef)
+ )
+ )
+
+ val tb_rtl = Source.fromFile(s"$testDir/Testbench.v").getLines.mkString
+ tb_rtl should include("module AddOne_1(")
+ tb_rtl should include("AddOne_1 inst0 (")
+ (tb_rtl should not).include("module AddOne(")
+ tb_rtl should include("AddOne inst1 (")
+ }
+ }
+
+ describe("(1): Repeat Module definitions") {
+ it("(1.a): should not occur when elaborating multiple Instances separately from its Definition.") {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutDef = getAddOneDefinition(testDir)
+
+ class Testbench(defn: Definition[AddOne]) extends Module {
+ val inst0 = Instance(defn)
+ val inst1 = Instance(defn)
+
+ inst0.in := 0.U
+ inst1.in := 0.U
+ }
+
+ // If there is a repeat module definition, FIRRTL emission will fail
+ (new ChiselStage).emitFirrtl(
+ gen = new Testbench(dutDef),
+ args = Array("-td", testDir, "--full-stacktrace"),
+ annotations = Seq(ImportDefinitionAnnotation(dutDef))
+ )
+ }
+ }
+
+ describe("(2): Multiple imported Definitions of modules without submodules") {
+ it(
+ "(2.a): should work if a list of imported Definitions is passed between Stages."
+ ) {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutAnnos0 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOneParameterized(4)),
+ TargetDirAnnotation(s"$testDir/dutDef0")
+ )
+ )
+ val dutDef0 = getDesignAnnotation(dutAnnos0).design.asInstanceOf[AddOneParameterized].toDefinition
+
+ val dutAnnos1 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOneParameterized(8)),
+ TargetDirAnnotation(s"$testDir/dutDef1"),
+ // pass in previously elaborated Definitions
+ ImportDefinitionAnnotation(dutDef0)
+ )
+ )
+ val dutDef1 = getDesignAnnotation(dutAnnos1).design.asInstanceOf[AddOneParameterized].toDefinition
+
+ class Testbench(defn0: Definition[AddOneParameterized], defn1: Definition[AddOneParameterized]) extends Module {
+ val inst0 = Instance(defn0)
+ val inst1 = Instance(defn1)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ inst0.in := 0.U
+ inst1.in := 0.U
+ }
+
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef0, dutDef1)),
+ TargetDirAnnotation(testDir),
+ ImportDefinitionAnnotation(dutDef0),
+ ImportDefinitionAnnotation(dutDef1)
+ )
+ )
+
+ val dutDef0_rtl = Source.fromFile(s"$testDir/dutDef0/AddOneParameterized.v").getLines.mkString
+ dutDef0_rtl should include("module AddOneParameterized(")
+ val dutDef1_rtl = Source.fromFile(s"$testDir/dutDef1/AddOneParameterized_1.v").getLines.mkString
+ dutDef1_rtl should include("module AddOneParameterized_1(")
+
+ val tb_rtl = Source.fromFile(s"$testDir/Testbench.v").getLines.mkString
+ tb_rtl should include("AddOneParameterized inst0 (")
+ tb_rtl should include("AddOneParameterized_1 inst1 (")
+ (tb_rtl should not).include("module AddOneParameterized(")
+ (tb_rtl should not).include("module AddOneParameterized_1(")
+ }
+
+ it(
+ "(2.b): should throw an exception if information is not passed between Stages."
+ ) {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutAnnos0 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOneParameterized(4)),
+ TargetDirAnnotation(s"$testDir/dutDef0")
+ )
+ )
+ val dutDef0 = getDesignAnnotation(dutAnnos0).design.asInstanceOf[AddOneParameterized].toDefinition
+
+ val dutAnnos1 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOneParameterized(8)),
+ TargetDirAnnotation(s"$testDir/dutDef1")
+ )
+ )
+ val dutDef1 = getDesignAnnotation(dutAnnos1).design.asInstanceOf[AddOneParameterized].toDefinition
+
+ class Testbench(defn0: Definition[AddOneParameterized], defn1: Definition[AddOneParameterized]) extends Module {
+ val inst0 = Instance(defn0)
+ val inst1 = Instance(defn1)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ inst0.in := 0.U
+ inst1.in := 0.U
+ }
+
+ // Because these elaborations have no knowledge of each other, they create
+ // modules of the same name
+ val dutDef0_rtl = Source.fromFile(s"$testDir/dutDef0/AddOneParameterized.v").getLines.mkString
+ dutDef0_rtl should include("module AddOneParameterized(")
+ val dutDef1_rtl = Source.fromFile(s"$testDir/dutDef1/AddOneParameterized.v").getLines.mkString
+ dutDef1_rtl should include("module AddOneParameterized(")
+
+ val errMsg = intercept[ChiselException] {
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef0, dutDef1)),
+ TargetDirAnnotation(testDir),
+ ImportDefinitionAnnotation(dutDef0),
+ ImportDefinitionAnnotation(dutDef1)
+ )
+ )
+ }
+ errMsg.getMessage should include(
+ "Expected distinct imported Definition names but found duplicates for: AddOneParameterized"
+ )
+ }
+ }
+
+ describe("(3): Multiple imported Definitions of modules with submodules") {
+ it(
+ "(3.a): should work if a list of imported Definitions for all modules is passed between Stages."
+ ) {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutAnnos0 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddTwoMixedModules),
+ TargetDirAnnotation(s"$testDir/dutDef0")
+ )
+ )
+ val dutDef0 = getDesignAnnotation(dutAnnos0).design.asInstanceOf[AddTwoMixedModules].toDefinition
+ val importDefinitionAnnos0 = allModulesToImportedDefs(dutAnnos0)
+
+ val dutAnnos1 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddTwoMixedModules),
+ TargetDirAnnotation(s"$testDir/dutDef1")
+ ) ++ importDefinitionAnnos0
+ )
+ val dutDef1 = getDesignAnnotation(dutAnnos1).design.asInstanceOf[AddTwoMixedModules].toDefinition
+ val importDefinitionAnnos1 = allModulesToImportedDefs(dutAnnos1)
+
+ class Testbench(defn0: Definition[AddTwoMixedModules], defn1: Definition[AddTwoMixedModules]) extends Module {
+ val inst0 = Instance(defn0)
+ val inst1 = Instance(defn1)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ inst0.in := 0.U
+ inst1.in := 0.U
+ }
+
+ val dutDef0_rtl = Source.fromFile(s"$testDir/dutDef0/AddTwoMixedModules.v").getLines.mkString
+ dutDef0_rtl should include("module AddOne(")
+ dutDef0_rtl should include("module AddTwoMixedModules(")
+ val dutDef1_rtl = Source.fromFile(s"$testDir/dutDef1/AddTwoMixedModules_1.v").getLines.mkString
+ dutDef1_rtl should include("module AddOne_2(")
+ dutDef1_rtl should include("module AddTwoMixedModules_1(")
+
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef0, dutDef1)),
+ TargetDirAnnotation(testDir)
+ ) ++ importDefinitionAnnos0 ++ importDefinitionAnnos1
+ )
+
+ val tb_rtl = Source.fromFile(s"$testDir/Testbench.v").getLines.mkString
+ tb_rtl should include("AddTwoMixedModules inst0 (")
+ tb_rtl should include("AddTwoMixedModules_1 inst1 (")
+ (tb_rtl should not).include("module AddTwoMixedModules(")
+ (tb_rtl should not).include("module AddTwoMixedModules_1(")
+ }
+ }
+
+ it(
+ "(3.b): should throw an exception if submodules are not passed between Definition elaborations."
+ ) {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutAnnos0 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddTwoMixedModules),
+ TargetDirAnnotation(s"$testDir/dutDef0")
+ )
+ )
+ val dutDef0 = getDesignAnnotation(dutAnnos0).design.asInstanceOf[AddTwoMixedModules].toDefinition
+ val importDefinitionAnnos0 = allModulesToImportedDefs(dutAnnos0)
+
+ val dutAnnos1 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddTwoMixedModules),
+ ImportDefinitionAnnotation(dutDef0),
+ TargetDirAnnotation(s"$testDir/dutDef1")
+ )
+ )
+ val dutDef1 = getDesignAnnotation(dutAnnos1).design.asInstanceOf[AddTwoMixedModules].toDefinition
+ val importDefinitionAnnos1 = allModulesToImportedDefs(dutAnnos1)
+
+ class Testbench(defn0: Definition[AddTwoMixedModules], defn1: Definition[AddTwoMixedModules]) extends Module {
+ val inst0 = Instance(defn0)
+ val inst1 = Instance(defn1)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ inst0.in := 0.U
+ inst1.in := 0.U
+ }
+
+ val dutDef0_rtl = Source.fromFile(s"$testDir/dutDef0/AddTwoMixedModules.v").getLines.mkString
+ dutDef0_rtl should include("module AddOne(")
+ dutDef0_rtl should include("module AddTwoMixedModules(")
+ val dutDef1_rtl = Source.fromFile(s"$testDir/dutDef1/AddTwoMixedModules_1.v").getLines.mkString
+ dutDef1_rtl should include("module AddOne(")
+ dutDef1_rtl should include("module AddTwoMixedModules_1(")
+
+ val errMsg = intercept[ChiselException] {
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef0, dutDef1)),
+ TargetDirAnnotation(testDir)
+ ) ++ importDefinitionAnnos0 ++ importDefinitionAnnos1
+ )
+ }
+ errMsg.getMessage should include(
+ "Expected distinct imported Definition names but found duplicates for: AddOne"
+ )
+ }
+
+ describe("(4): With ExtMod Names") {
+ it("(4.a): should pick correct ExtMod names when passed") {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutDef = getAddOneDefinition(testDir)
+
+ class Testbench(defn: Definition[AddOne]) extends Module {
+ val mod = Module(new AddOne)
+ val inst = Instance(defn)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ mod.in := 0.U
+ inst.in := 0.U
+ dontTouch(mod.out)
+ }
+
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef)),
+ TargetDirAnnotation(testDir),
+ ImportDefinitionAnnotation(dutDef, Some("CustomPrefix_AddOne_CustomSuffix"))
+ )
+ )
+
+ val tb_rtl = Source.fromFile(s"$testDir/Testbench.v").getLines.mkString
+
+ tb_rtl should include("module AddOne_1(")
+ tb_rtl should include("AddOne_1 mod (")
+ (tb_rtl should not).include("module AddOne(")
+ tb_rtl should include("CustomPrefix_AddOne_CustomSuffix inst (")
+ }
+ }
+
+ it(
+ "(4.b): should work if a list of imported Definitions is passed between Stages with ExtModName."
+ ) {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutAnnos0 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOneParameterized(4)),
+ TargetDirAnnotation(s"$testDir/dutDef0")
+ )
+ )
+ val dutDef0 = getDesignAnnotation(dutAnnos0).design.asInstanceOf[AddOneParameterized].toDefinition
+
+ val dutAnnos1 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOneParameterized(8)),
+ TargetDirAnnotation(s"$testDir/dutDef1"),
+ // pass in previously elaborated Definitions
+ ImportDefinitionAnnotation(dutDef0)
+ )
+ )
+ val dutDef1 = getDesignAnnotation(dutAnnos1).design.asInstanceOf[AddOneParameterized].toDefinition
+
+ class Testbench(defn0: Definition[AddOneParameterized], defn1: Definition[AddOneParameterized]) extends Module {
+ val inst0 = Instance(defn0)
+ val inst1 = Instance(defn1)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ inst0.in := 0.U
+ inst1.in := 0.U
+ }
+
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef0, dutDef1)),
+ TargetDirAnnotation(testDir),
+ ImportDefinitionAnnotation(dutDef0, Some("Inst1_Prefix_AddOnePramaterized_Inst1_Suffix")),
+ ImportDefinitionAnnotation(dutDef1, Some("Inst2_Prefix_AddOnePrameterized_1_Inst2_Suffix"))
+ )
+ )
+
+ val dutDef0_rtl = Source.fromFile(s"$testDir/dutDef0/AddOneParameterized.v").getLines.mkString
+ dutDef0_rtl should include("module AddOneParameterized(")
+ val dutDef1_rtl = Source.fromFile(s"$testDir/dutDef1/AddOneParameterized_1.v").getLines.mkString
+ dutDef1_rtl should include("module AddOneParameterized_1(")
+
+ val tb_rtl = Source.fromFile(s"$testDir/Testbench.v").getLines.mkString
+ tb_rtl should include("Inst1_Prefix_AddOnePramaterized_Inst1_Suffix inst0 (")
+ tb_rtl should include("Inst2_Prefix_AddOnePrameterized_1_Inst2_Suffix inst1 (")
+ (tb_rtl should not).include("module AddOneParameterized(")
+ (tb_rtl should not).include("module AddOneParameterized_1(")
+ }
+
+ it(
+ "(4.c): should throw an exception if a list of imported Definitions is passed between Stages with same ExtModName."
+ ) {
+ val testDir = createTestDirectory(this.getClass.getSimpleName).toString
+
+ val dutAnnos0 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOneParameterized(4)),
+ TargetDirAnnotation(s"$testDir/dutDef0")
+ )
+ )
+ val importDefinitionAnnos0 = allModulesToImportedDefs(dutAnnos0)
+ val dutDef0 = getDesignAnnotation(dutAnnos0).design.asInstanceOf[AddOneParameterized].toDefinition
+
+ val dutAnnos1 = (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new AddOneParameterized(8)),
+ TargetDirAnnotation(s"$testDir/dutDef1"),
+ // pass in previously elaborated Definitions
+ ImportDefinitionAnnotation(dutDef0)
+ )
+ )
+ val importDefinitionAnnos1 = allModulesToImportedDefs(dutAnnos1)
+ val dutDef1 = getDesignAnnotation(dutAnnos1).design.asInstanceOf[AddOneParameterized].toDefinition
+
+ class Testbench(defn0: Definition[AddOneParameterized], defn1: Definition[AddOneParameterized]) extends Module {
+ val inst0 = Instance(defn0)
+ val inst1 = Instance(defn1)
+
+ // Tie inputs to a value so ChiselStage does not complain
+ inst0.in := 0.U
+ inst1.in := 0.U
+ }
+
+ val dutDef0_rtl = Source.fromFile(s"$testDir/dutDef0/AddOneParameterized.v").getLines.mkString
+ dutDef0_rtl should include("module AddOneParameterized(")
+ val dutDef1_rtl = Source.fromFile(s"$testDir/dutDef1/AddOneParameterized_1.v").getLines.mkString
+ dutDef1_rtl should include("module AddOneParameterized_1(")
+
+ val errMsg = intercept[ChiselException] {
+ (new ChiselStage).run(
+ Seq(
+ ChiselGeneratorAnnotation(() => new Testbench(dutDef0, dutDef1)),
+ TargetDirAnnotation(testDir),
+ ImportDefinitionAnnotation(dutDef0, Some("Inst1_Prefix_AddOnePrameterized_Inst1_Suffix")),
+ ImportDefinitionAnnotation(dutDef1, Some("Inst1_Prefix_AddOnePrameterized_Inst1_Suffix"))
+ )
+ )
+ }
+ errMsg.getMessage should include(
+ "Expected distinct overrideDef names but found duplicates for: Inst1_Prefix_AddOnePrameterized_Inst1_Suffix"
+ )
+ }
+}