diff options
| author | Jack | 2022-07-30 22:41:15 +0000 |
|---|---|---|
| committer | Jack | 2022-07-30 22:41:15 +0000 |
| commit | 4cd44fa4dab370fcc5c20bcacc1fa0ee02327252 (patch) | |
| tree | 05730be260feca0d2a870c4bb88325d36631a8fc /src/test/scala/chiselTests | |
| parent | fe9635ef21bad233945617a24ab16cfa4055f2d1 (diff) | |
| parent | bced77045c8fc5db37e40b159c49220929e15d46 (diff) | |
Merge branch '3.5.x' into 3.5-release
Diffstat (limited to 'src/test/scala/chiselTests')
23 files changed, 1603 insertions, 84 deletions
diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index 2ab4c800..5d2cd496 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -381,4 +381,23 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } elaborate(new MyModule) } + + it should "support Bundles with vararg arguments" in { + // Without the fix, this doesn't even compile + // Extra parameter lists to make this a complex test case + class VarArgsBundle(x: Int)(y: Int, widths: Int*) extends Bundle { + def mkField(idx: Int): Option[UInt] = + (x +: y +: widths).lift(idx).map(w => UInt(w.W)) + val foo = mkField(0) + val bar = mkField(1) + val fizz = mkField(2) + val buzz = mkField(3) + } + class MyModule extends Module { + val in = IO(Input(new VarArgsBundle(1)(2, 3, 4))) + val out = IO(Output(new VarArgsBundle(1)(2, 3, 4))) + out := in + } + elaborate(new MyModule) + } } diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index 70dcda48..1e199297 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -351,4 +351,43 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { compile(new Top(true)) compile(new Top(false)) } + + "A unidirectional but flipped Bundle with something close to NotStrict compileOptions, but not exactly" should "bulk connect in import chisel3._ code correctly" in { + object Compat { + import Chisel.{defaultCompileOptions => _, _} + // arbitrary thing to make this *not* exactly NotStrict + implicit val defaultCompileOptions = new chisel3.ExplicitCompileOptions.CompileOptionsClass( + connectFieldsMustMatch = false, + declaredTypeMustBeUnbound = false, + dontTryConnectionsSwapped = false, + dontAssumeDirectionality = false, + checkSynthesizable = false, + explicitInvalidate = false, + inferModuleReset = true // different from NotStrict, to ensure case class equivalence to NotStrict is false + ) { + override def emitStrictConnects = false + } + + class MyBundle(extraFlip: Boolean) extends Bundle { + private def maybeFlip[T <: Data](t: T): T = if (extraFlip) t.flip else t + val foo = maybeFlip(new Bundle { + val bar = UInt(INPUT, width = 8) + }) + } + } + import chisel3._ + import Compat.{defaultCompileOptions => _, _} + class Top(extraFlip: Boolean) extends RawModule { + val port = IO(new MyBundle(extraFlip)) + val wire = Wire(new MyBundle(extraFlip)) + port <> DontCare + wire <> DontCare + port <> wire + wire <> port + port.foo <> wire.foo + wire.foo <> port.foo + } + compile(new Top(true)) + compile(new Top(false)) + } } diff --git a/src/test/scala/chiselTests/CompileOptionsTest.scala b/src/test/scala/chiselTests/CompileOptionsTest.scala index 3ec59954..b39d8ee3 100644 --- a/src/test/scala/chiselTests/CompileOptionsTest.scala +++ b/src/test/scala/chiselTests/CompileOptionsTest.scala @@ -163,4 +163,32 @@ class CompileOptionsSpec extends ChiselFlatSpec with Utils { } } + "Strict.copy()" should "be equivalent in all CompileOptions traits" in { + import chisel3.ExplicitCompileOptions.Strict + val copiedCompileOptions = Strict.copy() + assert(copiedCompileOptions.connectFieldsMustMatch == Strict.connectFieldsMustMatch) + assert(copiedCompileOptions.declaredTypeMustBeUnbound == Strict.declaredTypeMustBeUnbound) + assert(copiedCompileOptions.dontTryConnectionsSwapped == Strict.dontTryConnectionsSwapped) + assert(copiedCompileOptions.dontAssumeDirectionality == Strict.dontAssumeDirectionality) + assert(copiedCompileOptions.checkSynthesizable == Strict.checkSynthesizable) + assert(copiedCompileOptions.explicitInvalidate == Strict.explicitInvalidate) + assert(copiedCompileOptions.inferModuleReset == Strict.inferModuleReset) + assert(copiedCompileOptions.migrateInferModuleReset == Strict.migrateInferModuleReset) + assert(copiedCompileOptions.emitStrictConnects == Strict.emitStrictConnects) + } + + "NotStrict.copy()" should "be equivalent in all CompileOptions traits" in { + import chisel3.ExplicitCompileOptions.NotStrict + val copiedCompileOptions = NotStrict.copy() + assert(copiedCompileOptions.connectFieldsMustMatch == NotStrict.connectFieldsMustMatch) + assert(copiedCompileOptions.declaredTypeMustBeUnbound == NotStrict.declaredTypeMustBeUnbound) + assert(copiedCompileOptions.dontTryConnectionsSwapped == NotStrict.dontTryConnectionsSwapped) + assert(copiedCompileOptions.dontAssumeDirectionality == NotStrict.dontAssumeDirectionality) + assert(copiedCompileOptions.checkSynthesizable == NotStrict.checkSynthesizable) + assert(copiedCompileOptions.explicitInvalidate == NotStrict.explicitInvalidate) + assert(copiedCompileOptions.inferModuleReset == NotStrict.inferModuleReset) + assert(copiedCompileOptions.migrateInferModuleReset == NotStrict.migrateInferModuleReset) + assert(copiedCompileOptions.emitStrictConnects == NotStrict.emitStrictConnects) + } + } diff --git a/src/test/scala/chiselTests/DecoupledSpec.scala b/src/test/scala/chiselTests/DecoupledSpec.scala index 2d305f4a..69d74aab 100644 --- a/src/test/scala/chiselTests/DecoupledSpec.scala +++ b/src/test/scala/chiselTests/DecoupledSpec.scala @@ -17,4 +17,95 @@ class DecoupledSpec extends ChiselFlatSpec { assert(io.asUInt.widthOption.get === 4) }) } + + "Decoupled.map" should "apply a function to a wrapped Data" in { + val chirrtl = ChiselStage + .emitChirrtl(new Module { + val enq = IO(Flipped(Decoupled(UInt(8.W)))) + val deq = IO(Decoupled(UInt(8.W))) + deq <> enq.map(_ + 1.U) + }) + + // Check for data assignment + chirrtl should include("""node _deq_map_bits_T = add(enq.bits, UInt<1>("h1")""") + chirrtl should include("""node _deq_map_bits = tail(_deq_map_bits_T, 1)""") + chirrtl should include("""_deq_map.bits <= _deq_map_bits""") + chirrtl should include("""deq <= _deq_map""") + + // Check for back-pressure (ready signal is driven in the opposite direction of bits + valid) + chirrtl should include("""enq.ready <= _deq_map.ready""") + } + + "Decoupled.map" should "apply a function to a wrapped Bundle" in { + class TestBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + val fizz = Bool() + val buzz = Bool() + } + + // Add one to foo, subtract one from bar, set fizz to false and buzz to true + def func(t: TestBundle): TestBundle = { + val res = Wire(new TestBundle) + + res.foo := t.foo + 1.U + res.bar := t.bar - 1.U + res.fizz := false.B + res.buzz := true.B + + res + } + + val chirrtl = ChiselStage + .emitChirrtl(new Module { + val enq = IO(Flipped(Decoupled(new TestBundle))) + val deq = IO(Decoupled(new TestBundle)) + deq <> enq.map(func) + }) + + // Check for data assignment + chirrtl should include("""wire _deq_map_bits : { foo : UInt<8>, bar : UInt<8>, fizz : UInt<1>, buzz : UInt<1>}""") + + chirrtl should include("""node _deq_map_bits_res_foo_T = add(enq.bits.foo, UInt<1>("h1")""") + chirrtl should include("""node _deq_map_bits_res_foo_T_1 = tail(_deq_map_bits_res_foo_T, 1)""") + chirrtl should include("""_deq_map_bits.foo <= _deq_map_bits_res_foo_T_1""") + + chirrtl should include("""node _deq_map_bits_res_bar_T = sub(enq.bits.bar, UInt<1>("h1")""") + chirrtl should include("""node _deq_map_bits_res_bar_T_1 = tail(_deq_map_bits_res_bar_T, 1)""") + chirrtl should include("""_deq_map_bits.bar <= _deq_map_bits_res_bar_T_1""") + + chirrtl should include("""_deq_map_bits.fizz <= UInt<1>("h0")""") + chirrtl should include("""_deq_map_bits.buzz <= UInt<1>("h1")""") + + chirrtl should include("""_deq_map.bits <= _deq_map_bits""") + chirrtl should include("""deq <= _deq_map""") + + // Check for back-pressure (ready signal is driven in the opposite direction of bits + valid) + chirrtl should include("""enq.ready <= _deq_map.ready""") + } + + "Decoupled.map" should "apply a function to a wrapped Bundle and return a different typed DecoupledIO" in { + class TestBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + } + + val chirrtl = ChiselStage + .emitChirrtl(new Module { + val enq = IO(Flipped(Decoupled(new TestBundle))) + val deq = IO(Decoupled(UInt(8.W))) + deq <> enq.map(bundle => bundle.foo & bundle.bar) + }) + + // Check that the _map wire wraps a UInt and not a TestBundle + chirrtl should include("""wire _deq_map : { flip ready : UInt<1>, valid : UInt<1>, bits : UInt<8>}""") + + // Check for data assignment + chirrtl should include("""node _deq_map_bits = and(enq.bits.foo, enq.bits.bar)""") + chirrtl should include("""_deq_map.bits <= _deq_map_bits""") + chirrtl should include("""deq <= _deq_map""") + + // Check for back-pressure (ready signal is driven in the opposite direction of bits + valid) + chirrtl should include("""enq.ready <= _deq_map.ready""") + } } diff --git a/src/test/scala/chiselTests/ExtModule.scala b/src/test/scala/chiselTests/ExtModule.scala index 1dbd7447..b5a8ff7c 100644 --- a/src/test/scala/chiselTests/ExtModule.scala +++ b/src/test/scala/chiselTests/ExtModule.scala @@ -59,6 +59,35 @@ class MultiExtModuleTester extends BasicTester { stop() } +class ExtModuleWithSuggestName extends ExtModule { + val in = IO(Input(UInt(8.W))) + in.suggestName("foo") + val out = IO(Output(UInt(8.W))) +} + +class ExtModuleWithSuggestNameTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + val inst = Module(new ExtModuleWithSuggestName) + inst.in := in + out := inst.out +} + +class SimpleIOBundle extends Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) +} + +class ExtModuleWithFlatIO extends ExtModule { + val badIO = FlatIO(new SimpleIOBundle) +} + +class ExtModuleWithFlatIOTester extends Module { + val io = IO(new SimpleIOBundle) + val inst = Module(new ExtModuleWithFlatIO) + io <> inst.badIO +} + class ExtModuleSpec extends ChiselFlatSpec { "A ExtModule inverter" should "work" in { assertTesterPasses({ new ExtModuleTester }, Seq("/chisel3/BlackBoxTest.v"), TesterDriver.verilatorOnly) @@ -73,4 +102,19 @@ class ExtModuleSpec extends ChiselFlatSpec { assert(DataMirror.modulePorts(m) == Seq("in" -> m.in, "out" -> m.out)) }) } + + behavior.of("ExtModule") + + it should "work with .suggestName (aka it should not require reflection for naming)" in { + val chirrtl = ChiselStage.emitChirrtl(new ExtModuleWithSuggestNameTester) + chirrtl should include("input foo : UInt<8>") + chirrtl should include("inst.foo <= in") + } + + it should "work with FlatIO" in { + val chirrtl = ChiselStage.emitChirrtl(new ExtModuleWithFlatIOTester) + chirrtl should include("io.out <= inst.out") + chirrtl should include("inst.in <= io.in") + chirrtl shouldNot include("badIO") + } } diff --git a/src/test/scala/chiselTests/InstanceNameSpec.scala b/src/test/scala/chiselTests/InstanceNameSpec.scala index 7eaf3106..cc5980f4 100644 --- a/src/test/scala/chiselTests/InstanceNameSpec.scala +++ b/src/test/scala/chiselTests/InstanceNameSpec.scala @@ -22,28 +22,36 @@ class InstanceNameModule extends Module { io.bar := io.foo + x } -class InstanceNameSpec extends ChiselFlatSpec { +class InstanceNameSpec extends ChiselFlatSpec with Utils { behavior.of("instanceName") val moduleName = "InstanceNameModule" var m: InstanceNameModule = _ ChiselStage.elaborate { m = new InstanceNameModule; m } + val deprecationMsg = "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated" + it should "work with module IO" in { val io = m.io.pathName assert(io == moduleName + ".io") } - it should "work with internal vals" in { + it should "work for literals" in { val x = m.x.pathName - val y = m.y.pathName - val z = m.z.pathName assert(x == moduleName + ".UInt<2>(\"h03\")") + } + + it should "work with non-hardware values (but be deprecated)" in { + val (ylog, y) = grabLog(m.y.pathName) + val (zlog, z) = grabLog(m.z.pathName) + ylog should include(deprecationMsg) assert(y == moduleName + ".y") + zlog should include(deprecationMsg) assert(z == moduleName + ".z") } - it should "work with bundle elements" in { - val foo = m.z.foo.pathName + it should "work with non-hardware bundle elements (but be deprecated)" in { + val (log, foo) = grabLog(m.z.foo.pathName) + log should include(deprecationMsg) assert(foo == moduleName + ".z.foo") } diff --git a/src/test/scala/chiselTests/MigrateCompileOptionsSpec.scala b/src/test/scala/chiselTests/MigrateCompileOptionsSpec.scala new file mode 100644 index 00000000..091f7f28 --- /dev/null +++ b/src/test/scala/chiselTests/MigrateCompileOptionsSpec.scala @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3.stage.ChiselStage +import chisel3.ImplicitInvalidate +import chisel3.ExplicitCompileOptions + +import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks + +object MigrationExamples { + object InferResets { + import Chisel.{defaultCompileOptions => _, _} + import chisel3.RequireSyncReset + implicit val migrateIR = new chisel3.CompileOptions { + val connectFieldsMustMatch = false + val declaredTypeMustBeUnbound = false + val dontTryConnectionsSwapped = false + val dontAssumeDirectionality = false + val checkSynthesizable = false + val explicitInvalidate = false + val inferModuleReset = false + + override val migrateInferModuleReset = true + } + + class Foo extends Module { + val io = new Bundle {} + } + class FooWithRequireSyncReset extends Module with RequireSyncReset { + val io = new Bundle {} + } + } + object ExplicitInvalidate { + import chisel3.ImplicitInvalidate + val migrateEI = new chisel3.CompileOptions { + val connectFieldsMustMatch = false + val declaredTypeMustBeUnbound = false + val dontTryConnectionsSwapped = false + val dontAssumeDirectionality = false + val checkSynthesizable = false + val explicitInvalidate = true + val inferModuleReset = false + } + object ChiselChildren { + import Chisel.{defaultCompileOptions => _, _} + implicit val options = migrateEI + class Foo extends Module { + val io = new Bundle { + val out = Output(UInt(width = 3)) + } + } + class FooWithImplicitInvalidate extends Module with ImplicitInvalidate { + val io = new Bundle { + val out = Output(UInt(width = 3)) + } + } + class FooWire extends Module { + val io = new Bundle {} + val wire = Wire(Bool()) + } + class FooWireWithImplicitInvalidate extends Module with ImplicitInvalidate { + val io = new Bundle {} + val wire = Wire(Bool()) + } + } + object chisel3Children { + import chisel3._ + class Foo extends Module { + val in = IO(chisel3.Input(UInt(3.W))) + } + } + object ChiselParents { + import Chisel.{defaultCompileOptions => _, _} + implicit val options = migrateEI + + class FooParent extends Module { + val io = new Bundle {} + val i = Module(new chisel3Children.Foo) + } + class FooParentWithImplicitInvalidate extends Module with ImplicitInvalidate { + val io = new Bundle {} + val i = Module(new chisel3Children.Foo) + } + } + } +} + +class MigrateCompileOptionsSpec extends ChiselFunSpec with Utils { + import Chisel.{defaultCompileOptions => _, _} + import chisel3.RequireSyncReset + + describe("(0): Migrating infer resets") { + import MigrationExamples.InferResets._ + it("(0.a): Error if migrating, but not extended RequireSyncReset") { + intercept[Exception] { ChiselStage.elaborate(new Foo) } + } + it("(0.b): Not error if migrating, and you mix with RequireSyncReset") { + ChiselStage.elaborate(new FooWithRequireSyncReset) + } + } + + describe("(1): Migrating explicit invalidate") { + import MigrationExamples.ExplicitInvalidate._ + + it("(1.a): error if migrating module input, but not extending ImplicitInvalidate") { + intercept[_root_.firrtl.passes.CheckInitialization.RefNotInitializedException] { + ChiselStage.emitVerilog(new ChiselChildren.Foo) + } + } + it("(1.b): succeed if migrating module input with extending ImplicitInvalidate") { + ChiselStage.emitVerilog(new ChiselChildren.FooWithImplicitInvalidate) + } + + it("(1.c): error if migrating instance output, but not extending ImplicitInvalidate") { + intercept[_root_.firrtl.passes.CheckInitialization.RefNotInitializedException] { + ChiselStage.emitVerilog(new ChiselParents.FooParent) + } + } + it("(1.d): succeed if migrating instance output with extending ImplicitInvalidate") { + ChiselStage.emitVerilog(new ChiselParents.FooParentWithImplicitInvalidate) + } + + it("(1.e): error if migrating wire declaration, but not extending ImplicitInvalidate") { + intercept[_root_.firrtl.passes.CheckInitialization.RefNotInitializedException] { + ChiselStage.emitVerilog(new ChiselChildren.FooWire) + } + } + it("(1.f): succeed if migrating wire declaration with extending ImplicitInvalidate") { + ChiselStage.emitVerilog(new ChiselChildren.FooWireWithImplicitInvalidate) + } + } +} diff --git a/src/test/scala/chiselTests/NamingAnnotationTest.scala b/src/test/scala/chiselTests/NamingAnnotationTest.scala index ded321cd..a3f39c51 100644 --- a/src/test/scala/chiselTests/NamingAnnotationTest.scala +++ b/src/test/scala/chiselTests/NamingAnnotationTest.scala @@ -4,12 +4,13 @@ package chiselTests import chisel3._ import chisel3.experimental.chiselName +import chisel3.experimental.AffectsChiselPrefix import chisel3.internal.InstanceId import chisel3.stage.ChiselStage import scala.collection.mutable.ListBuffer -trait NamedModuleTester extends Module { +trait NamedModuleTester extends Module with AffectsChiselPrefix { val expectedNameMap = ListBuffer[(InstanceId, String)]() val expectedModuleNameMap = ListBuffer[(Module, String)]() @@ -48,25 +49,20 @@ trait NamedModuleTester extends Module { failures.toList } } -@chiselName -class OuterNamedNonModule { +class OuterNamedNonModule extends AffectsChiselPrefix { val value = Wire(Bool()) } -@chiselName -class NonModule { +class NonModule extends AffectsChiselPrefix { val value = Wire(Bool()) - @chiselName - class InnerNamedNonModule { + class InnerNamedNonModule extends AffectsChiselPrefix { val value = Wire(Bool()) } val inner = new InnerNamedNonModule val outer = new OuterNamedNonModule } -@chiselName class NamedModule extends NamedModuleTester { - @chiselName def FunctionMockupInner(): UInt = { val my2A = 1.U val my2B = expectName(my2A +& 2.U, "test_myNested_my2B") @@ -74,7 +70,6 @@ class NamedModule extends NamedModuleTester { my2C } - @chiselName def FunctionMockup(): UInt = { val myNested = expectName(FunctionMockupInner(), "test_myNested") val myA = expectName(1.U + myNested, "test_myA") @@ -123,11 +118,9 @@ class NamedModule extends NamedModuleTester { NoReturnFunction() } -@chiselName class NameCollisionModule extends NamedModuleTester { - @chiselName - def repeatedCalls(id: Int): UInt = { - val test = expectName(1.U + 3.U, s"test_$id") // should disambiguate by invocation order + def repeatedCalls(name: String): UInt = { + val test = expectName(1.U + 3.U, s"${name}_test") // should disambiguate by invocation order test + 2.U } @@ -135,8 +128,8 @@ class NameCollisionModule extends NamedModuleTester { def innerNamedFunction() { // ... but not this inner function def innerUnnamedFunction() { - val a = repeatedCalls(1) - val b = repeatedCalls(2) + val a = repeatedCalls("a") + val b = repeatedCalls("b") } innerUnnamedFunction() @@ -212,19 +205,17 @@ class NoChiselNamePrefixTester extends NamedModuleTester { val a = expectName(1.U +& 2.U, "a") } val inst = new NoChiselNamePrefixClass - @chiselName class NormalClass { val b = 1.U +& 2.U } - val foo = new NormalClass + val foo = new NormalClass with AffectsChiselPrefix expectName(foo.b, "foo_b") val bar = new NormalClass with chisel3.experimental.NoChiselNamePrefix expectName(bar.b, "b") // Check that we're not matching by name but actual type trait NoChiselNamePrefix - @chiselName - class FakeNoChiselNamePrefix extends NoChiselNamePrefix { + class FakeNoChiselNamePrefix extends NoChiselNamePrefix with AffectsChiselPrefix { val c = 1.U +& 2.U } val fizz = new FakeNoChiselNamePrefix diff --git a/src/test/scala/chiselTests/NewAnnotationsSpec.scala b/src/test/scala/chiselTests/NewAnnotationsSpec.scala new file mode 100644 index 00000000..38e1c1d9 --- /dev/null +++ b/src/test/scala/chiselTests/NewAnnotationsSpec.scala @@ -0,0 +1,72 @@ +package chiselTests +import chisel3._ +import chisel3.experimental.{annotate, ChiselMultiAnnotation} +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import firrtl.stage.FirrtlCircuitAnnotation +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.should.Matchers +import firrtl.transforms.NoDedupAnnotation +import firrtl.transforms.DontTouchAnnotation + +class NewAnnotationsSpec extends AnyFreeSpec with Matchers { + + class MuchUsedModule extends Module { + val io = IO(new Bundle { + val in = Input(UInt(16.W)) + val out = Output(UInt(16.W)) + }) + io.out := io.in +% 1.U + } + + class UsesMuchUsedModule extends Module { + val io = IO(new Bundle { + val in = Input(UInt(16.W)) + val out = Output(UInt(16.W)) + }) + + val mod0 = Module(new MuchUsedModule) + val mod1 = Module(new MuchUsedModule) + val mod2 = Module(new MuchUsedModule) + val mod3 = Module(new MuchUsedModule) + + mod0.io.in := io.in + mod1.io.in := mod0.io.out + mod2.io.in := mod1.io.out + mod3.io.in := mod2.io.out + io.out := mod3.io.out + + // Give two annotations as single element of the seq - ensures previous API works by wrapping into a seq. + annotate(new ChiselMultiAnnotation { def toFirrtl = Seq(new NoDedupAnnotation(mod2.toNamed)) }) + annotate(new ChiselMultiAnnotation { def toFirrtl = Seq(new NoDedupAnnotation(mod3.toNamed)) }) + + // Pass multiple annotations in the same seq - should get emitted out correctly. + annotate(new ChiselMultiAnnotation { + def toFirrtl = + Seq(new DontTouchAnnotation(mod1.io.in.toNamed), new DontTouchAnnotation(mod1.io.out.toNamed)) + }) + } + + val stage = new ChiselStage + "Ensure all annotations continue to be passed / digested correctly with the new API" - { + "NoDedup and DontTouch work as expected" in { + val dutAnnos = stage + .execute( + Array("-X", "low", "--target-dir", "test_run_dir"), + Seq(ChiselGeneratorAnnotation(() => new UsesMuchUsedModule)) + ) + + val dontTouchAnnos = dutAnnos.collect { case DontTouchAnnotation(target) => target.serialize } + val noDedupAnnos = dutAnnos.collect { case NoDedupAnnotation(target) => target.serialize } + require(dontTouchAnnos.size == 2, s"Exactly two DontTouch Annotations expected but got $dontTouchAnnos ") + require(noDedupAnnos.size == 2, s"Exactly two NoDedup Annotations expected but got $noDedupAnnos ") + val dontTouchAnnosCombined = dontTouchAnnos.mkString(",") + val noDedupAnnosCombined = noDedupAnnos.mkString(",") + + noDedupAnnosCombined should include("~UsesMuchUsedModule|MuchUsedModule_2") + noDedupAnnosCombined should include("~UsesMuchUsedModule|MuchUsedModule_3") + dontTouchAnnosCombined should include("~UsesMuchUsedModule|UsesMuchUsedModule/mod1:MuchUsedModule>io_out") + dontTouchAnnosCombined should include("~UsesMuchUsedModule|UsesMuchUsedModule/mod1:MuchUsedModule>io_in") + + } + } +} diff --git a/src/test/scala/chiselTests/PrintableSpec.scala b/src/test/scala/chiselTests/PrintableSpec.scala index 7d584cea..8039918d 100644 --- a/src/test/scala/chiselTests/PrintableSpec.scala +++ b/src/test/scala/chiselTests/PrintableSpec.scala @@ -9,7 +9,8 @@ import chisel3.testers.BasicTester import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers - +import chisel3.util._ +import org.scalactic.source.Position import java.io.File /** Dummy [[printf]] annotation. @@ -32,7 +33,7 @@ object PrintfAnnotation { } /* Printable Tests */ -class PrintableSpec extends AnyFlatSpec with Matchers { +class PrintableSpec extends AnyFlatSpec with Matchers with Utils { // This regex is brittle, it specifically finds the clock and enable signals followed by commas private val PrintfRegex = """\s*printf\(\w+, [^,]+,(.*)\).*""".r private val StringRegex = """([^"]*)"(.*?)"(.*)""".r @@ -47,7 +48,6 @@ class PrintableSpec extends AnyFlatSpec with Matchers { case _ => fail(s"Regex to process Printf should work on $str!") } } - firrtl.split("\n").collect { case PrintfRegex(matched) => val (str, args) = processBody(matched) @@ -55,26 +55,34 @@ class PrintableSpec extends AnyFlatSpec with Matchers { } } + // Generates firrtl, gets Printfs + // Calls fail() if failed match; else calls the partial function which could have its own check + private def generateAndCheck(gen: => RawModule)(check: PartialFunction[Seq[Printf], Unit])(implicit pos: Position) = { + val firrtl = ChiselStage.emitChirrtl(gen) + val printfs = getPrintfs(firrtl) + if (!check.isDefinedAt(printfs)) { + fail() + } else { + check(printfs) + } + } + behavior.of("Printable & Custom Interpolator") it should "pass exact strings through" in { class MyModule extends BasicTester { printf(p"An exact string") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("An exact string", Seq())) => - case e => fail() } } it should "handle Printable and String concatination" in { class MyModule extends BasicTester { printf(p"First " + PString("Second ") + "Third") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("First Second Third", Seq())) => - case e => fail() } } it should "call toString on non-Printable objects" in { @@ -82,10 +90,8 @@ class PrintableSpec extends AnyFlatSpec with Matchers { val myInt = 1234 printf(p"myInt = $myInt") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("myInt = 1234", Seq())) => - case e => fail() } } it should "generate proper printf for simple Decimal printing" in { @@ -93,41 +99,33 @@ class PrintableSpec extends AnyFlatSpec with Matchers { val myWire = WireDefault(1234.U) printf(p"myWire = ${Decimal(myWire)}") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("myWire = %d", Seq("myWire"))) => - case e => fail() } } it should "handle printing literals" in { class MyModule extends BasicTester { printf(Decimal(10.U(32.W))) } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("%d", Seq(lit))) => assert(lit contains "UInt<32>") - case e => fail() } } it should "correctly escape percent" in { class MyModule extends BasicTester { printf(p"%") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("%%", Seq())) => - case e => fail() } } it should "correctly emit tab" in { class MyModule extends BasicTester { printf(p"\t") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("\\t", Seq())) => - case e => fail() } } it should "support names of circuit elements including submodule IO" in { @@ -149,10 +147,8 @@ class PrintableSpec extends AnyFlatSpec with Matchers { printf(p"${FullName(myWire.foo)}") printf(p"${FullName(myInst.io.fizz)}") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("foo", Seq()), Printf("myWire.foo", Seq()), Printf("myInst.io.fizz", Seq())) => - case e => fail() } } it should "handle printing ports of submodules" in { @@ -165,10 +161,8 @@ class PrintableSpec extends AnyFlatSpec with Matchers { val myInst = Module(new MySubModule) printf(p"${myInst.io.fizz}") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("%d", Seq("myInst.io.fizz"))) => - case e => fail() } } it should "print UInts and SInts as Decimal by default" in { @@ -177,10 +171,8 @@ class PrintableSpec extends AnyFlatSpec with Matchers { val mySInt = WireDefault(-1.S) printf(p"$myUInt & $mySInt") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("%d & %d", Seq("myUInt", "mySInt"))) => - case e => fail() } } it should "print Vecs like Scala Seqs by default" in { @@ -189,10 +181,8 @@ class PrintableSpec extends AnyFlatSpec with Matchers { myVec.foreach(_ := 0.U) printf(p"$myVec") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("Vec(%d, %d, %d, %d)", Seq("myVec[0]", "myVec[1]", "myVec[2]", "myVec[3]"))) => - case e => fail() } } it should "print Bundles like Scala Maps by default" in { @@ -205,10 +195,8 @@ class PrintableSpec extends AnyFlatSpec with Matchers { myBun.bar := 0.U printf(p"$myBun") } - val firrtl = ChiselStage.emitChirrtl(new MyModule) - getPrintfs(firrtl) match { + generateAndCheck(new MyModule) { case Seq(Printf("AnonymousBundle(foo -> %d, bar -> %d)", Seq("myBun.foo", "myBun.bar"))) => - case e => fail() } } it should "get emitted with a name and annotated" in { @@ -261,4 +249,145 @@ class PrintableSpec extends AnyFlatSpec with Matchers { """printf(clock, UInt<1>("h1"), "adieu AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : farewell""" ) } + + // Unit tests for cf + it should "print regular scala variables with cf format specifier" in { + + class MyModule extends BasicTester { + val f1 = 20.4517 + val i1 = 10 + val str1 = "String!" + printf( + cf"F1 = $f1 D1 = $i1 F1 formatted = $f1%2.2f str1 = $str1%s i1_str = $i1%s i1_hex=$i1%x" + ) + + } + + generateAndCheck(new MyModule) { + case Seq(Printf("F1 = 20.4517 D1 = 10 F1 formatted = 20.45 str1 = String! i1_str = 10 i1_hex=a", Seq())) => + } + } + + it should "print chisel bits with cf format specifier" in { + + class MyBundle extends Bundle { + val foo = UInt(32.W) + val bar = UInt(32.W) + override def toPrintable: Printable = { + cf"Bundle : " + + cf"Foo : $foo%x Bar : $bar%x" + } + } + class MyModule extends BasicTester { + val b1 = 10.U + val w1 = Wire(new MyBundle) + w1.foo := 5.U + w1.bar := 10.U + printf(cf"w1 = $w1") + } + generateAndCheck(new MyModule) { + case Seq(Printf("w1 = Bundle : Foo : %x Bar : %x", Seq("w1.foo", "w1.bar"))) => + } + } + + it should "support names of circuit elements using format specifier including submodule IO with cf format specifier" in { + // Submodule IO is a subtle issue because the Chisel element has a different + // parent module + class MySubModule extends Module { + val io = IO(new Bundle { + val fizz = UInt(32.W) + }) + } + class MyBundle extends Bundle { + val foo = UInt(32.W) + } + class MyModule extends BasicTester { + override def desiredName: String = "MyModule" + val myWire = Wire(new MyBundle) + val myInst = Module(new MySubModule) + printf(cf"${myWire.foo}%n") + printf(cf"${myWire.foo}%N") + printf(cf"${myInst.io.fizz}%N") + } + generateAndCheck(new MyModule) { + case Seq(Printf("foo", Seq()), Printf("myWire.foo", Seq()), Printf("myInst.io.fizz", Seq())) => + } + } + + it should "correctly print strings after modifier" in { + class MyModule extends BasicTester { + val b1 = 10.U + printf(cf"This is here $b1%x!!!! And should print everything else") + } + generateAndCheck(new MyModule) { + case Seq(Printf("This is here %x!!!! And should print everything else", Seq("UInt<4>(\"ha\")"))) => + } + } + + it should "correctly print strings with a lot of literal %% and different format specifiers for Wires" in { + class MyModule extends BasicTester { + val b1 = 10.U + val b2 = 20.U + printf(cf"%% $b1%x%%$b2%b = ${b1 % b2}%d %%%% Tail String") + } + + generateAndCheck(new MyModule) { + case Seq(Printf("%% %x%%%b = %d %%%% Tail String", Seq(lita, litb, _))) => + assert(lita.contains("UInt<4>") && litb.contains("UInt<5>")) + } + } + + it should "not allow unescaped % in the message" in { + class MyModule extends BasicTester { + printf(cf"This should error out for sure because of % - it should be %%") + } + a[java.util.UnknownFormatConversionException] should be thrownBy { + extractCause[java.util.UnknownFormatConversionException] { + ChiselStage.elaborate { new MyModule } + } + } + } + + it should "allow Printables to be expanded and used" in { + class MyModule extends BasicTester { + val w1 = 20.U + val f1 = 30.2 + val i1 = 14 + val pable = cf"w1 = $w1%b f1 = $f1%2.2f" + printf(cf"Trying to expand printable $pable and mix with i1 = $i1%d") + } + generateAndCheck(new MyModule) { + case Seq(Printf("Trying to expand printable w1 = %b f1 = 30.20 and mix with i1 = 14", Seq(lit))) => + assert(lit.contains("UInt<5>")) + } + } + + it should "fail with a single % in the message" in { + class MyModule extends BasicTester { + printf(cf"%") + } + a[java.util.UnknownFormatConversionException] should be thrownBy { + extractCause[java.util.UnknownFormatConversionException] { + ChiselStage.elaborate { new MyModule } + } + } + } + + it should "fail when passing directly to StirngContext.cf a string with literal \\ correctly escaped " in { + a[StringContext.InvalidEscapeException] should be thrownBy { + extractCause[StringContext.InvalidEscapeException] { + val s_seq = Seq("Test with literal \\ correctly escaped") + StringContext(s_seq: _*).cf(Seq(): _*) + } + } + } + + it should "pass correctly escaped \\ when using Printable.pack" in { + class MyModule extends BasicTester { + printf(Printable.pack("\\ \\]")) + } + generateAndCheck(new MyModule) { + case Seq(Printf("\\\\ \\\\]", Seq())) => + } + } } diff --git a/src/test/scala/chiselTests/SIntOps.scala b/src/test/scala/chiselTests/SIntOps.scala index 55b4c915..ebbd2012 100644 --- a/src/test/scala/chiselTests/SIntOps.scala +++ b/src/test/scala/chiselTests/SIntOps.scala @@ -85,9 +85,9 @@ class SIntOpsTester(c: SIntOps) extends Tester(c) { */ class SIntLitExtractTester extends BasicTester { - assert(-5.S(1) === true.B) - assert(-5.S(2) === false.B) - assert(-5.S(100) === true.B) + assert(-5.S.extract(1) === true.B) + assert(-5.S.extract(2) === false.B) + assert(-5.S.extract(100) === true.B) assert(-5.S(3, 0) === "b1011".U) assert(-5.S(9, 0) === "b1111111011".U) assert(-5.S(4.W)(1) === true.B) diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala index c43d832a..44ed77f9 100644 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ b/src/test/scala/chiselTests/StrongEnum.scala @@ -4,8 +4,8 @@ package chiselTests import chisel3._ import chisel3.experimental.ChiselEnum +import chisel3.experimental.AffectsChiselPrefix import chisel3.internal.firrtl.UnknownWidth -import chisel3.internal.naming.chiselName import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util._ import chisel3.testers.BasicTester @@ -575,11 +575,10 @@ class StrongEnumAnnotator extends Module { val indexed2 = vec_of_bundles(cycle) } -@chiselName class StrongEnumAnnotatorWithChiselName extends Module { import EnumExample._ - object LocalEnum extends ChiselEnum { + object LocalEnum extends ChiselEnum with AffectsChiselPrefix { val le0, le1 = Value val le2 = Value val le100 = Value(100.U) diff --git a/src/test/scala/chiselTests/ToTargetSpec.scala b/src/test/scala/chiselTests/ToTargetSpec.scala new file mode 100644 index 00000000..dc4ec448 --- /dev/null +++ b/src/test/scala/chiselTests/ToTargetSpec.scala @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.stage.ChiselStage +import chisel3.util.Queue +import chisel3.internal.ChiselException + +class ToTargetSpec extends ChiselFlatSpec with Utils { + + var m: InstanceNameModule = _ + ChiselStage.elaborate { m = new InstanceNameModule; m } + + val mn = "InstanceNameModule" + val top = s"~$mn|$mn" + + behavior.of(".toTarget") + + val deprecationMsg = "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated" + + it should "work with module IO" in { + val io = m.io.toTarget.toString + assert(io == s"$top>io") + } + + it should "not work for literals" in { + a[ChiselException] shouldBe thrownBy { + m.x.toTarget.toString + } + } + + it should "work with non-hardware values (but be deprecated)" in { + val (ylog, y) = grabLog(m.y.toTarget.toString) + val (zlog, z) = grabLog(m.z.toTarget.toString) + assert(y == s"$top>y") + ylog should include(deprecationMsg) + assert(z == s"$top>z") + zlog should include(deprecationMsg) + } + + it should "work with non-hardware bundle elements (but be deprecated)" in { + val (log, foo) = grabLog(m.z.foo.toTarget.toString) + log should include(deprecationMsg) + assert(foo == s"$top>z.foo") + } + + it should "work with modules" in { + val q = m.q.toTarget.toString + assert(q == s"~$mn|Queue") + } +} diff --git a/src/test/scala/chiselTests/UIntOps.scala b/src/test/scala/chiselTests/UIntOps.scala index 0010e9ac..2f55da9a 100644 --- a/src/test/scala/chiselTests/UIntOps.scala +++ b/src/test/scala/chiselTests/UIntOps.scala @@ -172,9 +172,9 @@ class MatchedRotateLeftAndRight(w: Int = 13) extends BasicTester { } class UIntLitExtractTester extends BasicTester { - assert("b101010".U(2) === false.B) - assert("b101010".U(3) === true.B) - assert("b101010".U(100) === false.B) + assert("b101010".U.extract(2) === false.B) + assert("b101010".U.extract(3) === true.B) + assert("b101010".U.extract(100) === false.B) assert("b101010".U(3, 0) === "b1010".U) assert("b101010".U(9, 0) === "b0000101010".U) diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala index 2eb6ae5f..02743187 100644 --- a/src/test/scala/chiselTests/Vec.scala +++ b/src/test/scala/chiselTests/Vec.scala @@ -517,4 +517,26 @@ class VecSpec extends ChiselPropSpec with Utils { property("reduceTree should preserve input/output type") { assertTesterPasses { new ReduceTreeTester() } } + + property("Vecs of empty Bundles and empty Records should work") { + class MyModule(gen: Record) extends Module { + val idx = IO(Input(UInt(2.W))) + val in = IO(Input(gen)) + val out = IO(Output(gen)) + + val reg = RegInit(0.U.asTypeOf(Vec(4, gen))) + reg(idx) := in + out := reg(idx) + } + class EmptyBundle extends Bundle + class EmptyRecord extends Record { + val elements = collection.immutable.ListMap.empty + override def cloneType = (new EmptyRecord).asInstanceOf[this.type] + } + for (gen <- List(new EmptyBundle, new EmptyRecord)) { + val chirrtl = ChiselStage.emitChirrtl(new MyModule(gen)) + chirrtl should include("input in : { }") + chirrtl should include("reg reg : { }[4]") + } + } } diff --git a/src/test/scala/chiselTests/VerificationSpec.scala b/src/test/scala/chiselTests/VerificationSpec.scala index 95b0ffe6..32cee9e3 100644 --- a/src/test/scala/chiselTests/VerificationSpec.scala +++ b/src/test/scala/chiselTests/VerificationSpec.scala @@ -105,9 +105,9 @@ class VerificationSpec extends ChiselPropSpec with Matchers { val firLines = scala.io.Source.fromFile(firFile).getLines.toList // check that verification components have expected names - exactly(1, firLines) should include("cover(clock, _T, UInt<1>(\"h1\"), \"\") : cov") - exactly(1, firLines) should include("assume(clock, _T_3, UInt<1>(\"h1\"), \"\") : assm") - exactly(1, firLines) should include("assert(clock, _T_7, UInt<1>(\"h1\"), \"\") : asst") + (exactly(1, firLines) should include).regex("^\\s*cover\\(.*\\) : cov") + (exactly(1, firLines) should include).regex("^\\s*assume\\(.*\\) : assm") + (exactly(1, firLines) should include).regex("^\\s*assert\\(.*\\) : asst") } property("annotation of verification constructs with suggested name should work") { @@ -150,7 +150,7 @@ class VerificationSpec extends ChiselPropSpec with Matchers { val firLines = scala.io.Source.fromFile(firFile).getLines.toList // check that verification components have expected names - exactly(1, firLines) should include("assert(clock, _T, UInt<1>(\"h1\"), \"\") : hello") - exactly(1, firLines) should include("assume(clock, _T_4, UInt<1>(\"h1\"), \"\") : howdy") + (exactly(1, firLines) should include).regex("^\\s*assert\\(.*\\) : hello") + (exactly(1, firLines) should include).regex("^\\s*assume\\(.*\\) : howdy") } } 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" + ) + } +} diff --git a/src/test/scala/chiselTests/naming/NamePluginSpec.scala b/src/test/scala/chiselTests/naming/NamePluginSpec.scala index 18359fd2..a787bb80 100644 --- a/src/test/scala/chiselTests/naming/NamePluginSpec.scala +++ b/src/test/scala/chiselTests/naming/NamePluginSpec.scala @@ -3,6 +3,7 @@ package chiselTests.naming import chisel3._ +import chisel3.stage.ChiselStage import chisel3.aop.Select import chisel3.experimental.{prefix, treedump} import chiselTests.{ChiselFlatSpec, Utils} @@ -69,6 +70,24 @@ class NamePluginSpec extends ChiselFlatSpec with Utils { } } + "Scala plugin" should "name verification ops" in { + class Test extends Module { + val foo, bar = IO(Input(UInt(8.W))) + + { + val x1 = chisel3.assert(1.U === 1.U) + val x2 = cover(foo =/= bar) + val x3 = chisel3.assume(foo =/= 123.U) + val x4 = printf("foo = %d\n", foo) + } + } + val chirrtl = ChiselStage.emitChirrtl(new Test) + (chirrtl should include).regex("assert.*: x1") + (chirrtl should include).regex("cover.*: x2") + (chirrtl should include).regex("assume.*: x3") + (chirrtl should include).regex("printf.*: x4") + } + "Naming on option" should "work" in { class Test extends Module { @@ -321,4 +340,23 @@ class NamePluginSpec extends ChiselFlatSpec with Utils { Select.wires(top).map(_.instanceName) should be(List("a_b_c", "a_b", "a")) } } + + behavior.of("Unnamed values (aka \"Temporaries\")") + + they should "be declared by starting the name with '_'" in { + class Test extends Module { + { + val a = { + val b = { + val _c = Wire(UInt(3.W)) + 4.U // literal so there is no name + } + b + } + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("_a_b_c")) + } + } } diff --git a/src/test/scala/chiselTests/naming/PrefixSpec.scala b/src/test/scala/chiselTests/naming/PrefixSpec.scala index f9a78f0e..6d52407e 100644 --- a/src/test/scala/chiselTests/naming/PrefixSpec.scala +++ b/src/test/scala/chiselTests/naming/PrefixSpec.scala @@ -3,6 +3,7 @@ package chiselTests.naming import chisel3._ +import chisel3.stage.ChiselStage import chisel3.aop.Select import chisel3.experimental.{dump, noPrefix, prefix, treedump} import chiselTests.{ChiselPropSpec, Utils} @@ -232,18 +233,46 @@ class PrefixSpec extends ChiselPropSpec with Utils { } } - property("Prefixing should be the prefix during the last call to autoName/suggestName") { + property("Prefixing should NOT be influenced by suggestName") { class Test extends Module { { val wire = { - val x = Wire(UInt(3.W)).suggestName("mywire") - x + val x = Wire(UInt(3.W)) // wire_x + Wire(UInt(3.W)).suggestName("foo") + } + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("wire_x", "foo")) + } + } + + property("Prefixing should be influenced by the \"current name\" of the signal") { + class Test extends Module { + { + val wire = { + val y = Wire(UInt(3.W)).suggestName("foo") + val x = Wire(UInt(3.W)) // wire_x + y + } + + val wire2 = Wire(UInt(3.W)) + wire2 := { + val x = Wire(UInt(3.W)) // wire2_x + x + 1.U + } + wire2.suggestName("bar") + + val wire3 = Wire(UInt(3.W)) + wire3.suggestName("fizz") + wire3 := { + val x = Wire(UInt(3.W)) // fizz_x + x + 1.U } } } aspectTest(() => new Test) { top: Test => - Select.wires(top).map(_.instanceName) should be(List("mywire")) - Select.wires(top).map(_.instanceName) shouldNot be(List("wire_mywire")) + Select.wires(top).map(_.instanceName) should be(List("foo", "wire_x", "bar", "wire2_x", "fizz", "fizz_x")) } } @@ -391,6 +420,81 @@ class PrefixSpec extends ChiselPropSpec with Utils { aspectTest(() => new Test) { top: Test => Select.wires(top).map(_.instanceName) should be(List("x", "x_w_w", "x_w_w_w", "x_w_w_w_w")) } + } + + property("Prefixing should work for verification ops") { + class Test extends Module { + val foo, bar = IO(Input(UInt(8.W))) + + { + val x5 = { + val x1 = chisel3.assert(1.U === 1.U) + val x2 = cover(foo =/= bar) + val x3 = chisel3.assume(foo =/= 123.U) + val x4 = printf("foo = %d\n", foo) + x1 + } + } + } + val chirrtl = ChiselStage.emitChirrtl(new Test) + (chirrtl should include).regex("assert.*: x5") + (chirrtl should include).regex("cover.*: x5_x2") + (chirrtl should include).regex("assume.*: x5_x3") + (chirrtl should include).regex("printf.*: x5_x4") + } + property("Leading '_' in val names should be ignored in prefixes") { + class Test extends Module { + { + val a = { + val _b = { + val c = Wire(UInt(3.W)) + 4.U // literal because there is no name + } + _b + } + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("a_b_c")) + } + } + + // This checks that we don't just blanket ignore leading _ in prefixes + property("User-specified prefixes with '_' should be respected") { + class Test extends Module { + { + val a = { + val _b = prefix("_b") { + val c = Wire(UInt(3.W)) + } + 4.U + } + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("a__b_c")) + } + } + + property("Leading '_' in signal names should be ignored in prefixes from connections") { + class Test extends Module { + { + val a = { + val b = { + val _c = IO(Output(UInt(3.W))) // port so not selected as wire + _c := { + val d = Wire(UInt(3.W)) + d + } + 4.U // literal so there is no name + } + b + } + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("a_b_c_d")) + } } } diff --git a/src/test/scala/chiselTests/naming/ReflectiveNamingSpec.scala b/src/test/scala/chiselTests/naming/ReflectiveNamingSpec.scala new file mode 100644 index 00000000..baa991dd --- /dev/null +++ b/src/test/scala/chiselTests/naming/ReflectiveNamingSpec.scala @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.naming + +import chisel3._ +import chiselTests.{ChiselFlatSpec, Utils} + +class ReflectiveNamingSpec extends ChiselFlatSpec with Utils { + + behavior.of("Reflective naming") + + private def emitChirrtl(gen: => RawModule): String = { + // Annoyingly need to emit files to use CLI + val targetDir = createTestDirectory(this.getClass.getSimpleName).toString + val args = Array("--warn:reflective-naming", "-td", targetDir) + (new chisel3.stage.ChiselStage).emitChirrtl(gen, args) + } + + it should "NOT warn when no names are changed" in { + class Example extends Module { + val foo, bar = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + + val sum = foo +& bar + out := sum + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should equal("") + chirrtl should include("node sum = add(foo, bar)") + } + + it should "warn when changing the name of a node" in { + class Example extends Module { + val foo, bar = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + + val sum = foo +& bar + val fuzz = sum + out := sum + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should include("'sum' is renamed by reflection to 'fuzz'") + chirrtl should include("node fuzz = add(foo, bar)") + } + + // This also checks correct prefix reversing + it should "warn when changing the name of a node with a prefix in the name" in { + class Example extends Module { + val foo, bar = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + + // This is sketch, don't do this + var fuzz = 0.U + out := { + val sum = { + val node = foo +& bar + fuzz = node + node +% 0.U + } + sum + } + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should include("'out_sum_node' is renamed by reflection to 'fuzz'") + chirrtl should include("node fuzz = add(foo, bar)") + } + + it should "warn when changing the name of a Module instance" in { + import chisel3.util._ + class Example extends Module { + val enq = IO(Flipped(Decoupled(UInt(8.W)))) + val deq = IO(Decoupled(UInt(8.W))) + + val q = Module(new Queue(UInt(8.W), 4)) + q.io.enq <> enq + deq <> q.io.deq + + val fuzz = q + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should include("'q' is renamed by reflection to 'fuzz'") + chirrtl should include("inst fuzz of Queue") + } + + it should "warn when changing the name of an Instance" in { + import chisel3.experimental.hierarchy.{Definition, Instance} + import chiselTests.experimental.hierarchy.Examples.AddOne + class Example extends Module { + val defn = Definition(new AddOne) + val inst = Instance(defn) + val fuzz = inst + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should include("'inst' is renamed by reflection to 'fuzz'") + chirrtl should include("inst fuzz of AddOne") + } + + it should "warn when changing the name of a Mem" in { + class Example extends Module { + val mem = SyncReadMem(8, UInt(8.W)) + + val fuzz = mem + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should include("'mem' is renamed by reflection to 'fuzz'") + chirrtl should include("smem fuzz") + } + + it should "NOT warn when changing the name of a verification statement" in { + class Example extends Module { + val in = IO(Input(UInt(8.W))) + val z = chisel3.assert(in =/= 123.U) + val fuzz = z + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should equal("") + // But the name is actually changed + (chirrtl should include).regex("assert.*: fuzz") + } + + it should "NOT warn when \"naming\" a literal" in { + class Example extends Module { + val out = IO(Output(UInt(8.W))) + + val sum = 0.U + val fuzz = sum + out := sum + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should equal("") + chirrtl should include("out <= UInt") + } + + it should "NOT warn when \"naming\" a field of an Aggregate" in { + class Example extends Module { + val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + val in = io.in + val out = io.out + out := in + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should equal("") + chirrtl should include("io.out <= io.in") + } + + it should "NOT warn when \"naming\" unbound Data" in { + class Example extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + val z = UInt(8.W) + val a = z + out := in + } + val (log, chirrtl) = grabLog(emitChirrtl(new Example)) + log should equal("") + chirrtl should include("out <= in") + } +} diff --git a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala index fa2c6f08..9b2dd600 100644 --- a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala @@ -104,4 +104,21 @@ class TruthTableSpec extends AnyFlatSpec { assert(t.toString contains "111->?") assert(t.toString contains " 0") } + + "Using TruthTable.fromEspressoOutput" should "merge rows on conflict" in { + val mapping = List( + (BitPat("b110"), BitPat("b001")), + (BitPat("b111"), BitPat("b001")), + (BitPat("b111"), BitPat("b010")), + (BitPat("b111"), BitPat("b100")) + ) + + assert( + TruthTable.fromEspressoOutput(mapping, BitPat("b?")) == + TruthTable.fromString("""110->001 + |111->111 + |? + |""".stripMargin) + ) + } } |
