diff options
Diffstat (limited to 'src/test')
27 files changed, 1655 insertions, 42 deletions
diff --git a/src/test/scala/chiselTests/Assert.scala b/src/test/scala/chiselTests/Assert.scala index 1849ddf8..5e7b6496 100644 --- a/src/test/scala/chiselTests/Assert.scala +++ b/src/test/scala/chiselTests/Assert.scala @@ -61,6 +61,87 @@ class BadUnescapedPercentAssertTester extends BasicTester { stop() } +class PrintableFormattedAssertTester extends BasicTester { + val foobar = Wire(UInt(32.W)) + foobar := 123.U + assert(foobar === 123.U, cf"Error! Wire foobar =/= $foobar%x This is 100%% wrong.\n") + stop() +} + +class PrintableBadUnescapedPercentAssertTester extends BasicTester { + assert(1.U === 1.U, p"I'm 110% sure this is an invalid message") + stop() +} + +class PrintableAssumeTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + + val w = Wire(UInt(8.W)) + w := 255.U + assume(w === 255.U, cf"Assumption failed, Wire w =/= $w%x") + + out := in +} + +class PrintableScopeTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := in + + val w = Wire(UInt(8.W)) + w := 255.U + + val printableWire = cf"$w" + val printablePort = cf"$in" +} + +class AssertPrintableWireScope extends BasicTester { + val mod = Module(new PrintableScopeTester) + assert(1.U === 2.U, mod.printableWire) + stop() +} + +class AssertPrintablePortScope extends BasicTester { + val mod = Module(new PrintableScopeTester) + mod.in := 255.U + assert(1.U === 1.U, mod.printablePort) + stop() +} + +class AssertPrintableFailingWhenScope extends BasicTester { + val mod = Module(new PrintableWhenScopeTester) + assert(1.U === 1.U, mod.printable) + stop() +} + +class AssumePrintableWireScope extends BasicTester { + val mod = Module(new PrintableScopeTester) + assume(1.U === 1.U, mod.printableWire) + stop() +} + +class AssumePrintablePortScope extends BasicTester { + val mod = Module(new PrintableScopeTester) + mod.in := 255.U + assume(1.U === 1.U, mod.printablePort) + stop() +} + +class PrintableWhenScopeTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + + out := in + + val w = Wire(UInt(8.W)) + w := 255.U + var printable = cf"" + when(true.B) { + printable = cf"$w" + } +} + class AssertSpec extends ChiselFlatSpec with Utils { "A failing assertion" should "fail the testbench" in { assert(!runTester { new FailingAssertTester }) @@ -71,12 +152,37 @@ class AssertSpec extends ChiselFlatSpec with Utils { "An assertion" should "not assert until we come out of reset" in { assertTesterPasses { new PipelinedResetTester } } + + "Assert Printables" should "respect port scoping" in { + assertTesterPasses { new AssertPrintablePortScope } + } + "Assert Printables" should "respect wire scoping" in { + a[ChiselException] should be thrownBy { ChiselStage.elaborate(new AssertPrintableWireScope) } + } + "Assume Printables" should "respect port scoping" in { + assertTesterPasses { new AssumePrintablePortScope } + } + + "Assume Printables" should "respect wire scoping" in { + a[ChiselException] should be thrownBy { ChiselStage.elaborate(new AssumePrintableWireScope) } + } + + "Assert Printables" should "respect when scope" in { + a[ChiselException] should be thrownBy { ChiselStage.elaborate(new AssertPrintableFailingWhenScope) } + } + "Assertions" should "allow the modulo operator % in the message" in { assertTesterPasses { new ModuloAssertTester } } they should "allow printf-style format strings with arguments" in { assertTesterPasses { new FormattedAssertTester } } + they should "allow printf-style format strings in Assumes" in { + val chirrtl = ChiselStage.emitChirrtl(new PrintableAssumeTester) + chirrtl should include( + """assume(w === 255.U, cf\"Assumption failed, Wire w =/= $w%%%%x\")\n", w)""" + ) + } they should "not allow unescaped % in the message" in { a[java.util.UnknownFormatConversionException] should be thrownBy { extractCause[java.util.UnknownFormatConversionException] { @@ -84,4 +190,16 @@ class AssertSpec extends ChiselFlatSpec with Utils { } } } + + they should "allow printable format strings with arguments" in { + assertTesterPasses { new FormattedAssertTester } + } + they should "not allow unescaped % in the printable message" in { + a[java.util.UnknownFormatConversionException] should be thrownBy { + extractCause[java.util.UnknownFormatConversionException] { + ChiselStage.elaborate { new BadUnescapedPercentAssertTester } + } + } + } + } diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index 5d2cd496..353ae58c 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -6,6 +6,8 @@ import chisel3._ import chisel3.testers.TestUtils import chisel3.util.QueueIO import chisel3.stage.ChiselStage.elaborate +import chisel3.experimental.AutoCloneType +import scala.collection.immutable.ListMap class BundleWithIntArg(val i: Int) extends Bundle { val out = UInt(i.W) @@ -72,6 +74,25 @@ class InheritingBundle extends QueueIO(UInt(8.W), 8) { val error = Output(Bool()) } +class RecordAutoCloneType[T <: Data](gen: T) extends Record with AutoCloneType { + lazy val elements = ListMap("value" -> gen) + // This is a weird thing to do, but as only Bundles have these methods, it should be legal + protected def _elementsImpl: Iterable[(String, Any)] = elements + protected def _usingPlugin = false +} + +// Records that don't mixin AutoCloneType should still be able to implement the related methods +// NOTE: This is a very weird thing to do, don't do it. +class RecordWithVerbotenMethods(w: Int) extends Record { + lazy val elements = ListMap("value" -> UInt(w.W)) + override def cloneType: this.type = (new RecordWithVerbotenMethods(w)).asInstanceOf[this.type] + // Verboten methods + protected def _usingPlugin = false + protected override def _cloneTypeImpl = this.cloneType + + protected def _elementsImpl: Iterable[(String, Any)] = Nil +} + class AutoClonetypeSpec extends ChiselFlatSpec with Utils { "Bundles with Scala args" should "not need clonetype" in { @@ -400,4 +421,24 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } elaborate(new MyModule) } + + it should "support Records that mixin AutoCloneType" in { + class MyModule extends Module { + val gen = new RecordAutoCloneType(UInt(8.W)) + val in = IO(Input(gen)) + val out = IO(Output(gen)) + out := in + } + elaborate(new MyModule) + } + + it should "support Records that don't mixin AutoCloneType and use forbidden methods" in { + class MyModule extends Module { + val gen = new RecordWithVerbotenMethods(8) + val in = IO(Input(gen)) + val out = IO(Output(gen)) + out := in + } + elaborate(new MyModule) + } } diff --git a/src/test/scala/chiselTests/BulkConnectSpec.scala b/src/test/scala/chiselTests/BulkConnectSpec.scala index 281890d4..0a1616d3 100644 --- a/src/test/scala/chiselTests/BulkConnectSpec.scala +++ b/src/test/scala/chiselTests/BulkConnectSpec.scala @@ -54,7 +54,15 @@ class BulkConnectSpec extends ChiselPropSpec { }) chirrtl should include("out.buzz.foo <= in.buzz.foo") + chirrtl should include("out.fizz <= in.fizz") + chirrtl should include("deq.bits <- enq.bits") + chirrtl should include("deq.valid <= enq.valid") + chirrtl should include("enq.ready <= deq.ready") + chirrtl shouldNot include("deq <= enq") + chirrtl shouldNot include("deq.bits.foo <= enq.bits.foo") + chirrtl shouldNot include("deq.bits.foo <- enq.bits.foo") + chirrtl shouldNot include("deq.bits.bar") } property("Chisel connects should not emit FIRRTL bulk connects between differing FIRRTL types") { @@ -74,7 +82,9 @@ class BulkConnectSpec extends ChiselPropSpec { out <> in }) // out <- in is illegal FIRRTL - chirrtl should include("out.foo.bar <= in.foo.bar") + exactly(2, chirrtl.split('\n')) should include("out.foo.bar <= in.foo.bar") + chirrtl shouldNot include("out <= in") + chirrtl shouldNot include("out <- in") } property("Chisel connects should not emit a FIRRTL bulk connect for a bidirectional MonoConnect") { @@ -91,6 +101,9 @@ class BulkConnectSpec extends ChiselPropSpec { }) chirrtl shouldNot include("wire <= enq") + chirrtl should include("wire.bits <= enq.bits") + chirrtl should include("wire.valid <= enq.valid") + chirrtl should include("wire.ready <= enq.ready") chirrtl should include("deq <= enq") } diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 6f560b94..e00afcf6 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -38,7 +38,10 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { annotations: AnnotationSeq = Seq() ): Boolean = { // Change this to enable Treadle as a backend - val defaultBackend = chisel3.testers.TesterDriver.defaultBackend + val defaultBackend = { + val useTreadle = sys.env.get("CHISEL3_CI_USE_TREADLE").isDefined + if (useTreadle) chisel3.testers.TreadleBackend else chisel3.testers.TesterDriver.defaultBackend + } val hasBackend = TestUtils.containsBackend(annotations) val annos: Seq[Annotation] = if (hasBackend) annotations else defaultBackend +: annotations TesterDriver.execute(() => t, additionalVResources, annos) diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index 1e199297..d388c093 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -3,6 +3,7 @@ package chiselTests import scala.collection.immutable.ListMap +import chisel3.stage.ChiselStage.emitChirrtl // Keep Chisel._ separate from chisel3._ below object CompatibilityComponents { @@ -390,4 +391,430 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { compile(new Top(true)) compile(new Top(false)) } + + "A Chisel.Bundle with only unspecified directions" should "work with D/I" in { + + object Compat { + import Chisel._ + import chisel3.experimental.hierarchy.{instantiable, public} + + class CompatBiDirUnspecifiedBundle extends Bundle { + val out = Bool() + val in = Flipped(Bool()) + } + + @instantiable + class CompatModule extends Module { + @public val io = IO(new CompatBiDirUnspecifiedBundle) + } + } + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.Instance + class Example extends Module { + val mod = Module(new Compat.CompatModule()) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A Chisel.Bundle with mixed Specified and Unspecified directions" should "work with D/I" in { + + object Compat { + import Chisel._ + import chisel3.experimental.hierarchy.{instantiable, public} + + class CompatBiDirMixedBundle extends Bundle { + val out = Bool() + val in = Flipped(Bool()) + val explicit = Output(Bool()) + } + + @instantiable + class CompatModule extends Module { + @public val io = IO(new CompatBiDirMixedBundle) + } + } + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.Instance + class Example extends Module { + val mod = Module(new Compat.CompatModule) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A Chisel.Bundle with only unspecified vec direction" should "work with D/I" in { + + object Compat { + import Chisel._ + import chisel3.experimental.hierarchy.{instantiable, public} + + class CompatBiDirUnspecifiedVecBundle extends Bundle { + val out = Vec(3, Bool()) + val in = Flipped(Vec(3, Bool())) + } + + @instantiable + class CompatModule extends Module { + @public val io = IO(new CompatBiDirUnspecifiedVecBundle) + } + } + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.Instance + class Example extends Module { + val mod = Module(new Compat.CompatModule()) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A chisel3.Bundle with only unspecified directions" should "work with D/I" in { + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.{instantiable, public, Instance} + + class BiDirUnspecifiedBundle extends Bundle { + val out = Bool() + val in = Flipped(Bool()) + } + + @instantiable + class MyModule extends Module { + @public val io = IO(new BiDirUnspecifiedBundle) + io <> DontCare + } + + class Example extends Module { + val mod = Module(new MyModule()) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A chisel3.Bundle with mixed Specified and Unspecified directions" should "work with D/I" in { + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.{instantiable, public, Instance} + + class BiDirMixedBundle extends Bundle { + val out = Bool() + val in = Flipped(Bool()) + val explicit = Output(Bool()) + } + + @instantiable + class MyModule extends Module { + @public val io = IO(new BiDirMixedBundle) + io <> DontCare + } + class Example extends Module { + val mod = Module(new MyModule) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A chisel3.Bundle with only unspecified vec direction" should "work with D/I" in { + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.{instantiable, public, Instance} + + class BiDirUnspecifiedVecBundle extends Bundle { + val out = Vec(3, Bool()) + val in = Flipped(Vec(3, Bool())) + } + + @instantiable + class MyModule extends Module { + @public val io = IO(new BiDirUnspecifiedVecBundle) + io <> DontCare + } + + class Example extends Module { + val mod = Module(new MyModule()) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A chisel3.Bundle with only unspecified vec direction within an unspecified direction parent Bundle" should "work with D/I" in { + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.{instantiable, public, Instance} + + class UnspecifiedVecBundle extends Bundle { + val vec = Vec(3, Bool()) + } + + class UnspecifiedParentBundle extends Bundle { + val out = new UnspecifiedVecBundle + } + + @instantiable + class MyModule extends Module { + @public val io = IO(new UnspecifiedParentBundle) + io <> DontCare + } + + class Example extends Module { + val mod = Module(new MyModule()) + + val wire = Wire(new UnspecifiedParentBundle) + wire.out.vec <> mod.io.out.vec + val inst = Instance(mod.toDefinition) + wire.out.vec <> inst.io.out.vec + + } + } + compile(new Chisel3.Example) + } + + "A undirectioned Chisel.Bundle used in a MixedVec " should "bulk connect in import chisel3._ code correctly" in { + + object Compat { + + import Chisel._ + import chisel3.util.MixedVec + + class ChiselModule extends Module { + val io = IO(new Bundle { + val out = MixedVec(Seq.fill(3) { Bool() }) + val in = Flipped(MixedVec(Seq.fill(3) { Bool() })) + }) + io.out := RegNext(io.in) + } + + } + object Chisel3 { + import chisel3._ + + class Chisel3Module extends Compat.ChiselModule + + class Example extends Module { + val oldMod = Module(new Compat.ChiselModule) + val newMod = Module(new Chisel3Module) + + oldMod.io.in <> DontCare + newMod.io.in <> DontCare + + } + } + compile(new Chisel3.Example) + } + + "A undirectioned Chisel.Bundle with Records with undirectioned and directioned fields " should "work" in { + + object Compat { + + import Chisel._ + + class ChiselModule(gen: () => Data) extends Module { + val io = IO(new Bundle { + val mixed = new Chisel3.MyRecord(gen) + }) + } + + } + object Chisel3 { + import chisel3._ + import scala.collection.immutable.SeqMap + + class MyRecord(gen: () => Data) extends Record with chisel3.experimental.AutoCloneType { + val elements = SeqMap("genDirectioned" -> Output(gen()), "genUndirectioned" -> gen()) + } + + class Example extends Module { + val newMod = Module(new Compat.ChiselModule(() => Bool())) + } + } + compile(new Chisel3.Example) + } + + "A BlackBox with Chisel._ fields in its IO" should "bulk connect in import chisel3._ code correctly" in { + object Compat { + import Chisel._ + class LegacyChiselIO extends Bundle { + val foo = Output(Bool()) + val bar = Output(Bool()) + } + } + object Chisel3 { + import chisel3._ + import chisel3.util.Valid + + class FooModuleIO extends Bundle { + val quz = Input(new QuzIO) + val foo = Output(Bool()) + val bar = Input(Bool()) + } + class QuzIO extends Bundle { + val q = Flipped(Valid(new Compat.LegacyChiselIO)) + } + class FooModule extends Module { + val io = IO(new FooModuleIO) + io <> DontCare + } + class FooMirrorBlackBox extends BlackBox { + val io = IO(Flipped(new FooModuleIO)) + } + class Top extends Module { + val foo = Module(new FooModule) + val mirror = Module(new FooMirrorBlackBox) + foo.io <> mirror.io + } + } + val chirrtl = emitChirrtl(new Chisel3.Top) + chirrtl should include("foo.io.bar <= mirror.bar") + chirrtl should include("mirror.foo <= foo.io.foo") + chirrtl should include("foo.io.quz.q.bits <- mirror.quz.q.bits") + chirrtl should include("foo.io.quz.q.valid <= mirror.quz.q.valid") + } + + "A chisel3.Bundle bulk connected to a Chisel Bundle in either direction" should "work even with mismatched fields" in { + object Compat { + import Chisel._ + class FooBundle extends Bundle { + val foo = UInt(width = 8) + } + } + object Chisel3 { + import chisel3._ + class BarBundle extends Bundle { + val bar = UInt(8.W) + } + class MyModule(swap: Boolean) extends Module { + val in = IO(Input(if (swap) new Compat.FooBundle else new BarBundle)) + val out = IO(Output(if (swap) new BarBundle else new Compat.FooBundle)) + out <> DontCare + out <> in + } + } + val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl0 shouldNot include("<=") + chirrtl0 should include("out <- in") + val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl1 shouldNot include("<=") + chirrtl1 should include("out <- in") + } + + it should "work with missing fields in the Chisel._" in { + object Compat { + import Chisel._ + class FooBundle extends Bundle { + val foo = UInt(width = 8) + } + } + object Chisel3 { + import chisel3._ + class FooBarBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + } + + class MyModule(swap: Boolean) extends Module { + val in = IO(Input(if (swap) new Compat.FooBundle else new FooBarBundle)) + val out = IO(Output(if (swap) new FooBarBundle else new Compat.FooBundle)) + out <> DontCare + out <> in + } + } + val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl0 shouldNot include("<=") + chirrtl0 should include("out <- in") + val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl1 shouldNot include("<=") + chirrtl1 should include("out <- in") + } + + it should "work with missing fields in the chisel3._" in { + object Compat { + import Chisel._ + class FooBundle extends Bundle { + val foo = UInt(width = 8) + } + } + object Chisel3 { + import chisel3._ + class FooBarBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + } + + class MyModule(swap: Boolean) extends Module { + val in = IO(Input(if (swap) new Compat.FooBundle else new FooBarBundle)) + val out = IO(Output(if (swap) new FooBarBundle else new Compat.FooBundle)) + out <> DontCare + out <> in + } + } + val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl0 shouldNot include("<=") + chirrtl0 should include("out <- in") + val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl1 shouldNot include("<=") + chirrtl1 should include("out <- in") + } + + it should "emit FIRRTL connects if possible" in { + object Compat { + import Chisel._ + class FooBarBundle extends Bundle { + val foo = UInt(8.W) + val bar = Flipped(UInt(8.W)) + } + } + object Chisel3 { + import chisel3._ + class FooBarBundle extends Bundle { + val foo = Output(UInt(8.W)) + val bar = Input(UInt(8.W)) + } + class MyModule(swap: Boolean) extends Module { + val in = IO(Flipped((if (swap) new Compat.FooBarBundle else new FooBarBundle))) + val out = IO(if (swap) new FooBarBundle else new Compat.FooBarBundle) + out <> DontCare + out <> in + } + } + val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl0 should include("out <= in") + chirrtl0 shouldNot include("out <- in") + val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl1 should include("out <= in") + chirrtl1 shouldNot include("out <- in") + } } diff --git a/src/test/scala/chiselTests/CompatibilitySpec.scala b/src/test/scala/chiselTests/CompatibilitySpec.scala index 41cfbec4..5a3b43e6 100644 --- a/src/test/scala/chiselTests/CompatibilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilitySpec.scala @@ -614,4 +614,26 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck ChiselStage.elaborate(new MyModule) } + behavior.of("BlackBox") + + it should "have invalidated ports in a compatibility context" in { + class ExtModuleInvalidatedTester extends Module { + val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + val inst = Module(new BlackBox { + val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + }) + inst.io.in := io.in + io.out := inst.io.out + } + + val chirrtl = ChiselStage.emitChirrtl(new ExtModuleInvalidatedTester) + chirrtl should include("inst.in is invalid") + chirrtl should include("inst.out is invalid") + } } diff --git a/src/test/scala/chiselTests/DataEqualitySpec.scala b/src/test/scala/chiselTests/DataEqualitySpec.scala new file mode 100644 index 00000000..4ac3292d --- /dev/null +++ b/src/test/scala/chiselTests/DataEqualitySpec.scala @@ -0,0 +1,257 @@ +package chiselTests + +import chisel3._ +import chisel3.experimental.VecLiterals._ +import chisel3.experimental.BundleLiterals._ +import chisel3.experimental.{Analog, ChiselEnum, ChiselRange, FixedPoint, Interval} +import chisel3.stage.ChiselStage +import chisel3.testers.BasicTester +import chisel3.util.Valid + +class EqualityModule(lhsGen: => Data, rhsGen: => Data) extends Module { + val out = IO(Output(Bool())) + + val lhs = lhsGen + val rhs = rhsGen + + out := lhs === rhs +} + +class EqualityTester(lhsGen: => Data, rhsGen: => Data) extends BasicTester { + val module = Module(new EqualityModule(lhsGen, rhsGen)) + + assert(module.out) + + stop() +} + +class AnalogBundle extends Bundle { + val analog = Analog(32.W) +} + +class AnalogExceptionModule extends Module { + class AnalogExceptionModuleIO extends Bundle { + val bundle1 = new AnalogBundle + val bundle2 = new AnalogBundle + } + + val io = IO(new AnalogExceptionModuleIO) +} + +class AnalogExceptionTester extends BasicTester { + val module = Module(new AnalogExceptionModule) + + module.io.bundle1 <> DontCare + module.io.bundle2 <> DontCare + + assert(module.io.bundle1 === module.io.bundle2) + + stop() +} + +class DataEqualitySpec extends ChiselFlatSpec with Utils { + object MyEnum extends ChiselEnum { + val sA, sB = Value + } + object MyEnumB extends ChiselEnum { + val sA, sB = Value + } + class MyBundle extends Bundle { + val a = UInt(8.W) + val b = Bool() + val c = MyEnum() + } + class LongBundle extends Bundle { + val a = UInt(48.W) + val b = SInt(32.W) + val c = FixedPoint(16.W, 4.BP) + } + class RuntimeSensitiveBundle(gen: => Bundle) extends Bundle { + val a = UInt(8.W) + val b: Bundle = gen + } + + behavior.of("UInt === UInt") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(0.U, 0.U) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(0.U, 1.U) + } + } + + behavior.of("SInt === SInt") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(0.S, 0.S) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(0.S, 1.S) + } + } + + behavior.of("Reset === Reset") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(true.B, true.B) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(true.B, false.B) + } + } + + behavior.of("AsyncReset === AsyncReset") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(true.B.asAsyncReset, true.B.asAsyncReset) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(true.B.asAsyncReset, false.B.asAsyncReset) + } + } + + behavior.of("Interval === Interval") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(2.I, 2.I) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(2.I, 3.I) + } + } + + behavior.of("FixedPoint === FixedPoint") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(4.5.F(16.W, 4.BP), 4.5.F(16.W, 4.BP)) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(4.5.F(16.W, 4.BP), 4.6.F(16.W, 4.BP)) + } + } + + behavior.of("ChiselEnum === ChiselEnum") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(MyEnum.sA, MyEnum.sA) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(MyEnum.sA, MyEnum.sB) + } + } + + behavior.of("Vec === Vec") + it should "pass with equal sizes, equal values" in { + assertTesterPasses { + new EqualityTester( + Vec(3, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + Vec(3, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U) + ) + } + } + it should "fail with equal sizes, differing values" in { + assertTesterFails { + new EqualityTester( + Vec(3, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + Vec(3, UInt(8.W)).Lit(0 -> 0.U, 1 -> 1.U, 2 -> 2.U) + ) + } + } + it should "throw a ChiselException with differing sizes" in { + (the[ChiselException] thrownBy extractCause[ChiselException] { + assertTesterFails { + new EqualityTester( + Vec(3, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + Vec(4, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 3 -> 4.U) + ) + } + }).getMessage should include("Vec sizes differ") + } + + behavior.of("Bundle === Bundle") + it should "pass with equal type, equal values" in { + assertTesterPasses { + new EqualityTester( + (new MyBundle).Lit(_.a -> 42.U, _.b -> false.B, _.c -> MyEnum.sB), + (new MyBundle).Lit(_.a -> 42.U, _.b -> false.B, _.c -> MyEnum.sB) + ) + } + } + it should "fail with equal type, differing values" in { + assertTesterFails { + new EqualityTester( + (new MyBundle).Lit(_.a -> 42.U, _.b -> false.B, _.c -> MyEnum.sB), + (new MyBundle).Lit(_.a -> 42.U, _.b -> false.B, _.c -> MyEnum.sA) + ) + } + } + it should "throw a ChiselException with differing runtime types" in { + (the[ChiselException] thrownBy extractCause[ChiselException] { + assertTesterFails { + new EqualityTester( + (new RuntimeSensitiveBundle(new MyBundle)).Lit( + _.a -> 1.U, + _.b -> (new MyBundle).Lit( + _.a -> 42.U, + _.b -> false.B, + _.c -> MyEnum.sB + ) + ), + (new RuntimeSensitiveBundle(new LongBundle)).Lit( + _.a -> 1.U, + _.b -> (new LongBundle).Lit( + _.a -> 42.U, + _.b -> 0.S, + _.c -> 4.5.F(16.W, 4.BP) + ) + ) + ) + } + }).getMessage should include("Runtime types differ") + } + + behavior.of("DontCare === DontCare") + it should "pass with two invalids" in { + assertTesterPasses { + new EqualityTester(Valid(UInt(8.W)).Lit(_.bits -> 123.U), Valid(UInt(8.W)).Lit(_.bits -> 123.U)) + } + } + it should "exhibit the same behavior as comparing two invalidated wires" in { + // Also check that two invalidated wires are equal + assertTesterPasses { + new EqualityTester(WireInit(UInt(8.W), DontCare), WireInit(UInt(8.W), DontCare)) + } + + // Compare the verilog generated from both test cases and verify that they both are equal to true + val verilog1 = ChiselStage.emitVerilog( + new EqualityModule(Valid(UInt(8.W)).Lit(_.bits -> 123.U), Valid(UInt(8.W)).Lit(_.bits -> 123.U)) + ) + val verilog2 = + ChiselStage.emitVerilog(new EqualityModule(WireInit(UInt(8.W), DontCare), WireInit(UInt(8.W), DontCare))) + + verilog1 should include("assign out = 1'h1;") + verilog2 should include("assign out = 1'h1;") + } + + behavior.of("Analog === Analog") + it should "throw a ChiselException" in { + (the[ChiselException] thrownBy extractCause[ChiselException] { + assertTesterFails { new AnalogExceptionTester } + }).getMessage should include("Equality isn't defined for Analog values") + } +} diff --git a/src/test/scala/chiselTests/Direction.scala b/src/test/scala/chiselTests/Direction.scala index 0c657273..ddbd99d2 100644 --- a/src/test/scala/chiselTests/Direction.scala +++ b/src/test/scala/chiselTests/Direction.scala @@ -4,9 +4,12 @@ package chiselTests import org.scalatest._ import chisel3._ +import chisel3.experimental.OpaqueType import chisel3.stage.ChiselStage import org.scalatest.matchers.should.Matchers +import scala.collection.immutable.SeqMap + class DirectionedBundle extends Bundle { val in = Input(UInt(32.W)) val out = Output(UInt(32.W)) @@ -83,15 +86,15 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils { }) } - property("Empty Vecs with no direction on the sample_element *should* cause direction errors") { - an[Exception] should be thrownBy extractCause[Exception] { - ChiselStage.elaborate(new Module { - val io = IO(new Bundle { - val foo = Input(UInt(8.W)) - val x = Vec(0, UInt(8.W)) - }) + property( + "Empty Vecs with no direction on the sample_element should not cause direction errors, as Chisel and chisel3 directions are merged" + ) { + ChiselStage.elaborate(new Module { + val io = IO(new Bundle { + val foo = Input(UInt(8.W)) + val x = Vec(0, UInt(8.W)) }) - } + }) } property("Empty Bundles should not cause direction errors") { @@ -117,15 +120,15 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils { }) } - property("Explicitly directioned but empty Bundles should cause direction errors") { - an[Exception] should be thrownBy extractCause[Exception] { - ChiselStage.elaborate(new Module { - val io = IO(new Bundle { - val foo = UInt(8.W) - val x = Input(new Bundle {}) - }) + property( + "Explicitly directioned but empty Bundles should not cause direction errors because Chisel and chisel3 directionality are merged" + ) { + ChiselStage.elaborate(new Module { + val io = IO(new Bundle { + val foo = UInt(8.W) + val x = Input(new Bundle {}) }) - } + }) } import chisel3.experimental.{DataMirror, Direction} @@ -327,4 +330,114 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils { } } } + property("Can now describe a Decoupled bundle using Flipped, not Input/Output in chisel3") { + class Decoupled extends Bundle { + val bits = UInt(3.W) + val valid = Bool() + val ready = Flipped(Bool()) + } + class MyModule extends RawModule { + val incoming = IO(Flipped(new Decoupled)) + val outgoing = IO(new Decoupled) + + outgoing <> incoming + } + + val emitted: String = ChiselStage.emitChirrtl(new MyModule) + + // Check that emitted directions are correct. + assert(emitted.contains("input incoming : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}")) + assert(emitted.contains("output outgoing : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}")) + assert(emitted.contains("outgoing <= incoming")) + } + property("Can now mix Input/Output and Flipped within the same bundle") { + class Decoupled extends Bundle { + val bits = UInt(3.W) + val valid = Bool() + val ready = Flipped(Bool()) + } + class DecoupledAndMonitor extends Bundle { + val producer = new Decoupled() + val consumer = Flipped(new Decoupled()) + val monitor = Input(new Decoupled()) // Same as Flipped(stripFlipsIn(..)) + val driver = Output(new Decoupled()) // Same as stripFlipsIn(..) + } + class MyModule extends RawModule { + val io = IO(Flipped(new DecoupledAndMonitor())) + io.consumer <> io.producer + io.monitor.bits := io.driver.bits + io.monitor.valid := io.driver.valid + io.monitor.ready := io.driver.ready + } + + val emitted: String = ChiselStage.emitChirrtl(new MyModule) + + assert( + emitted.contains( + "input io : { producer : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}, flip consumer : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}, flip monitor : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}, driver : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}}" + ) + ) + assert(emitted.contains("io.consumer <= io.producer")) + assert(emitted.contains("io.monitor.bits <= io.driver.bits")) + assert(emitted.contains("io.monitor.valid <= io.driver.valid")) + assert(emitted.contains("io.monitor.ready <= io.driver.ready")) + } + property("Bugfix: marking Vec fields with mixed directionality as Output/Input clears inner directions") { + class Decoupled extends Bundle { + val bits = UInt(3.W) + val valid = Bool() + val ready = Flipped(Bool()) + } + class Coercing extends Bundle { + val source = Output(Vec(1, new Decoupled())) + val sink = Input(Vec(1, new Decoupled())) + } + class MyModule extends RawModule { + val io = IO(new Coercing()) + val source = IO(Output(Vec(1, new Decoupled()))) + val sink = IO(Input(Vec(1, new Decoupled()))) + } + + val emitted: String = ChiselStage.emitChirrtl(new MyModule) + + assert( + emitted.contains( + "output io : { source : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}[1], flip sink : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}[1]}" + ) + ) + assert( + emitted.contains( + "output source : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}[1]" + ) + ) + assert( + emitted.contains( + "input sink : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}[1]" + ) + ) + } + property("Bugfix: clearing all flips inside an opaque type") { + + class Decoupled extends Bundle { + val bits = UInt(3.W) + val valid = Bool() + val ready = Flipped(Bool()) + } + class MyOpaqueType extends Record with OpaqueType { + val k = new Decoupled() + val elements = SeqMap("" -> k) + override def cloneType: this.type = (new MyOpaqueType).asInstanceOf[this.type] + } + class MyModule extends RawModule { + val w = Wire(new MyOpaqueType()) + } + + val emitted: String = ChiselStage.emitChirrtl(new MyModule) + + assert( + emitted.contains( + "wire w : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}" + ) + ) + } } diff --git a/src/test/scala/chiselTests/ExtModule.scala b/src/test/scala/chiselTests/ExtModule.scala index b5a8ff7c..3ab4cc32 100644 --- a/src/test/scala/chiselTests/ExtModule.scala +++ b/src/test/scala/chiselTests/ExtModule.scala @@ -88,6 +88,17 @@ class ExtModuleWithFlatIOTester extends Module { io <> inst.badIO } +class ExtModuleInvalidatedTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + val inst = Module(new ExtModule { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + }) + inst.in := in + out := inst.out +} + class ExtModuleSpec extends ChiselFlatSpec { "A ExtModule inverter" should "work" in { assertTesterPasses({ new ExtModuleTester }, Seq("/chisel3/BlackBoxTest.v"), TesterDriver.verilatorOnly) @@ -117,4 +128,10 @@ class ExtModuleSpec extends ChiselFlatSpec { chirrtl should include("inst.in <= io.in") chirrtl shouldNot include("badIO") } + + it should "not have invalidated ports in a chisel3._ context" in { + val chirrtl = ChiselStage.emitChirrtl(new ExtModuleInvalidatedTester) + chirrtl shouldNot include("inst.in is invalid") + chirrtl shouldNot include("inst.out is invalid") + } } diff --git a/src/test/scala/chiselTests/IntervalSpec.scala b/src/test/scala/chiselTests/IntervalSpec.scala index c0338f6d..a2d36579 100644 --- a/src/test/scala/chiselTests/IntervalSpec.scala +++ b/src/test/scala/chiselTests/IntervalSpec.scala @@ -211,7 +211,7 @@ class ClipSqueezeWrapDemo( val wrapped = counter.wrap(0.U.asInterval(targetRange)) when(counter === startValue) { - printf(s"Target range is $range\n") + printf(cf"Target range is $range\n") printf("value clip squeeze wrap\n") } @@ -245,10 +245,7 @@ class SqueezeFunctionalityTester(range: IntervalRange, startNum: BigDecimal, end squeezeTemplate := toSqueeze.squeeze(squeezeInterval) printf( - s"SqueezeTest %d %d.squeeze($range) => %d\n", - counter, - toSqueeze.asSInt(), - squeezeTemplate.asSInt() + cf"SqueezeTest $counter%d ${toSqueeze.asSInt()}%d.squeeze($range) => ${squeezeTemplate.asSInt()}%d\n" ) } diff --git a/src/test/scala/chiselTests/MultiClockSpec.scala b/src/test/scala/chiselTests/MultiClockSpec.scala index 381b4009..29ec6509 100644 --- a/src/test/scala/chiselTests/MultiClockSpec.scala +++ b/src/test/scala/chiselTests/MultiClockSpec.scala @@ -166,14 +166,6 @@ class MultiClockSpec extends ChiselFlatSpec with Utils { } "Differing clocks at memory and read accessor instantiation" should "warn" in { - class modMemReadDifferingClock extends Module { - val myClock = IO(Input(Clock())) - val mem = withClock(myClock) { Mem(4, UInt(8.W)) } - val readVal = mem.read(0.U) - } - val (logMemReadDifferingClock, _) = grabLog(ChiselStage.elaborate(new modMemReadDifferingClock)) - logMemReadDifferingClock should include("memory is different") - class modSyncReadMemReadDifferingClock extends Module { val myClock = IO(Input(Clock())) val mem = withClock(myClock) { SyncReadMem(4, UInt(8.W)) } diff --git a/src/test/scala/chiselTests/Printf.scala b/src/test/scala/chiselTests/Printf.scala index 4171f97f..6c9f05f0 100644 --- a/src/test/scala/chiselTests/Printf.scala +++ b/src/test/scala/chiselTests/Printf.scala @@ -4,6 +4,7 @@ package chiselTests import chisel3._ import chisel3.testers.BasicTester +import chisel3.stage.ChiselStage class SinglePrintfTester() extends BasicTester { val x = 254.U @@ -28,6 +29,38 @@ class ASCIIPrintableTester extends BasicTester { stop() } +class ScopeTesterModule extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := in + + val w = Wire(UInt(8.W)) + w := 125.U + + val p = cf"$in" + val wp = cf"$w" +} + +class PrintablePrintfScopeTester extends BasicTester { + ChiselStage.elaborate { + new Module { + val mod = Module(new ScopeTesterModule) + printf(mod.p) + } + } + stop() +} + +class PrintablePrintfWireScopeTester extends BasicTester { + ChiselStage.elaborate { + new Module { + val mod = Module(new ScopeTesterModule) + printf(mod.wp) + } + } + stop() +} + class PrintfSpec extends ChiselFlatSpec { "A printf with a single argument" should "run" in { assertTesterPasses { new SinglePrintfTester } @@ -41,4 +74,10 @@ class PrintfSpec extends ChiselFlatSpec { "A printf with Printable ASCII characters 1-127" should "run" in { assertTesterPasses { new ASCIIPrintableTester } } + "A printf with Printable" should "respect port scopes" in { + assertTesterPasses { new PrintablePrintfScopeTester } + } + "A printf with Printable" should "respect wire scopes" in { + a[ChiselException] should be thrownBy { assertTesterPasses { new PrintablePrintfWireScopeTester } } + } } diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 30b55812..3414ec8a 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -6,7 +6,7 @@ import chisel3._ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.util.{Counter, Queue} -import chisel3.experimental.DataMirror +import chisel3.experimental.{DataMirror, OpaqueType} import scala.collection.immutable.SeqMap @@ -108,6 +108,123 @@ trait RecordSpecUtils { require(DataMirror.checkTypeEquivalence(wire0, wire1)) require(!DataMirror.checkTypeEquivalence(wire1, wire2)) } + + class SingleElementRecord extends Record with OpaqueType { + private val underlying = UInt(8.W) + val elements = SeqMap("" -> underlying) + override def cloneType: this.type = (new SingleElementRecord).asInstanceOf[this.type] + + def +(that: SingleElementRecord): SingleElementRecord = { + val _w = Wire(new SingleElementRecord) + _w.underlying := this.underlying + that.underlying + _w + } + } + + class SingleElementRecordModule extends Module { + val in1 = IO(Input(new SingleElementRecord)) + val in2 = IO(Input(new SingleElementRecord)) + val out = IO(Output(new SingleElementRecord)) + + val r = new SingleElementRecord + + out := in1 + in2 + } + + class InnerRecord extends Record with OpaqueType { + val k = new InnerInnerRecord + val elements = SeqMap("" -> k) + override def cloneType: this.type = (new InnerRecord).asInstanceOf[this.type] + } + + class InnerInnerRecord extends Record with OpaqueType { + val k = new SingleElementRecord + val elements = SeqMap("" -> k) + override def cloneType: this.type = (new InnerInnerRecord).asInstanceOf[this.type] + } + + class NestedRecordModule extends Module { + val in = IO(Input(new InnerRecord)) + val out = IO(Output(new InnerRecord)) + val inst = Module(new InnerModule) + inst.io.foo := in + out := inst.io.bar + } + class InnerModule extends Module { + val io = IO(new Bundle { + val foo = Input(new InnerRecord) + val bar = Output(new InnerRecord) + }) + + // DO NOT do this; just for testing element connections + io.bar.elements.head._2 := io.foo.elements.head._2 + } + + class NamedSingleElementRecord extends Record with OpaqueType { + private val underlying = UInt(8.W) + val elements = SeqMap("unused" -> underlying) + + override def cloneType: this.type = (new NamedSingleElementRecord).asInstanceOf[this.type] + } + + class NamedSingleElementModule extends Module { + val in = IO(Input(new NamedSingleElementRecord)) + val out = IO(Output(new NamedSingleElementRecord)) + out := in + } + + class ErroneousOverride extends Record with OpaqueType { + private val underlyingA = UInt(8.W) + private val underlyingB = UInt(8.W) + val elements = SeqMap("x" -> underlyingA, "y" -> underlyingB) + + override def opaqueType = true + override def cloneType: this.type = (new ErroneousOverride).asInstanceOf[this.type] + } + + class ErroneousOverrideModule extends Module { + val in = IO(Input(new ErroneousOverride)) + val out = IO(Output(new ErroneousOverride)) + out := in + } + + class NotActuallyOpaqueType extends Record with OpaqueType { + private val underlyingA = UInt(8.W) + private val underlyingB = UInt(8.W) + val elements = SeqMap("x" -> underlyingA, "y" -> underlyingB) + + override def opaqueType = false + override def cloneType: this.type = (new NotActuallyOpaqueType).asInstanceOf[this.type] + } + + class NotActuallyOpaqueTypeModule extends Module { + val in = IO(Input(new NotActuallyOpaqueType)) + val out = IO(Output(new NotActuallyOpaqueType)) + out := in + } + + // Illustrate how to dyanmically decide between OpaqueType or not + sealed trait MaybeBoxed[T <: Data] extends Record { + def underlying: T + def boxed: Boolean + } + object MaybeBoxed { + def apply[T <: Data](gen: T, boxed: Boolean): MaybeBoxed[T] = { + if (boxed) new Boxed(gen) else new Unboxed(gen) + } + } + class Boxed[T <: Data](gen: T) extends MaybeBoxed[T] { + def boxed = true + lazy val elements = SeqMap("underlying" -> gen.cloneType) + def underlying = elements.head._2 + override def cloneType: this.type = (new Boxed(gen)).asInstanceOf[this.type] + } + class Unboxed[T <: Data](gen: T) extends MaybeBoxed[T] with OpaqueType { + def boxed = false + lazy val elements = SeqMap("" -> gen.cloneType) + def underlying = elements.head._2 + override def cloneType: this.type = (new Unboxed(gen)).asInstanceOf[this.type] + } } class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { @@ -133,7 +250,7 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { class AliasedFieldRecord extends Record { val foo = UInt(8.W) val elements = SeqMap("foo" -> foo, "bar" -> foo) - override def cloneType: AliasedFieldRecord.this.type = this + override def cloneType: AliasedFieldRecord.this.type = (new AliasedFieldRecord).asInstanceOf[this.type] } val e = intercept[AliasedAggregateFieldException] { @@ -146,6 +263,85 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { e.getMessage should include("contains aliased fields named (bar,foo)") } + they should "support OpaqueType for maps with single unnamed elements" in { + val singleElementChirrtl = ChiselStage.emitChirrtl { new SingleElementRecordModule } + singleElementChirrtl should include("input in1 : UInt<8>") + singleElementChirrtl should include("input in2 : UInt<8>") + singleElementChirrtl should include("add(in1, in2)") + } + + they should "work correctly for toTarget in nested OpaqueType Records" in { + var mod: NestedRecordModule = null + ChiselStage.elaborate { mod = new NestedRecordModule; mod } + val testStrings = Seq( + mod.inst.io.foo.toTarget.serialize, + mod.inst.io.foo.k.toTarget.serialize, + mod.inst.io.foo.k.k.toTarget.serialize, + mod.inst.io.foo.elements.head._2.toTarget.serialize, + mod.inst.io.foo.k.elements.head._2.toTarget.serialize, + mod.inst.io.foo.k.k.elements.head._2.toTarget.serialize + ) + testStrings.foreach(x => assert(x == "~NestedRecordModule|InnerModule>io.foo")) + } + + they should "work correctly when connecting nested OpaqueType elements" in { + val nestedRecordChirrtl = ChiselStage.emitChirrtl { new NestedRecordModule } + nestedRecordChirrtl should include("input in : UInt<8>") + nestedRecordChirrtl should include("output out : UInt<8>") + nestedRecordChirrtl should include("inst.io.foo <= in") + nestedRecordChirrtl should include("out <= inst.io.bar") + nestedRecordChirrtl should include("output io : { flip foo : UInt<8>, bar : UInt<8>}") + nestedRecordChirrtl should include("io.bar <= io.foo") + } + + they should "throw an error when map contains a named element and OpaqueType is mixed in" in { + (the[Exception] thrownBy extractCause[Exception] { + ChiselStage.elaborate { new NamedSingleElementModule } + }).getMessage should include("Opaque types must have exactly one element with an empty name") + } + + they should "throw an error when map contains more than one element and OpaqueType is mixed in" in { + (the[Exception] thrownBy extractCause[Exception] { + ChiselStage.elaborate { new ErroneousOverrideModule } + }).getMessage should include("Opaque types must have exactly one element with an empty name") + } + + they should "work correctly when an OpaqueType overrides the def as false" in { + val chirrtl = ChiselStage.emitChirrtl(new NotActuallyOpaqueTypeModule) + chirrtl should include("input in : { y : UInt<8>, x : UInt<8>}") + chirrtl should include("output out : { y : UInt<8>, x : UInt<8>}") + chirrtl should include("out <= in") + } + + they should "support conditional OpaqueTypes via traits and factory methods" in { + class MyModule extends Module { + val in0 = IO(Input(MaybeBoxed(UInt(8.W), true))) + val out0 = IO(Output(MaybeBoxed(UInt(8.W), true))) + val in1 = IO(Input(MaybeBoxed(UInt(8.W), false))) + val out1 = IO(Output(MaybeBoxed(UInt(8.W), false))) + out0 := in0 + out1 := in1 + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("input in0 : { underlying : UInt<8>}") + chirrtl should include("input in1 : UInt<8>") + } + + they should "work with .toTarget" in { + var m: SingleElementRecordModule = null + ChiselStage.elaborate { m = new SingleElementRecordModule; m } + val q = m.in1.toTarget.toString + assert(q == "~SingleElementRecordModule|SingleElementRecordModule>in1") + } + + they should "work (but warn) with .toTarget on non-data OpaqueType Record" in { + var m: SingleElementRecordModule = null + ChiselStage.elaborate { m = new SingleElementRecordModule; m } + val (log, q) = grabLog(m.r.toTarget) + log should include(".toTarget of non-hardware Data is deprecated") + assert(q.toString == "~SingleElementRecordModule|SingleElementRecordModule>r") + } + they should "follow UInt serialization/deserialization API" in { assertTesterPasses { new RecordSerializationTest } } @@ -184,4 +380,17 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { "CustomBundle" should "check the types" in { ChiselStage.elaborate { new RecordTypeTester } } + + "Record with unstable elements" should "error" in { + class MyRecord extends Record { + def elements = SeqMap("a" -> UInt(8.W)) + override def cloneType: this.type = (new MyRecord).asInstanceOf[this.type] + } + val e = the[ChiselException] thrownBy { + ChiselStage.elaborate(new Module { + val io = IO(Input(new MyRecord)) + }) + } + e.getMessage should include("does not return the same objects when calling .elements multiple times") + } } diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala index 44ed77f9..e9f412fe 100644 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ b/src/test/scala/chiselTests/StrongEnum.scala @@ -5,6 +5,7 @@ package chiselTests import chisel3._ import chisel3.experimental.ChiselEnum import chisel3.experimental.AffectsChiselPrefix +import chisel3.experimental.suppressEnumCastWarning import chisel3.internal.firrtl.UnknownWidth import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util._ @@ -163,6 +164,27 @@ class StrongEnumFSM extends Module { } } +object Opcode extends ChiselEnum { + val load = Value(0x03.U) + val imm = Value(0x13.U) + val auipc = Value(0x17.U) + val store = Value(0x23.U) + val reg = Value(0x33.U) + val lui = Value(0x37.U) + val br = Value(0x63.U) + val jalr = Value(0x67.U) + val jal = Value(0x6f.U) +} + +class LoadStoreExample extends Module { + val io = IO(new Bundle { + val opcode = Input(Opcode()) + val load_or_store = Output(Bool()) + }) + io.load_or_store := io.opcode.isOneOf(Opcode.load, Opcode.store) + printf(p"${io.opcode}") +} + class CastToUIntTester extends BasicTester { for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { val mod = Module(new CastToUInt) @@ -481,6 +503,46 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils { (log should not).include("warn") } + it should "suppress warning using suppressEnumCastWarning" in { + object TestEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(TestEnum())) + suppressEnumCastWarning { + val res = TestEnum(in) + out := res + } + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + (log should not).include("warn") + } + + it should "suppress exactly one warning using suppressEnumCastWarning" in { + object TestEnum1 extends ChiselEnum { + val e0, e1, e2 = Value + } + object TestEnum2 extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out1 = IO(Output(TestEnum1())) + val out2 = IO(Output(TestEnum2())) + suppressEnumCastWarning { + out1 := TestEnum1(in) + } + out2 := TestEnum2(in) + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + log should include("warn") + log should include("TestEnum2") // not suppressed + (log should not).include("TestEnum1") // suppressed + } + "Casting a UInt to an Enum with .safe" should "NOT warn" in { object MyEnum extends ChiselEnum { val e0, e1, e2 = Value @@ -514,6 +576,12 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils { it should "correctly check if the enumeration is one of the values in a given sequence" in { assertTesterPasses(new IsOneOfTester) } + + it should "work with Printables" in { + ChiselStage.emitChirrtl(new LoadStoreExample) should include( + """printf(clock, UInt<1>("h1"), "%c%c%c%c%c", _chiselTestsOpcodePrintable[0], _chiselTestsOpcodePrintable[1], _chiselTestsOpcodePrintable[2], _chiselTestsOpcodePrintable[3], _chiselTestsOpcodePrintable[4])""" + ) + } } class StrongEnumAnnotator extends Module { diff --git a/src/test/scala/chiselTests/ToTargetSpec.scala b/src/test/scala/chiselTests/ToTargetSpec.scala index dc4ec448..de46cdcb 100644 --- a/src/test/scala/chiselTests/ToTargetSpec.scala +++ b/src/test/scala/chiselTests/ToTargetSpec.scala @@ -49,4 +49,21 @@ class ToTargetSpec extends ChiselFlatSpec with Utils { val q = m.q.toTarget.toString assert(q == s"~$mn|Queue") } + + it should "warn on non-hardware types and provide information" in { + class Example extends Module { + val tpe = UInt(8.W) + + val in = IO(Input(tpe)) + val out = IO(Output(tpe)) + out := in + } + + var e: Example = null + chisel3.stage.ChiselStage.elaborate { e = new Example; e } + val (log, foo) = grabLog(e.tpe.toTarget) + log should include( + "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated: 'tpe', in module 'Example'" + ) + } } diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala index 02743187..e46774dd 100644 --- a/src/test/scala/chiselTests/Vec.scala +++ b/src/test/scala/chiselTests/Vec.scala @@ -6,7 +6,7 @@ import org.scalacheck._ import chisel3._ import chisel3.stage.ChiselStage -import chisel3.testers.BasicTester +import chisel3.testers.{BasicTester, TesterDriver} import chisel3.util._ import org.scalacheck.Shrink import scala.annotation.tailrec @@ -111,7 +111,7 @@ class FillTester(n: Int, value: Int) extends BasicTester { val x = VecInit(Array.fill(n)(value.U)) val u = VecInit.fill(n)(value.U) - assert(x.asUInt() === u.asUInt(), s"Expected Vec to be filled like $x, instead VecInit.fill created $u") + assert(x.asUInt() === u.asUInt(), cf"Expected Vec to be filled like $x, instead VecInit.fill created $u") stop() } @@ -235,7 +235,7 @@ class IterateTester(start: Int, len: Int)(f: UInt => UInt) extends BasicTester { val testVec = VecInit.iterate(start.U, len)(f) assert( controlVec.asUInt() === testVec.asUInt(), - s"Expected Vec to be filled like $controlVec, instead creaeted $testVec\n" + cf"Expected Vec to be filled like $controlVec, instead created $testVec\n" ) stop() } @@ -456,7 +456,7 @@ class VecSpec extends ChiselPropSpec with Utils { } property("Infering widths on huge Vecs should not cause a stack overflow") { - assertTesterPasses { new HugeVecTester(10000) } + assertTesterPasses(new HugeVecTester(10000), annotations = TesterDriver.verilatorOnly) } property("A Reg of a Vec of a single 1 bit element should compile and work") { diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala index fa97a8c8..e2eb791d 100644 --- a/src/test/scala/chiselTests/VecLiteralSpec.scala +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -205,7 +205,7 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { assertTesterPasses { new BasicTester { - chisel3.assert(outsideVecLit(0) === 0xdd.U, s"v(0)") + chisel3.assert(outsideVecLit(0) === 0xdd.U, "v(0)") stop() } } @@ -216,7 +216,7 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { assertTesterPasses { new BasicTester { - chisel3.assert(outsideVecLit(0) === 0xdd.U, s"v(0)") + chisel3.assert(outsideVecLit(0) === 0xdd.U, "v(0)") chisel3.assert(outsideVecLit(1) === 0xcc.U) chisel3.assert(outsideVecLit(2) === 0xbb.U) chisel3.assert(outsideVecLit(3) === 0xaa.U) diff --git a/src/test/scala/chiselTests/WarningSpec.scala b/src/test/scala/chiselTests/WarningSpec.scala new file mode 100644 index 00000000..1cef1ffc --- /dev/null +++ b/src/test/scala/chiselTests/WarningSpec.scala @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.util._ +import chisel3.stage.ChiselStage +import chisel3.experimental.ChiselEnum +import chisel3.experimental.EnumType + +class WarningSpec extends ChiselFlatSpec with Utils { + behavior.of("Warnings") + + object MyEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out1 = IO(Output(MyEnum())) + val out2 = IO(Output(MyEnum())) + def func(out: EnumType): Unit = { + out := MyEnum(in) + } + func(out1) + func(out2) + } + + "Warnings" should "be de-duplicated" in { + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + def countSubstring(s: String, sub: String) = + s.sliding(sub.length).count(_ == sub) + countSubstring(log, "Casting non-literal UInt") should be(1) + } + + "Warnings" should "be treated as errors with warningsAsErrors" in { + a[ChiselException] should be thrownBy extractCause[ChiselException] { + val args = Array("--warnings-as-errors") + (new ChiselStage).emitChirrtl(new MyModule, args) + } + } +} diff --git a/src/test/scala/chiselTests/WireSpec.scala b/src/test/scala/chiselTests/WireSpec.scala index 11a1f1a1..058a7f08 100644 --- a/src/test/scala/chiselTests/WireSpec.scala +++ b/src/test/scala/chiselTests/WireSpec.scala @@ -3,6 +3,7 @@ package chiselTests import chisel3._ +import chisel3.stage.ChiselStage class WireSpec extends ChiselFlatSpec { "WireDefault.apply" should "work" in { @@ -17,4 +18,19 @@ class WireSpec extends ChiselFlatSpec { it should "not allow init argument to affect type inference" in { assertDoesNotCompile("val x: UInt = WireDefault(UInt(4.W), 2.S)") } + it should "have source locator information on wires" in { + class Dummy extends chisel3.Module { + val in = IO(Input(Bool())) + val out = IO(Output(Bool())) + + val wire = WireInit(Bool(), true.B) + val wire2 = Wire(Bool()) + wire2 := in + out := in & wire & wire2 + } + + val chirrtl = ChiselStage.emitChirrtl(new Dummy) + chirrtl should include("wire wire : UInt<1> @[WireSpec.scala") + chirrtl should include("wire wire2 : UInt<1> @[WireSpec.scala") + } } diff --git a/src/test/scala/chiselTests/aop/InjectionSpec.scala b/src/test/scala/chiselTests/aop/InjectionSpec.scala index 9b29b0ba..1b69efa3 100644 --- a/src/test/scala/chiselTests/aop/InjectionSpec.scala +++ b/src/test/scala/chiselTests/aop/InjectionSpec.scala @@ -108,6 +108,7 @@ class InjectionSpec extends ChiselFlatSpec with Utils { { _: SubmoduleManipulationTester => // By creating a second SubmoduleA, the module names would conflict unless they were uniquified val moduleSubmoduleC = Module(new SubmoduleC) + moduleSubmoduleC.io <> DontCare //if we're here then we've elaborated correctly stop() } diff --git a/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala b/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala index 731596ec..09fdf3c4 100644 --- a/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala +++ b/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala @@ -8,7 +8,26 @@ import chisel3.stage.ChiselStage import chisel3.experimental.DataMirror import chiselTests.ChiselFlatSpec +object DataMirrorSpec { + import org.scalatest.matchers.should.Matchers._ + class GrandChild(parent: RawModule) extends Module { + DataMirror.getParent(this) should be(Some(parent)) + } + class Child(parent: RawModule) extends Module { + val inst = Module(new GrandChild(this)) + DataMirror.getParent(inst) should be(Some(this)) + DataMirror.getParent(this) should be(Some(parent)) + } + class Parent extends Module { + val inst = Module(new Child(this)) + DataMirror.getParent(inst) should be(Some(this)) + DataMirror.getParent(this) should be(None) + } +} + class DataMirrorSpec extends ChiselFlatSpec { + import DataMirrorSpec._ + behavior.of("DataMirror") def assertBinding(x: Data, io: Boolean, wire: Boolean, reg: Boolean) = { @@ -55,4 +74,18 @@ class DataMirrorSpec extends ChiselFlatSpec { } ChiselStage.elaborate(new MyModule) } + + it should "support getParent for normal modules" in { + ChiselStage.elaborate(new Parent) + } + + it should "support getParent for normal modules even when used in a D/I context" in { + import chisel3.experimental.hierarchy._ + class Top extends Module { + val defn = Definition(new Parent) + val inst = Instance(defn) + DataMirror.getParent(this) should be(None) + } + ChiselStage.elaborate(new Top) + } } diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index ac8357f0..cefc893c 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -7,7 +7,7 @@ import chisel3._ import chisel3.experimental.dataview._ import chisel3.experimental.conversions._ import chisel3.experimental.DataMirror.internal.chiselTypeClone -import chisel3.experimental.HWTuple2 +import chisel3.experimental.{Analog, HWTuple2} import chisel3.stage.ChiselStage import chisel3.util.{Decoupled, DecoupledIO} @@ -91,6 +91,16 @@ class DataViewSpec extends ChiselFlatSpec { chirrtl should include("bar <= in") } + it should "handle viewing Analogs as Analogs" in { + class MyModule extends Module { + val foo = IO(Analog(8.W)) + val bar = IO(Analog(8.W)) + foo <> bar.viewAs[Analog] + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("attach (foo, bar)") + } + it should "handle viewing Bundles as their same concrete type" in { class MyBundle extends Bundle { val foo = UInt(8.W) @@ -177,6 +187,28 @@ class DataViewSpec extends ChiselFlatSpec { chirrtl should include("fooOut.foo <= barIn.foo") } + it should "be easy to make a PartialDataView viewing a Bundle as a Parent Bundle type" in { + class Foo(x: Int) extends Bundle { + val foo = UInt(x.W) + } + class Bar(val x: Int) extends Foo(x) { + val bar = UInt(x.W) + } + implicit val view = PartialDataView.supertype[Bar, Foo](b => new Foo(b.x)) + class MyModule extends Module { + val fooIn = IO(Input(new Foo(8))) + val barOut = IO(Output(new Bar(8))) + barOut.viewAs[Foo] := fooIn + + val barIn = IO(Input(new Bar(8))) + val fooOut = IO(Output(new Foo(8))) + fooOut := barIn.viewAs[Foo] + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("barOut.foo <= fooIn.foo") + chirrtl should include("fooOut.foo <= barIn.foo") + } + it should "error if viewing a parent Bundle as a child Bundle type" in { assertTypeError(""" class Foo extends Bundle { diff --git a/src/test/scala/chiselTests/experimental/FlatIOSpec.scala b/src/test/scala/chiselTests/experimental/FlatIOSpec.scala index dfce447f..ebb7cbdb 100644 --- a/src/test/scala/chiselTests/experimental/FlatIOSpec.scala +++ b/src/test/scala/chiselTests/experimental/FlatIOSpec.scala @@ -5,7 +5,7 @@ package chiselTests.experimental import chisel3._ import chisel3.util.Valid import chisel3.stage.ChiselStage.emitChirrtl -import chisel3.experimental.FlatIO +import chisel3.experimental.{Analog, FlatIO} import chiselTests.ChiselFlatSpec class FlatIOSpec extends ChiselFlatSpec { @@ -48,4 +48,19 @@ class FlatIOSpec extends ChiselFlatSpec { val chirrtl = emitChirrtl(new MyModule) chirrtl should include("out[addr] <= in[addr]") } + + it should "support Analog members" in { + class MyBundle extends Bundle { + val foo = Output(UInt(8.W)) + val bar = Analog(8.W) + } + class MyModule extends RawModule { + val in = IO(Flipped(new MyBundle)) + val out = IO(new MyBundle) + out <> in + } + val chirrtl = emitChirrtl(new MyModule) + chirrtl should include("out.foo <= in.foo") + chirrtl should include("attach (out.bar, in.bar)") + } } diff --git a/src/test/scala/chiselTests/experimental/ForceNames.scala b/src/test/scala/chiselTests/experimental/ForceNames.scala index 233b4a5f..9ba825c4 100644 --- a/src/test/scala/chiselTests/experimental/ForceNames.scala +++ b/src/test/scala/chiselTests/experimental/ForceNames.scala @@ -59,7 +59,7 @@ object ForceNamesHierarchy { } } -class ForceNamesSpec extends ChiselFlatSpec { +class ForceNamesSpec extends ChiselFlatSpec with Utils { def run[T <: RawModule]( dut: => T, @@ -110,4 +110,19 @@ class ForceNamesSpec extends ChiselFlatSpec { ) } } + + "Force Name of non-hardware value" should "warn" in { + class Example extends Module { + val tpe = UInt(8.W) + forceName(tpe, "foobar") + + val in = IO(Input(tpe)) + val out = IO(Output(tpe)) + out := in + } + + val (log, foo) = grabLog(chisel3.stage.ChiselStage.elaborate(new Example)) + log should include("deprecated") + log should include("Using forceName 'foobar' on non-hardware value UInt<8>") + } } diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala index fa26cbde..27725c49 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala @@ -271,4 +271,70 @@ object Examples { @public val mem = Mem(8, UInt(32.W)) @public val syncReadMem = SyncReadMem(8, UInt(32.W)) } + + @instantiable + class LeafInstantiable(val bundle: Data) { + @public val bundle = bundle + } + + @instantiable + class NestedInstantiable(val in: LeafInstantiable, val out: LeafInstantiable) { + @public val in = in + @public val out = out + } + + @instantiable + class AddOneNestedInstantiableData(width: Int) extends Module { + @public val in = IO(Input(UInt(width.W))) + @public val out = IO(Output(UInt(width.W))) + out := in + 1.U + + @public val leafOut = new LeafInstantiable(out) + @public val leafIn = new LeafInstantiable(in) + @public val nested = new NestedInstantiable(in = leafIn, out = leafOut) + + } + + class AddTwoNestedInstantiableData(width: Int) extends Module { + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt(width.W))) + val addOneDef = Definition(new AddOneNestedInstantiableData(width)) + val i0 = Instance(addOneDef) + val i1 = Instance(addOneDef) + i0.in := in + i1.in := i0.out + out := i1.out + + // both are equivalent to the above + i1.leafIn.bundle := i0.leafOut.bundle + i1.nested.in.bundle := i0.nested.out.bundle + } + + class AddTwoNestedInstantiableDataSubmodule(addOneDef: Definition[AddOneNestedInstantiableData]) extends Module { + val in = IO(Input(UInt(addOneDef.in.getWidth.W))) + val out = IO(Output(UInt(addOneDef.out.getWidth.W))) + val i0 = Instance(addOneDef) + val i1 = Instance(addOneDef) + i0.in := in + i1.in := i0.out + out := i1.out + + // both are equivalent to the above + i1.leafIn.bundle := i0.leafOut.bundle + i1.nested.in.bundle := i0.nested.out.bundle + } + + class AddTwoNestedInstantiableDataWrapper(width: Int) extends Module { + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt(width.W))) + + val original = Module(new AddOneNestedInstantiableData(width)) + val copy = Module(new AddTwoNestedInstantiableDataSubmodule(original.toDefinition)) + + original.in := in + copy.in := original.out + out := copy.out + + } + } diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala index 8d8f7ea5..6596cd51 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala @@ -364,6 +364,16 @@ class InstanceSpec extends ChiselFunSpec with Utils { annos should contain(MarkAnnotation("~Top|Top/i:HasMems>mem".rt, "Mem")) annos should contain(MarkAnnotation("~Top|Top/i:HasMems>syncReadMem".rt, "SyncReadMem")) } + it("(3.p): should make connectable IOs on nested IsInstantiables that have IO Datas in them") { + val (chirrtl, _) = getFirrtlAndAnnos(new AddTwoNestedInstantiableData(4)) + exactly(3, chirrtl.serialize.split('\n')) should include("i1.in <= i0.out") + } + it( + "(3.q): should make connectable IOs on nested IsInstantiables's Data when the Instance and Definition do not have the same parent" + ) { + val (chirrtl, _) = getFirrtlAndAnnos(new AddTwoNestedInstantiableDataWrapper(4)) + exactly(3, chirrtl.serialize.split('\n')) should include("i1.in <= i0.out") + } } describe("4: toInstance") { it("4.0: should work on modules") { diff --git a/src/test/scala/chiselTests/naming/PrefixSpec.scala b/src/test/scala/chiselTests/naming/PrefixSpec.scala index 6d52407e..d8cb3348 100644 --- a/src/test/scala/chiselTests/naming/PrefixSpec.scala +++ b/src/test/scala/chiselTests/naming/PrefixSpec.scala @@ -7,6 +7,7 @@ import chisel3.stage.ChiselStage import chisel3.aop.Select import chisel3.experimental.{dump, noPrefix, prefix, treedump} import chiselTests.{ChiselPropSpec, Utils} +import chisel3.experimental.AffectsChiselPrefix class PrefixSpec extends ChiselPropSpec with Utils { implicit val minimumMajorVersion: Int = 12 @@ -497,4 +498,53 @@ class PrefixSpec extends ChiselPropSpec with Utils { Select.wires(top).map(_.instanceName) should be(List("a_b_c_d")) } } + + property("Prefixing of AffectsChiselPrefix objects should work") { + class NotAData extends AffectsChiselPrefix { + val value = Wire(UInt(3.W)) + } + class NotADataUnprefixed { + val value = Wire(UInt(3.W)) + } + class Test extends Module { + { + val nonData = new NotAData + // Instance name of nonData.value should be nonData_value + nonData.value := RegNext(3.U) + + val nonData2 = new NotADataUnprefixed + // Instance name of nonData2.value should be value + nonData2.value := RegNext(3.U) + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("nonData_value", "value")) + } + } + property("Prefixing should not be affected by repeated calls of suggestName") { + class Test extends Module { + val in = IO(Input(UInt(3.W))) + val prefixed = { + val wire = Wire(UInt(3.W)).suggestName("wire") // "prefixed_wire" + wire := in + + val thisShouldNotBeHere = { + // Second suggestName doesn't modify the instanceName since it was + // already suggested, but also should not modify the prefix either + + // Incorrect behavior would rename the wire to + // "prefixed_thisShouldNotBeHere_wire" + wire.suggestName("wire") + + val out = IO(Output(UInt(3.W))) + out := wire + out + } + thisShouldNotBeHere + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("prefixed_wire")) + } + } } |
