diff options
Diffstat (limited to 'src/test/scala/chiselTests/experimental')
3 files changed, 572 insertions, 0 deletions
diff --git a/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala b/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala new file mode 100644 index 00000000..731596ec --- /dev/null +++ b/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.experimental + +import chisel3._ +import chisel3.util.Valid +import chisel3.stage.ChiselStage +import chisel3.experimental.DataMirror +import chiselTests.ChiselFlatSpec + +class DataMirrorSpec extends ChiselFlatSpec { + behavior.of("DataMirror") + + def assertBinding(x: Data, io: Boolean, wire: Boolean, reg: Boolean) = { + DataMirror.isIO(x) should be(io) + DataMirror.isWire(x) should be(wire) + DataMirror.isReg(x) should be(reg) + } + + def assertIO(x: Data) = assertBinding(x, true, false, false) + + def assertWire(x: Data) = assertBinding(x, false, true, false) + + def assertReg(x: Data) = assertBinding(x, false, false, true) + + def assertNone(x: Data) = assertBinding(x, false, false, false) + + it should "validate bindings" in { + class MyModule extends Module { + val typ = UInt(4.W) + val vectyp = Vec(8, UInt(4.W)) + val io = IO(new Bundle { + val in = Input(UInt(4.W)) + val vec = Input(vectyp) + val out = Output(UInt(4.W)) + }) + val vec = Wire(vectyp) + val regvec = Reg(vectyp) + val wire = Wire(UInt(4.W)) + val reg = RegNext(wire) + + assertIO(io) + assertIO(io.in) + assertIO(io.out) + assertIO(io.vec(1)) + assertIO(io.vec) + assertWire(vec) + assertWire(vec(0)) + assertWire(wire) + assertReg(reg) + assertReg(regvec) + assertReg(regvec(2)) + assertNone(typ) + assertNone(vectyp) + } + ChiselStage.elaborate(new MyModule) + } +} diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index e7caacfd..ac8357f0 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -479,6 +479,25 @@ class DataViewSpec extends ChiselFlatSpec { (err.getMessage should fullyMatch).regex(expected) } + it should "support invalidation" in { + class MyModule extends Module { + val a, b, c, d, e, f = IO(Output(UInt(8.W))) + val foo = (a, b).viewAs + val bar = (c, d).viewAs + val fizz = (e, f).viewAs + foo := DontCare + bar <> DontCare + fizz._1 := DontCare + fizz._2 <> DontCare + } + + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + val expected = ('a' to 'f').map(c => s"$c is invalid") + for (line <- expected) { + chirrtl should include(line) + } + } + behavior.of("PartialDataView") it should "still error if the mapping is non-total in the view" in { 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" + ) + } +} |
