From 98ce9194e5d87fdd5be931b6cd516d180a6540cd Mon Sep 17 00:00:00 2001 From: Albert Magyar Date: Mon, 1 Feb 2021 14:06:39 -0800 Subject: Update reported width from div/rem to match FIRRTL results (#1748) * Update reported width from div/rem to match FIRRTL results * Add tests for width of % and / on UInt and SInt * Add loop-based test for known UInt/SInt op result widths Co-authored-by: Jack Koenig --- src/test/scala/chiselTests/SIntOps.scala | 32 ++++++++++++++++ src/test/scala/chiselTests/UIntOps.scala | 33 +++++++++++++++++ src/test/scala/chiselTests/WidthSpec.scala | 59 ++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/SIntOps.scala b/src/test/scala/chiselTests/SIntOps.scala index 9aacc378..f2e238e9 100644 --- a/src/test/scala/chiselTests/SIntOps.scala +++ b/src/test/scala/chiselTests/SIntOps.scala @@ -116,4 +116,36 @@ class SIntOpsSpec extends ChiselPropSpec with Utils { assertTesterPasses(new SIntLitExtractTester) } + // We use WireDefault with 2 arguments because of + // https://www.chisel-lang.org/api/3.4.1/chisel3/WireDefault$.html + // Single Argument case 2 + property("modulo divide should give min width of arguments") { + assertKnownWidth(4) { + val x = WireDefault(SInt(8.W), DontCare) + val y = WireDefault(SInt(4.W), DontCare) + val op = x % y + WireDefault(chiselTypeOf(op), op) + } + assertKnownWidth(4) { + val x = WireDefault(SInt(4.W), DontCare) + val y = WireDefault(SInt(8.W), DontCare) + val op = x % y + WireDefault(chiselTypeOf(op), op) + } + } + + property("division should give the width of the numerator + 1") { + assertKnownWidth(9) { + val x = WireDefault(SInt(8.W), DontCare) + val y = WireDefault(SInt(4.W), DontCare) + val op = x / y + WireDefault(chiselTypeOf(op), op) + } + assertKnownWidth(5) { + val x = WireDefault(SInt(4.W), DontCare) + val y = WireDefault(SInt(8.W), DontCare) + val op = x / y + WireDefault(chiselTypeOf(op), op) + } + } } diff --git a/src/test/scala/chiselTests/UIntOps.scala b/src/test/scala/chiselTests/UIntOps.scala index bba06d11..62d00de2 100644 --- a/src/test/scala/chiselTests/UIntOps.scala +++ b/src/test/scala/chiselTests/UIntOps.scala @@ -153,4 +153,37 @@ class UIntOpsSpec extends ChiselPropSpec with Matchers with Utils { io.out := io.in.asBools()(2) }) } + + // We use WireDefault with 2 arguments because of + // https://www.chisel-lang.org/api/3.4.1/chisel3/WireDefault$.html + // Single Argument case 2 + property("modulo divide should give min width of arguments") { + assertKnownWidth(4) { + val x = WireDefault(UInt(8.W), DontCare) + val y = WireDefault(UInt(4.W), DontCare) + val op = x % y + WireDefault(chiselTypeOf(op), op) + } + assertKnownWidth(4) { + val x = WireDefault(UInt(4.W), DontCare) + val y = WireDefault(UInt(8.W), DontCare) + val op = x % y + WireDefault(chiselTypeOf(op), op) + } + } + + property("division should give the width of the numerator") { + assertKnownWidth(8) { + val x = WireDefault(UInt(8.W), DontCare) + val y = WireDefault(UInt(4.W), DontCare) + val op = x / y + WireDefault(chiselTypeOf(op), op) + } + assertKnownWidth(4) { + val x = WireDefault(UInt(4.W), DontCare) + val y = WireDefault(UInt(8.W), DontCare) + val op = x / y + WireDefault(chiselTypeOf(op), op) + } + } } diff --git a/src/test/scala/chiselTests/WidthSpec.scala b/src/test/scala/chiselTests/WidthSpec.scala index 2a33c1d6..34159214 100644 --- a/src/test/scala/chiselTests/WidthSpec.scala +++ b/src/test/scala/chiselTests/WidthSpec.scala @@ -186,3 +186,62 @@ class RegInitWidthSpec extends WireDefaultRegInitSpecImpl { def builder2[T <: Data](x: T, y: T): T = RegInit(x, y) } +class OpWidthSpec extends ChiselFlatSpec { + import firrtl._ + import firrtl.ir._ + + val maxWidth = 5 + val uIntOps: Seq[((UInt, UInt) => UInt, PrimOp)] = + Seq( + (_ +& _, PrimOps.Add), + (_ -& _, PrimOps.Sub), + (_ * _, PrimOps.Mul), + (_ / _, PrimOps.Div), + (_ % _, PrimOps.Rem), + (_ << _, PrimOps.Dshl), + (_ >> _, PrimOps.Dshr) + ) + + assertTesterPasses(new chisel3.testers.BasicTester { + for (i <- 0 to maxWidth) { + for (j <- 0 to maxWidth) { + for ((cOp, fOp) <- uIntOps) { + val args = Seq(i, j).map(w => Reference("", UIntType(IntWidth(w)))) + fOp.propagateType(DoPrim(fOp, args, Nil, UnknownType)) match { + case UIntType(IntWidth(w)) => + val x = 0.U(maxWidth.W).head(i) + val y = 0.U(maxWidth.W).head(j) + assert(w == cOp(x, y).getWidth) + } + } + } + } + stop() + }) + + val sIntOps: Seq[((SInt, SInt) => SInt, PrimOp)] = + Seq( + (_ +& _, PrimOps.Add), + (_ -& _, PrimOps.Sub), + (_ * _, PrimOps.Mul), + (_ / _, PrimOps.Div), + (_ % _, PrimOps.Rem) + ) + + assertTesterPasses(new chisel3.testers.BasicTester { + for (i <- 0 to maxWidth) { + for (j <- 0 to maxWidth) { + for ((cOp, fOp) <- sIntOps) { + val args = Seq(i, j).map(w => Reference("", SIntType(IntWidth(w)))) + fOp.propagateType(DoPrim(fOp, args, Nil, UnknownType)) match { + case SIntType(IntWidth(w)) => + val x = 0.U(maxWidth.W).head(i).asSInt + val y = 0.U(maxWidth.W).head(j).asSInt + assert(w == cOp(x, y).getWidth) + } + } + } + } + stop() + }) +} -- cgit v1.2.3 From f45216effc573d33d4aa4e525cff955ab332efbd Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 4 Feb 2021 00:36:12 +0000 Subject: Remove Deprecated APIs (#1730) --- src/test/scala/chiselTests/AsyncResetSpec.scala | 3 +- src/test/scala/chiselTests/CompatibilitySpec.scala | 56 ------------ src/test/scala/chiselTests/DriverSpec.scala | 101 --------------------- .../chiselTests/experimental/ForceNames.scala | 2 +- 4 files changed, 2 insertions(+), 160 deletions(-) delete mode 100644 src/test/scala/chiselTests/DriverSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/AsyncResetSpec.scala b/src/test/scala/chiselTests/AsyncResetSpec.scala index a8e62fe8..d49f390c 100644 --- a/src/test/scala/chiselTests/AsyncResetSpec.scala +++ b/src/test/scala/chiselTests/AsyncResetSpec.scala @@ -209,7 +209,6 @@ class AsyncResetSpec extends ChiselFlatSpec with Utils { } it should "support Fixed regs" in { - import chisel3.experimental.{withReset => _, _} assertTesterPasses(new BasicTester { val reg = withReset(reset.asAsyncReset)(RegNext(-6.0.F(2.BP), 3.F(2.BP))) val (count, done) = Counter(true.B, 4) @@ -223,7 +222,7 @@ class AsyncResetSpec extends ChiselFlatSpec with Utils { } it should "support Interval regs" in { - import chisel3.experimental.{withReset => _, _} + import chisel3.experimental._ assertTesterPasses(new BasicTester { val reg = withReset(reset.asAsyncReset) { val x = RegInit(Interval(range"[0,13]"), 13.I) diff --git a/src/test/scala/chiselTests/CompatibilitySpec.scala b/src/test/scala/chiselTests/CompatibilitySpec.scala index 6a77c821..c7a68e7c 100644 --- a/src/test/scala/chiselTests/CompatibilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilitySpec.scala @@ -103,7 +103,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck Reverse(wire) shouldBe a [UInt] Cat(wire, wire) shouldBe a [UInt] Log2(wire) shouldBe a [UInt] - unless(Bool(false)) {} // 'switch' and 'is' are tested below in Risc Counter(2) shouldBe a [Counter] DecoupledIO(wire) shouldBe a [DecoupledIO[UInt]] @@ -353,13 +352,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck info("Deprecated method DC hasn't been removed") val bp = BitPat.DC(4) - - info("BitPat != UInt is a Bool") - (bp != UInt(4)) shouldBe a [Bool] - - /* This test does not work, but I'm not sure it's supposed to? It does *not* work on chisel3. */ - // info("UInt != BitPat is a Bool") - // (UInt(4) != bp) shouldBe a [Bool] } ChiselStage.elaborate(new Foo) @@ -474,22 +466,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck behavior of "Data methods" - it should "support legacy methods" in { - class Foo extends Module { - val io = IO(new Bundle{}) - - info("litArg works") - UInt(width=3).litArg() should be (None) - UInt(0, width=3).litArg() should be (Some(chisel3.internal.firrtl.ULit(0, 3.W))) - - info("toBits works") - val wire = Wire(UInt(width=4)) - Vec.fill(4)(wire).toBits.getWidth should be (wire.getWidth * 4) - } - - ChiselStage.elaborate(new Foo) - } - behavior of "Wire" it should "support legacy methods" in { @@ -542,9 +518,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck val u = UInt(8) val s = SInt(-4) - info("toBools works") - u.toBools shouldBe a [Seq[Bool]] - info("asBits works") s.asBits shouldBe a [Bits] @@ -553,35 +526,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck info("toUInt works") s.toUInt shouldBe a [UInt] - - info("toBool works") - UInt(1).toBool shouldBe a [Bool] - } - - ChiselStage.elaborate(new Foo) - } - - behavior of "UInt" - - it should "support legacy methods" in { - class Foo extends Module { - val io = new Bundle{} - - info("!= works") - (UInt(1) != UInt(1)) shouldBe a [Bool] - } - - ChiselStage.elaborate(new Foo) - } - - behavior of "SInt" - - it should "support legacy methods" in { - class Foo extends Module { - val io = new Bundle{} - - info("!= works") - (SInt(-1) != SInt(-1)) shouldBe a [Bool] } ChiselStage.elaborate(new Foo) diff --git a/src/test/scala/chiselTests/DriverSpec.scala b/src/test/scala/chiselTests/DriverSpec.scala deleted file mode 100644 index 3a78683b..00000000 --- a/src/test/scala/chiselTests/DriverSpec.scala +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chiselTests - -import java.io.File - -import chisel3._ -import firrtl.FirrtlExecutionSuccess -import org.scalacheck.Test.Failed -import org.scalatest.Succeeded -import org.scalatest.freespec.AnyFreeSpec -import org.scalatest.matchers.should.Matchers - -class DummyModule extends Module { - val io = IO(new Bundle { - val in = Input(UInt(1.W)) - val out = Output(UInt(1.W)) - }) - io.out := io.in -} - -class TypeErrorModule extends chisel3.Module { - val in = IO(Input(UInt(1.W))) - val out = IO(Output(SInt(1.W))) - out := in -} - -class DriverSpec extends AnyFreeSpec with Matchers with chiselTests.Utils { - "Driver's execute methods are used to run chisel and firrtl" - { - "options can be picked up from comand line with no args" in { - // NOTE: Since we don't provide any arguments (notably, "--target-dir"), - // the generated files will be created in the current directory. - val targetDir = "." - Driver.execute(Array.empty[String], () => new DummyModule) match { - case ChiselExecutionSuccess(_, _, Some(_: FirrtlExecutionSuccess)) => - val exts = List("anno.json", "fir", "v") - for (ext <- exts) { - val dummyOutput = new File(targetDir, "DummyModule" + "." + ext) - info(s"${dummyOutput.toString} exists") - dummyOutput.exists() should be(true) - dummyOutput.delete() - } - Succeeded - case _ => - Failed - } - } - - "options can be picked up from comand line setting top name" in { - val targetDir = "local-build" - Driver.execute(Array("-tn", "dm", "-td", targetDir), () => new DummyModule) match { - case ChiselExecutionSuccess(_, _, Some(_: FirrtlExecutionSuccess)) => - val exts = List("anno.json", "fir", "v") - for (ext <- exts) { - val dummyOutput = new File(targetDir, "dm" + "." + ext) - info(s"${dummyOutput.toString} exists") - dummyOutput.exists() should be(true) - dummyOutput.delete() - } - Succeeded - case _ => - Failed - } - - } - - "execute returns a chisel execution result" in { - val targetDir = "test_run_dir" - val args = Array("--compiler", "low", "--target-dir", targetDir) - - info("Driver returned a ChiselExecutionSuccess") - val result = Driver.execute(args, () => new DummyModule) - result shouldBe a[ChiselExecutionSuccess] - - info("emitted circuit included 'circuit DummyModule'") - val successResult = result.asInstanceOf[ChiselExecutionSuccess] - successResult.emitted should include ("circuit DummyModule") - - val dummyOutput = new File(targetDir, "DummyModule.lo.fir") - info(s"${dummyOutput.toString} exists") - dummyOutput.exists() should be(true) - dummyOutput.delete() - } - - "user errors show a trimmed stack trace" in { - val targetDir = "test_run_dir" - val args = Array("--compiler", "low", "--target-dir", targetDir) - - val (stdout, stderr, result) = grabStdOutErr { Driver.execute(args, () => new TypeErrorModule) } - - info("stdout shows a trimmed stack trace") - stdout should include ("Stack trace trimmed to user code only") - - info("stdout does not include FIRRTL information") - stdout should not include ("firrtl.") - - info("Driver returned a ChiselExecutionFailure") - result shouldBe a [ChiselExecutionFailure] - } - } -} diff --git a/src/test/scala/chiselTests/experimental/ForceNames.scala b/src/test/scala/chiselTests/experimental/ForceNames.scala index b3534f11..06f911e6 100644 --- a/src/test/scala/chiselTests/experimental/ForceNames.scala +++ b/src/test/scala/chiselTests/experimental/ForceNames.scala @@ -4,7 +4,7 @@ package chiselTests import firrtl._ import chisel3._ -import chisel3.core.annotate +import chisel3.experimental.annotate import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util.experimental.{ForceNameAnnotation, ForceNamesTransform, InlineInstance, forceName} import firrtl.annotations.{Annotation, ReferenceTarget} -- cgit v1.2.3 From 2ed343e2305b7c22000f3f46fa81d73a369907eb Mon Sep 17 00:00:00 2001 From: Vladimir Milovanović Date: Mon, 8 Feb 2021 20:55:48 +0100 Subject: Parametrized Mem- & SyncReadMem-based implementation of the Queue class (#1740) * Added SyncReadMem-based implementation of the Queue class * Rework of the parametrized Queue class SyncReadMem-based implementation * Modification of the parametrized Queue class SyncReadMem-based implementation * Limiting the visibility of the read address for SyncReadMem-based Queue Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/test/scala/chiselTests/QueueSpec.scala | 56 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/QueueSpec.scala b/src/test/scala/chiselTests/QueueSpec.scala index 9dc7f120..3555a13c 100644 --- a/src/test/scala/chiselTests/QueueSpec.scala +++ b/src/test/scala/chiselTests/QueueSpec.scala @@ -9,8 +9,8 @@ import chisel3.testers.BasicTester import chisel3.util._ import chisel3.util.random.LFSR -class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth)) +class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt() }) @@ -34,8 +34,8 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int } } -class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth)) +class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt() }) @@ -62,8 +62,8 @@ class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: I } } -class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth)) +class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt(bitWidth.W) }) @@ -89,8 +89,8 @@ class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, t } } -class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), 1, pipe = true)) +class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), 1, pipe = true, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt(bitWidth.W) }) @@ -115,8 +115,8 @@ class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int) extends } } -class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth, pipe = true)) +class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, pipe = true, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt(bitWidth.W) }) @@ -141,8 +141,8 @@ class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I } } -class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth, flow = true)) +class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, flow = true, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt() }) @@ -169,9 +169,9 @@ class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I } } -class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { +class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { val enq = Wire(Decoupled(UInt(bitWidth.W))) - val deq = Queue(enq, queueDepth) + val deq = Queue(enq, queueDepth, useSyncReadMem = useSyncReadMem) val elems = VecInit(elements.map { _.asUInt() @@ -202,70 +202,70 @@ class QueueSpec extends ChiselPropSpec { implicit val noShrinkInt = Shrink[Int](_ => Stream.empty) property("Queue should have things pass through") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { assertTesterPasses { - new ThingsPassThroughTester(se._2, depth, se._1, tap) + new ThingsPassThroughTester(se._2, depth, se._1, tap, isSync) } } } } property("Queue should have reasonable ready/valid") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { assertTesterPasses { - new QueueReasonableReadyValid(se._2, depth, se._1, tap) + new QueueReasonableReadyValid(se._2, depth, se._1, tap, isSync) } } } } property("Queue should have correct count") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { assertTesterPasses { - new CountIsCorrectTester(se._2, depth, se._1, tap) + new CountIsCorrectTester(se._2, depth, se._1, tap, isSync) } } } } property("Queue pipe should work for 1-element queues") { - forAll(safeUIntN(20), Gen.choose(0, 15)) { (se, tap) => + forAll(safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (se, tap, isSync) => whenever(se._1 >= 1 && se._2.nonEmpty) { assertTesterPasses { - new QueueSinglePipeTester(se._2, se._1, tap) + new QueueSinglePipeTester(se._2, se._1, tap, isSync) } } } } property("Queue pipe should work for more general queues") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { assertTesterPasses { - new QueuePipeTester(se._2, depth, se._1, tap) + new QueuePipeTester(se._2, depth, se._1, tap, isSync) } } } } property("Queue flow should work") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { assertTesterPasses { - new QueueFlowTester(se._2, depth, se._1, tap) + new QueueFlowTester(se._2, depth, se._1, tap, isSync) } } } } property("Queue companion object factory method should work") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && se._2.nonEmpty) { assertTesterPasses { - new QueueFactoryTester(se._2, depth, se._1, tap) + new QueueFactoryTester(se._2, depth, se._1, tap, isSync) } } } -- cgit v1.2.3 From 53b620478ddab1faa96512e473fa198f7f1fcf50 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Tue, 9 Feb 2021 17:12:37 -0800 Subject: Add no-plugin-tests for testing Chisel without the compiler plugin This is a new SBT build unit that symlinks in some files from the normal chisel project tests, but builds them without the compiler plugin. --- src/test/scala/chiselTests/ChiselSpec.scala | 56 +------------------- .../chiselTests/ChiselTestUtilitiesSpec.scala | 60 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 843b3192..8df680d6 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -90,62 +90,8 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { /** Spec base class for BDD-style testers. */ abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matchers -class ChiselTestUtilitiesSpec extends ChiselFlatSpec { - import org.scalatest.exceptions.TestFailedException - // Who tests the testers? - "assertKnownWidth" should "error when the expected width is wrong" in { - val caught = intercept[ChiselException] { - assertKnownWidth(7) { - Wire(UInt(8.W)) - } - } - assert(caught.getCause.isInstanceOf[TestFailedException]) - } - - it should "error when the width is unknown" in { - a [ChiselException] shouldBe thrownBy { - assertKnownWidth(7) { - Wire(UInt()) - } - } - } - - it should "work if the width is correct" in { - assertKnownWidth(8) { - Wire(UInt(8.W)) - } - } - - "assertInferredWidth" should "error if the width is known" in { - val caught = intercept[ChiselException] { - assertInferredWidth(8) { - Wire(UInt(8.W)) - } - } - assert(caught.getCause.isInstanceOf[TestFailedException]) - } - - it should "error if the expected width is wrong" in { - a [TestFailedException] shouldBe thrownBy { - assertInferredWidth(8) { - val w = Wire(UInt()) - w := 2.U(2.W) - w - } - } - } - - it should "pass if the width is correct" in { - assertInferredWidth(4) { - val w = Wire(UInt()) - w := 2.U(4.W) - w - } - } -} - /** Spec base class for property-based testers. */ -class ChiselPropSpec extends PropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { +abstract class ChiselPropSpec extends PropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { // Constrain the default number of instances generated for every use of forAll. implicit override val generatorDrivenConfig: PropertyCheckConfiguration = diff --git a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala new file mode 100644 index 00000000..6d3d526c --- /dev/null +++ b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import org.scalatest.exceptions.TestFailedException + +class ChiselTestUtilitiesSpec extends ChiselFlatSpec { + // Who tests the testers? + "assertKnownWidth" should "error when the expected width is wrong" in { + val caught = intercept[ChiselException] { + assertKnownWidth(7) { + Wire(UInt(8.W)) + } + } + assert(caught.getCause.isInstanceOf[TestFailedException]) + } + + it should "error when the width is unknown" in { + a [ChiselException] shouldBe thrownBy { + assertKnownWidth(7) { + Wire(UInt()) + } + } + } + + it should "work if the width is correct" in { + assertKnownWidth(8) { + Wire(UInt(8.W)) + } + } + + "assertInferredWidth" should "error if the width is known" in { + val caught = intercept[ChiselException] { + assertInferredWidth(8) { + Wire(UInt(8.W)) + } + } + assert(caught.getCause.isInstanceOf[TestFailedException]) + } + + it should "error if the expected width is wrong" in { + a [TestFailedException] shouldBe thrownBy { + assertInferredWidth(8) { + val w = Wire(UInt()) + w := 2.U(2.W) + w + } + } + } + + it should "pass if the width is correct" in { + assertInferredWidth(4) { + val w = Wire(UInt()) + w := 2.U(4.W) + w + } + } +} + -- cgit v1.2.3 From 0a0d7c6aac4326f2127d6d95efa5a4e10c81946c Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Mon, 12 Oct 2020 21:02:27 -0700 Subject: Make it possible to GC Data instances No longer create a pointer from parent to every HasId, only do it by default for BaseModules and MemBases. Add pointer from parent to Data upon binding the Data. * Add MemTypeBinding for port types of Mems This binding is similar to the SampleElementBinding for Vecs in that these Data are not truly hardware, but are represented in the FIRRTL IR and thus need some representation. * Call _onModuleClose on unbound Records This maintains some corner-case behavior that is nevertheless relied upon. It ensures that refs are set for the elements of Records, even if they are not bound to any real hardware. --- src/test/scala/chiselTests/CompatibilitySpec.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/CompatibilitySpec.scala b/src/test/scala/chiselTests/CompatibilitySpec.scala index c7a68e7c..2d4ad517 100644 --- a/src/test/scala/chiselTests/CompatibilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilitySpec.scala @@ -451,6 +451,18 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck ChiselStage.elaborate(new Foo) } + it should "support data-types of mixed directionality" in { + class Foo extends Module { + val io = IO(new Bundle {}) + val tpe = new Bundle { val foo = UInt(OUTPUT, width = 4); val bar = UInt(width = 4) } + // NOTE for some reason, the old bug this hit did not occur when `tpe` is inlined + val mem = SeqMem(tpe, 8) + mem(3.U) + + } + ChiselStage.elaborate((new Foo)) + } + behavior of "debug" it should "still exist" in { -- cgit v1.2.3 From 2b5466c7773c8cd7a08c48aa00d9365cbb205fd2 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Wed, 10 Feb 2021 22:19:09 -0500 Subject: Fix stack trace trimming across Driver/ChiselStage (#1771) * Handle MemTypeBinding in Analog Signed-off-by: Schuyler Eldridge * Fix stack trace trimming across ChiselStage Fix bug in stack trace trimming behavior. Now, the following is what happens: 1. The Builder, if catching accumulated errors, will now throw a ChiselException with a Scala-trimmed Stack trace. Previously, this would throw the full excpetion. 2. The Elaborate phase handles stack trace trimming. By default, any Throwable thrown during elaboration will have its stack trace *mutably* trimmed and is rethrown. A logger.error is printed stating that there was an error during elaboration and how the user can turn on the full stack trace. If the --full-stacktrace option is on, then the Throwable is not caught and only the first logger.error (saying that elaboration failed) will be printed. 3. ChiselStage (the class), ChiselStage$ (the object), and ChiselMain all inherit the behavior of (2). Mutable stack trace trimming behavior is moved into an implicit class (previously this was defined on ChiselException only) so this can be applied to any Throwable. No StageErrors are now thrown anymore. However, StageErrors may still be caught by ChiselMain (since it is a StageMain). Testing is added for ChiselMain, ChiselStage, and ChiselStage$ to test all this behavior. Signed-off-by: Schuyler Eldridge --- .../chiselTests/ChiselTestUtilitiesSpec.scala | 9 +- src/test/scala/chiselTests/OneHotMuxSpec.scala | 12 +- .../scala/chiselTests/stage/ChiselMainSpec.scala | 191 +++++++++++++++------ .../scala/chiselTests/stage/ChiselStageSpec.scala | 64 ++++++- 4 files changed, 209 insertions(+), 67 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala index 6d3d526c..40358d11 100644 --- a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala +++ b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala @@ -8,16 +8,15 @@ import org.scalatest.exceptions.TestFailedException class ChiselTestUtilitiesSpec extends ChiselFlatSpec { // Who tests the testers? "assertKnownWidth" should "error when the expected width is wrong" in { - val caught = intercept[ChiselException] { + intercept[TestFailedException] { assertKnownWidth(7) { Wire(UInt(8.W)) } } - assert(caught.getCause.isInstanceOf[TestFailedException]) } it should "error when the width is unknown" in { - a [ChiselException] shouldBe thrownBy { + intercept[ChiselException] { assertKnownWidth(7) { Wire(UInt()) } @@ -31,12 +30,11 @@ class ChiselTestUtilitiesSpec extends ChiselFlatSpec { } "assertInferredWidth" should "error if the width is known" in { - val caught = intercept[ChiselException] { + intercept[TestFailedException] { assertInferredWidth(8) { Wire(UInt(8.W)) } } - assert(caught.getCause.isInstanceOf[TestFailedException]) } it should "error if the expected width is wrong" in { @@ -57,4 +55,3 @@ class ChiselTestUtilitiesSpec extends ChiselFlatSpec { } } } - diff --git a/src/test/scala/chiselTests/OneHotMuxSpec.scala b/src/test/scala/chiselTests/OneHotMuxSpec.scala index 887843d4..7608a3e7 100644 --- a/src/test/scala/chiselTests/OneHotMuxSpec.scala +++ b/src/test/scala/chiselTests/OneHotMuxSpec.scala @@ -37,26 +37,18 @@ class OneHotMuxSpec extends AnyFreeSpec with Matchers with ChiselRunners { } } "simple one hot mux with all fixed width bundles but with different bundles should Not work" in { - try { + intercept[IllegalArgumentException] { assertTesterPasses(new DifferentBundleOneHotTester) - } catch { - case a: ChiselException => a.getCause match { - case _: IllegalArgumentException => - } } } "UIntToOH with output width greater than 2^(input width)" in { assertTesterPasses(new UIntToOHTester) } "UIntToOH should not accept width of zero (until zero-width wires are fixed" in { - try { + intercept[IllegalArgumentException] { assertTesterPasses(new BasicTester { val out = UIntToOH(0.U, 0) }) - } catch { - case a: ChiselException => a.getCause match { - case _: IllegalArgumentException => - } } } diff --git a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala index 0fc42fc6..1634e765 100644 --- a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala +++ b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala @@ -10,6 +10,8 @@ import chisel3.aop.inspecting.{InspectingAspect, InspectorAspect} import org.scalatest.GivenWhenThen import org.scalatest.featurespec.AnyFeatureSpec import org.scalatest.matchers.should.Matchers +import org.scalatest.Inside._ +import org.scalatest.EitherValues._ import scala.io.Source import firrtl.Parser @@ -32,7 +34,7 @@ object ChiselMainSpec { /** A module that fails a requirement */ class FailingRequirementModule extends RawModule { - require(false) + require(false, "the user wrote a failing requirement") } /** A module that triggers a Builder.error (as opposed to exception) */ @@ -69,14 +71,35 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit args: Array[String], generator: Option[Class[_ <: RawModule]] = None, files: Seq[String] = Seq.empty, - stdout: Option[String] = None, - stderr: Option[String] = None, + stdout: Seq[Either[String, String]] = Seq.empty, + stderr: Seq[Either[String, String]] = Seq.empty, result: Int = 0, fileChecks: Map[String, File => Unit] = Map.empty) { def testName: String = "args" + args.mkString("_") def argsString: String = args.mkString(" ") } + /** A test of ChiselMain that is going to involve catching an exception. + * @param args command line arguments (excluding --module) to pass in + * @param generator the module to build (used to generate --module) + * @param message snippets of text that should appear (Right) or not appear (Left) in the exception message + * @param stdout snippets of text that should appear (Right) or not appear (Left) in STDOUT + * @param stderr snippets of text that should appear (Right) or not appear (Left) in STDERR + * @param stackTrace snippets of text that should appear (Right) or not appear (Left) in the stack trace + * @tparam the type of exception that should occur + */ + case class ChiselMainExceptionTest[A <: Throwable]( + args: Array[String], + generator: Option[Class[_ <: RawModule]] = None, + message: Seq[Either[String, String]] = Seq.empty, + stdout: Seq[Either[String, String]] = Seq.empty, + stderr: Seq[Either[String, String]] = Seq.empty, + stackTrace: Seq[Either[String, String]] = Seq.empty + ) { + def testName: String = "args" + args.mkString("_") + def argsString: String = args.mkString(" ") + } + def runStageExpectFiles(p: ChiselMainTest): Unit = { Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") { val f = new ChiselMainFixture @@ -85,32 +108,33 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit p.files.foreach( f => new File(td.buildDir + s"/$f").delete() ) When(s"""the user tries to compile with '${p.argsString}'""") + val module: Array[String] = + (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) } + else { Array.empty[String] }) + f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args) val (stdout, stderr, result) = grabStdOutErr { catchStatus { - val module: Array[String] = Array("foo") ++ - (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) } - else { Array.empty[String] }) f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args) } } - p.stdout match { - case Some(a) => + p.stdout.foreach { + case Right(a) => Then(s"""STDOUT should include "$a"""") stdout should include (a) - case None => - Then(s"nothing should print to STDOUT") - stdout should be (empty) + case Left(a) => + Then(s"""STDOUT should not include "$a"""") + stdout should not include (a) } - p.stderr match { - case Some(a) => - And(s"""STDERR should include "$a"""") + p.stderr.foreach { + case Right(a) => + Then(s"""STDERR should include "$a"""") stderr should include (a) - case None => - And(s"nothing should print to STDERR") - stderr should be (empty) + case Left(a) => + Then(s"""STDERR should not include "$a"""") + stderr should not include (a) } p.result match { @@ -131,56 +155,128 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit } } + /** Run a ChiselMainExceptionTest and verify that all the properties it spells out hold. + * @param p the test to run + * @tparam the type of the exception to catch (you shouldn't have to explicitly provide this) + */ + def runStageExpectException[A <: Throwable: scala.reflect.ClassTag](p: ChiselMainExceptionTest[A]): Unit = { + Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") { + val f = new ChiselMainFixture + val td = new TargetDirectoryFixture(p.testName) + + When(s"""the user tries to compile with '${p.argsString}'""") + val module: Array[String] = + (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) } + else { Array.empty[String] }) + val (stdout, stderr, result) = + grabStdOutErr { + catchStatus { + intercept[A] { + f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args) + } + } + } + + Then("the expected exception was thrown") + result should be a ('right) + val exception = result.right.get + info(s""" - Exception was a "${exception.getClass.getName}"""") + + val message = exception.getMessage + p.message.foreach { + case Right(a) => + Then(s"""STDOUT should include "$a"""") + message should include (a) + case Left(a) => + Then(s"""STDOUT should not include "$a"""") + message should not include (a) + } + + p.stdout.foreach { + case Right(a) => + Then(s"""STDOUT should include "$a"""") + stdout should include (a) + case Left(a) => + Then(s"""STDOUT should not include "$a"""") + stdout should not include (a) + } + + p.stderr.foreach { + case Right(a) => + Then(s"""STDERR should include "$a"""") + stderr should include (a) + case Left(a) => + Then(s"""STDERR should not include "$a"""") + stderr should not include (a) + } + + val stackTraceString = exception.getStackTrace.mkString("\n") + p.stackTrace.foreach { + case Left(a) => + And(s"""the stack does not include "$a"""") + stackTraceString should not include (a) + case Right(a) => + And(s"""the stack trace includes "$a"""") + stackTraceString should include (a) + } + + } + } + info("As a Chisel user") info("I compile a design") Feature("show elaborating message") { runStageExpectFiles( ChiselMainTest(args = Array("-X", "high"), - generator = Some(classOf[SameTypesModule]), - stdout = Some("Done elaborating.") + generator = Some(classOf[SameTypesModule]) ) ) } info("I screw up and compile some bad code") - Feature("Stack trace trimming") { + Feature("Stack trace trimming of ChiselException") { Seq( - ChiselMainTest(args = Array("-X", "low"), - generator = Some(classOf[DifferentTypesModule]), - stdout = Some("Stack trace trimmed to user code only"), - result = 1), - ChiselMainTest(args = Array("-X", "high", "--full-stacktrace"), - generator = Some(classOf[DifferentTypesModule]), - stdout = Some("org.scalatest"), - result = 1) - ).foreach(runStageExpectFiles) + ChiselMainExceptionTest[chisel3.internal.ChiselException]( + args = Array("-X", "low"), + generator = Some(classOf[DifferentTypesModule]), + stackTrace = Seq(Left("java"), Right(classOf[DifferentTypesModule].getName)) + ), + ChiselMainExceptionTest[chisel3.internal.ChiselException]( + args = Array("-X", "low", "--full-stacktrace"), + generator = Some(classOf[DifferentTypesModule]), + stackTrace = Seq(Right("java"), Right(classOf[DifferentTypesModule].getName)) + ) + ).foreach(runStageExpectException) } - Feature("Report properly trimmed stack traces") { + Feature("Stack trace trimming of user exceptions") { Seq( - ChiselMainTest(args = Array("-X", "low"), - generator = Some(classOf[FailingRequirementModule]), - stdout = Some("requirement failed"), - result = 1), - ChiselMainTest(args = Array("-X", "low", "--full-stacktrace"), - generator = Some(classOf[FailingRequirementModule]), - stdout = Some("chisel3.internal.ChiselException"), - result = 1) - ).foreach(runStageExpectFiles) + ChiselMainExceptionTest[java.lang.IllegalArgumentException]( + args = Array("-X", "low"), + generator = Some(classOf[FailingRequirementModule]), + stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Left("java")) + ), + ChiselMainExceptionTest[java.lang.IllegalArgumentException]( + args = Array("-X", "low", "--full-stacktrace"), + generator = Some(classOf[FailingRequirementModule]), + stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Right("java")) + ) + ).foreach(runStageExpectException) } - Feature("Builder.error source locator") { + Feature("Stack trace trimming and Builder.error errors") { Seq( - ChiselMainTest(args = Array("-X", "none"), + ChiselMainExceptionTest[chisel3.internal.ChiselException]( + args = Array("-X", "low"), generator = Some(classOf[BuilderErrorModule]), - stdout = Some("ChiselMainSpec.scala:41: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule"), - result = 1) - ).foreach(runStageExpectFiles) + message = Seq(Right("Fatal errors during hardware elaboration")), + stdout = Seq(Right("ChiselMainSpec.scala:43: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule")) + ) + ).foreach(runStageExpectException) } Feature("Specifying a custom output file") { runStageExpectFiles(ChiselMainTest( args = Array("--chisel-output-file", "Foo", "--no-run-firrtl"), generator = Some(classOf[SameTypesModule]), - stdout = Some(""), files = Seq("Foo.fir"), fileChecks = Map( "Foo.fir" -> { file => @@ -192,7 +288,6 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit runStageExpectFiles(ChiselMainTest( args = Array("--chisel-output-file", "Foo.pb", "--no-run-firrtl"), generator = Some(classOf[SameTypesModule]), - stdout = Some(""), files = Seq("Foo.pb"), fileChecks = Map( "Foo.pb" -> { file => @@ -209,10 +304,10 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit Seq( ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestClassAspect" ), generator = Some(classOf[SameTypesModule]), - stdout = Some("Ran inspectingAspect")), + stdout = Seq(Right("Ran inspectingAspect"))), ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestObjectAspect" ), generator = Some(classOf[SameTypesModule]), - stdout = Some("Ran inspectingAspect")) + stdout = Seq(Right("Ran inspectingAspect"))) ).foreach(runStageExpectFiles) } diff --git a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala index 98bbb2ea..167e414b 100644 --- a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala +++ b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala @@ -30,6 +30,10 @@ object ChiselStageSpec { out := memory(bar.out) } + class UserExceptionModule extends RawModule { + assert(false, "User threw an exception") + } + } class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { @@ -40,13 +44,13 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { val stage = new ChiselStage } - behavior of "ChiselStage.emitChirrtl" + behavior of "ChiselStage$.emitChirrtl" it should "return a CHIRRTL string" in { ChiselStage.emitChirrtl(new Foo) should include ("infer mport") } - behavior of "ChiselStage.emitFirrtl" + behavior of "ChiselStage$.emitFirrtl" it should "return a High FIRRTL string" in { ChiselStage.emitFirrtl(new Foo) should include ("mem memory") @@ -58,7 +62,7 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { .emitFirrtl(new Foo, args) should include ("module Bar") } - behavior of "ChiselStage.emitVerilog" + behavior of "ChiselStage$.emitVerilog" it should "return a Verilog string" in { ChiselStage.emitVerilog(new Foo) should include ("endmodule") @@ -142,4 +146,58 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { exactly (1, order) should be (Dependency[chisel3.stage.phases.Elaborate]) } + behavior of "ChiselStage$ exception handling" + + it should "truncate a user exception" in { + info("The user's java.lang.AssertionError was thrown") + val exception = intercept[java.lang.AssertionError] { + ChiselStage.emitChirrtl(new UserExceptionModule) + } + + val message = exception.getMessage + info("The exception includes the user's message") + message should include ("User threw an exception") + + info("The stack trace is trimmed") + exception.getStackTrace.mkString("\n") should not include ("java") + } + + behavior of "ChiselStage exception handling" + + it should "truncate a user exception" in { + info("The user's java.lang.AssertionError was thrown") + val exception = intercept[java.lang.AssertionError] { + (new ChiselStage).emitChirrtl(new UserExceptionModule) + } + + info(s""" - Exception was a ${exception.getClass.getName}""") + + val message = exception.getMessage + info("The exception includes the user's message") + message should include ("User threw an exception") + + val stackTrace = exception.getStackTrace.mkString("\n") + info("The stack trace is trimmed") + stackTrace should not include ("java") + + info("The stack trace include information about running --full-stacktrace") + stackTrace should include ("--full-stacktrace") + } + + it should """not truncate a user exception with "--full-stacktrace"""" in { + info("The user's java.lang.AssertionError was thrown") + val exception = intercept[java.lang.AssertionError] { + (new ChiselStage).emitChirrtl(new UserExceptionModule, Array("--full-stacktrace")) + } + + info(s""" - Exception was a ${exception.getClass.getName}""") + + val message = exception.getMessage + info("The exception includes the user's message") + message should include ("User threw an exception") + + info("The stack trace is not trimmed") + exception.getStackTrace.mkString("\n") should include ("java") + } + } -- cgit v1.2.3 From 923ccbde1353e37f0948d3c5d94b49965dc6d950 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sat, 27 Feb 2021 05:01:10 +0800 Subject: Expose AnnotationSeq to Module. (#1731) --- src/test/scala/chiselTests/Module.scala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/Module.scala b/src/test/scala/chiselTests/Module.scala index 932c94a5..bc9c524a 100644 --- a/src/test/scala/chiselTests/Module.scala +++ b/src/test/scala/chiselTests/Module.scala @@ -3,8 +3,10 @@ package chiselTests import chisel3._ -import chisel3.stage.ChiselStage import chisel3.experimental.DataMirror +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation} +import firrtl.annotations.NoTargetAnnotation +import firrtl.options.Unserializable class SimpleIO extends Bundle { val in = Input(UInt(32.W)) @@ -140,6 +142,17 @@ class ModuleSpec extends ChiselPropSpec with Utils { assert(checkModule(this)) }) } + + property("object chisel3.util.experimental.getAnnotations should return current annotations.") { + case class DummyAnnotation() extends NoTargetAnnotation with Unserializable + (new ChiselStage).transform(Seq( + ChiselGeneratorAnnotation(() => new RawModule { + assert(chisel3.util.experimental.getAnnotations().contains(DummyAnnotation())) + }), + DummyAnnotation(), + NoRunFirrtlCompilerAnnotation)) + } + property("DataMirror.modulePorts should work") { ChiselStage.elaborate(new Module { val io = IO(new Bundle { }) -- cgit v1.2.3 From 9ea57e031f834841609a30031ddab08fe4f47029 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Date: Thu, 11 Mar 2021 18:25:54 -0300 Subject: Import memory files inline for Verilog generation (#1805) This annotation adds memory import with inline generation for the emmiter. Supports both readmemh and readmemb statements based on argument.--- .../scala/chiselTests/LoadMemoryFromFileSpec.scala | 53 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala index 3be649e1..8a998496 100644 --- a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala +++ b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala @@ -6,7 +6,7 @@ import java.io.File import chisel3._ import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} -import chisel3.util.experimental.loadMemoryFromFile +import chisel3.util.experimental.{loadMemoryFromFile,loadMemoryFromFileInline} import chisel3.util.log2Ceil import firrtl.FirrtlExecutionSuccess import firrtl.annotations.MemoryLoadFileType @@ -33,6 +33,26 @@ class UsesThreeMems(memoryDepth: Int, memoryType: Data) extends Module { io.value3 := memory3(io.address) } +class UsesThreeMemsInline(memoryDepth: Int, memoryType: Data, memoryFile: String, hexOrBinary: MemoryLoadFileType.FileType) extends Module { + val io = IO(new Bundle { + val address = Input(UInt(memoryType.getWidth.W)) + val value1 = Output(memoryType) + val value2 = Output(memoryType) + val value3 = Output(memoryType) + }) + + val memory1 = Mem(memoryDepth, memoryType) + val memory2 = Mem(memoryDepth, memoryType) + val memory3 = Mem(memoryDepth, memoryType) + loadMemoryFromFileInline(memory1, memoryFile, hexOrBinary) + loadMemoryFromFileInline(memory2, memoryFile, hexOrBinary) + loadMemoryFromFileInline(memory3, memoryFile, hexOrBinary) + + io.value1 := memory1(io.address) + io.value2 := memory2(io.address) + io.value3 := memory3(io.address) +} + class UsesMem(memoryDepth: Int, memoryType: Data) extends Module { val io = IO(new Bundle { val address = Input(UInt(memoryType.getWidth.W)) @@ -205,4 +225,35 @@ class LoadMemoryFromFileSpec extends AnyFreeSpec with Matchers { file.delete() } + "Module with more than one hex memory inline should work" in { + val testDirName = "test_run_dir/load_three_memory_spec_inline" + + val result = (new ChiselStage).execute( + args = Array("-X", "verilog", "--target-dir", testDirName), + annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMemsInline(memoryDepth = 8, memoryType = UInt(16.W), "./testmem.h", MemoryLoadFileType.Hex))) + ) + val dir = new File(testDirName) + val file = new File(dir, s"UsesThreeMemsInline.v") + file.exists() should be (true) + val fileText = io.Source.fromFile(file).getLines().mkString("\n") + fileText should include (s"""$$readmemh("./testmem.h", memory1);""") + fileText should include (s"""$$readmemh("./testmem.h", memory2);""") + fileText should include (s"""$$readmemh("./testmem.h", memory3);""") + } + + "Module with more than one bin memory inline should work" in { + val testDirName = "test_run_dir/load_three_memory_spec_inline" + + val result = (new ChiselStage).execute( + args = Array("-X", "verilog", "--target-dir", testDirName), + annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMemsInline(memoryDepth = 8, memoryType = UInt(16.W), "testmem.bin", MemoryLoadFileType.Binary))) + ) + val dir = new File(testDirName) + val file = new File(dir, s"UsesThreeMemsInline.v") + file.exists() should be (true) + val fileText = io.Source.fromFile(file).getLines().mkString("\n") + fileText should include (s"""$$readmemb("testmem.bin", memory1);""") + fileText should include (s"""$$readmemb("testmem.bin", memory2);""") + fileText should include (s"""$$readmemb("testmem.bin", memory3);""") + } } -- cgit v1.2.3 From 1494231212425fd09f915d819102ca5cdef0dfcf Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 11 Feb 2021 18:12:48 -0800 Subject: [plugin] Implement autoclonetype in the compiler plugin --- src/test/scala/chisel3/testers/TestUtils.scala | 12 ++ src/test/scala/chiselTests/AutoClonetypeSpec.scala | 179 +++++++++++++++++---- .../scala/chiselTests/AutoNestedCloneSpec.scala | 73 +++++---- .../MissingCloneBindingExceptionSpec.scala | 55 ------- 4 files changed, 203 insertions(+), 116 deletions(-) delete mode 100644 src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chisel3/testers/TestUtils.scala b/src/test/scala/chisel3/testers/TestUtils.scala index 12712bf7..c72c779a 100644 --- a/src/test/scala/chisel3/testers/TestUtils.scala +++ b/src/test/scala/chisel3/testers/TestUtils.scala @@ -3,10 +3,22 @@ package chisel3.testers import TesterDriver.Backend +import chisel3.{Bundle, RawModule} +import chisel3.internal.firrtl.Circuit +import chisel3.stage.ChiselStage import firrtl.AnnotationSeq object TestUtils { // Useful because TesterDriver.Backend is chisel3 package private def containsBackend(annos: AnnotationSeq): Boolean = annos.collectFirst { case b: Backend => b }.isDefined + + // Allows us to check that the compiler plugin cloneType is actually working + val usingPlugin: Boolean = (new Bundle { def check = _usingPlugin }).check + def elaborateNoReflectiveAutoCloneType(f: => RawModule): Circuit = { + ChiselStage.elaborate { + chisel3.internal.Builder.allowReflectiveAutoCloneType = !usingPlugin + f + } + } } diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index b791297d..a3da109a 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -3,7 +3,7 @@ package chiselTests import chisel3._ -import chisel3.stage.ChiselStage +import chisel3.testers.TestUtils class BundleWithIntArg(val i: Int) extends Bundle { val out = UInt(i.W) @@ -65,10 +65,14 @@ class NestedAnonymousBundle extends Bundle { // Not necessarily good style (and not necessarily recommended), but allowed to preserve compatibility. class BundleWithArgumentField(val x: Data, val y: Data) extends Bundle +// TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802 class AutoClonetypeSpec extends ChiselFlatSpec with Utils { + val usingPlugin: Boolean = TestUtils.usingPlugin + val elaborate = TestUtils.elaborateNoReflectiveAutoCloneType _ + "Bundles with Scala args" should "not need clonetype" in { - ChiselStage.elaborate { new Module { - val io = IO(new Bundle{}) + elaborate { new Module { + val io = IO(new Bundle{}).suggestName("io") val myWire = Wire(new BundleWithIntArg(8)) assert(myWire.i == 8) @@ -76,8 +80,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } "Bundles with Scala implicit args" should "not need clonetype" in { - ChiselStage.elaborate { new Module { - val io = IO(new Bundle{}) + elaborate { new Module { + val io = IO(new Bundle{}).suggestName("io") implicit val implicitInt: Int = 4 val myWire = Wire(new BundleWithImplicit()) @@ -87,8 +91,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } "Bundles with Scala explicit and impicit args" should "not need clonetype" in { - ChiselStage.elaborate { new Module { - val io = IO(new Bundle{}) + elaborate { new Module { + val io = IO(new Bundle{}).suggestName("io") implicit val implicitInt: Int = 4 val myWire = Wire(new BundleWithArgAndImplicit(8)) @@ -99,16 +103,16 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } "Subtyped Bundles" should "not need clonetype" in { - ChiselStage.elaborate { new Module { - val io = IO(new Bundle{}) + elaborate { new Module { + val io = IO(new Bundle{}).suggestName("io") val myWire = Wire(new SubBundle(8, 4)) assert(myWire.i == 8) assert(myWire.i2 == 4) } } - ChiselStage.elaborate { new Module { - val io = IO(new Bundle{}) + elaborate { new Module { + val io = IO(new Bundle{}).suggestName("io") val myWire = Wire(new SubBundleVal(8, 4)) @@ -117,68 +121,84 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } } } - "Subtyped Bundles that don't clone well" should "be caught" in { - a [ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate { new Module { - val io = IO(new Bundle{}) - val myWire = Wire(new SubBundleInvalid(8, 4)) - } } + def checkSubBundleInvalid() = { + elaborate { new Module { + val io = IO(new Bundle{}).suggestName("io") + val myWire = Wire(new SubBundleInvalid(8, 4)) + } } + } + if (usingPlugin) { + "Subtyped Bundles that don't clone well" should "be now be supported!" in { + checkSubBundleInvalid() + } + } else { + "Subtyped Bundles that don't clone well" should "be caught" in { + a [ChiselException] should be thrownBy extractCause[ChiselException] { + checkSubBundleInvalid() + } } } "Inner bundles with Scala args" should "not need clonetype" in { - ChiselStage.elaborate { new ModuleWithInner } + elaborate { new ModuleWithInner } } "Bundles with arguments as fields" should "not need clonetype" in { - ChiselStage.elaborate { new Module { - val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W)))) + elaborate { new Module { + val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W)))).suggestName("io") io.x := 1.U io.y := 1.U } } } + it should "also work when giving directions to the fields" in { + elaborate { new Module { + val io = IO(new BundleWithArgumentField(Input(UInt(8.W)), Output(UInt(8.W)))).suggestName("io") + io.y := io.x + } } + } + "Bundles inside companion objects" should "not need clonetype" in { - ChiselStage.elaborate { new Module { - val io = IO(Output(new CompanionObjectWithBundle.Inner)) + elaborate { new Module { + val io = IO(Output(new CompanionObjectWithBundle.Inner)).suggestName("io") io.data := 1.U } } } "Parameterized bundles inside companion objects" should "not need clonetype" in { - ChiselStage.elaborate { new Module { - val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8))) + elaborate { new Module { + val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8))).suggestName("io") io.data := 1.U } } } "Nested directioned anonymous Bundles" should "not need clonetype" in { - ChiselStage.elaborate { new Module { - val io = IO(new NestedAnonymousBundle) + elaborate { new Module { + val io = IO(new NestedAnonymousBundle).suggestName("io") val a = WireDefault(io) io.a.a := 1.U } } } "3.0 null compatibility" should "not need clonetype" in { - ChiselStage.elaborate { new Module { + elaborate { new Module { class InnerClassThing { def createBundle: Bundle = new Bundle { val a = Output(UInt(8.W)) } } - val io = IO((new InnerClassThing).createBundle) + val io = IO((new InnerClassThing).createBundle).suggestName("io") val a = WireDefault(io) } } } "Aliased fields" should "be caught" in { a [ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate { new Module { + elaborate { new Module { val bundleFieldType = UInt(8.W) val io = IO(Output(new Bundle { val a = bundleFieldType - })) + })).suggestName("io") io.a := 0.U } } } @@ -190,8 +210,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { val a = typeTuple._1 } - ChiselStage.elaborate { new Module { - val io = IO(Output(new BadBundle(UInt(8.W), 1))) + elaborate { new Module { + val io = IO(Output(new BadBundle(UInt(8.W), 1))).suggestName("io") io.a := 0.U } } } @@ -204,7 +224,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { val x = Output(UInt(3.W)) })) } - ChiselStage.elaborate { new TestModule } + elaborate { new TestModule } } "Wrapped IO construction with parent references" should "not fail for autoclonetype" in { @@ -216,6 +236,97 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { val x = Output(UInt(blah.W)) })) } - ChiselStage.elaborate { new TestModule(3) } + elaborate { new TestModule(3) } + } + + "Autoclonetype" should "support Bundles with if-blocks" in { + class MyModule(n: Int) extends MultiIOModule { + val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + if (n > 4) { + println("Here we are!") + } + }) + io.out := io.in + } + elaborate(new MyModule(3)) + } + + // New tests from the plugin + if (usingPlugin) { + behavior of "Compiler Plugin Autoclonetype" + + it should "support Bundles with non-val parameters" in { + class MyBundle(i: Int) extends Bundle { + val foo = UInt(i.W) + } + elaborate { new MultiIOModule { + val in = IO(Input(new MyBundle(8))) + val out = IO(Output(new MyBundle(8))) + out := in + }} + } + + it should "support type-parameterized Bundles" in { + class MyBundle[T <: Data](gen: T) extends Bundle { + val foo = gen + } + elaborate { new MultiIOModule { + val in = IO(Input(new MyBundle(UInt(8.W)))) + val out = IO(Output(new MyBundle(UInt(8.W)))) + out := in + }} + } + + it should "support Bundles with non-val implicit parameters" in { + class MyBundle(implicit i: Int) extends Bundle { + val foo = UInt(i.W) + } + elaborate { new MultiIOModule { + implicit val x = 8 + val in = IO(Input(new MyBundle)) + val out = IO(Output(new MyBundle)) + out := in + }} + } + + it should "support Bundles with multiple parameter lists" in { + class MyBundle(i: Int)(j: Int, jj: Int)(k: UInt) extends Bundle { + val foo = UInt((i + j + jj + k.getWidth).W) + } + elaborate { + new MultiIOModule { + val in = IO(Input(new MyBundle(8)(8, 8)(UInt(8.W)))) + val out = IO(Output(new MyBundle(8)(8, 8)(UInt(8.W)))) + out := in + } + } + } + + it should "support Bundles that implement their own cloneType" in { + class MyBundle(i: Int) extends Bundle { + val foo = UInt(i.W) + override def cloneType = new MyBundle(i).asInstanceOf[this.type] + } + elaborate { new MultiIOModule { + val in = IO(Input(new MyBundle(8))) + val out = IO(Output(new MyBundle(8))) + out := in + }} + } + + it should "support Bundles that capture type parameters from their parent scope" in { + class MyModule[T <: Data](gen: T) extends MultiIOModule { + class MyBundle(n: Int) extends Bundle { + val foo = Vec(n, gen) + } + val in = IO(Input(new MyBundle(4))) + val out = IO(Output(new MyBundle(4))) + out := in + } + elaborate(new MyModule(UInt(8.W))) + } + } } diff --git a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala index 8e40ad20..401766e2 100644 --- a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala +++ b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala @@ -1,10 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 package chiselTests -import Chisel.ChiselException -import org.scalatest._ import chisel3._ -import chisel3.stage.ChiselStage +import chisel3.testers.TestUtils import org.scalatest.matchers.should.Matchers class BundleWithAnonymousInner(val w: Int) extends Bundle { @@ -13,11 +11,15 @@ class BundleWithAnonymousInner(val w: Int) extends Bundle { } } +// TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802 class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { + val usingPlugin: Boolean = TestUtils.usingPlugin + val elaborate = TestUtils.elaborateNoReflectiveAutoCloneType _ + behavior of "autoCloneType of inner Bundle in Chisel3" it should "clone a doubly-nested inner bundle successfully" in { - ChiselStage.elaborate { + elaborate { class Outer(val w: Int) extends Module { class Middle(val w: Int) { class InnerIOType extends Bundle { @@ -25,7 +27,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { } def getIO: InnerIOType = new InnerIOType } - val io = IO(new Bundle {}) + val io = IO(new Bundle {}).suggestName("io") val myWire = Wire((new Middle(w)).getIO) } new Outer(2) @@ -33,9 +35,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { } it should "clone an anonymous inner bundle successfully" in { - ChiselStage.elaborate { + elaborate { class TestTop(val w: Int) extends Module { - val io = IO(new Bundle {}) + val io = IO(new Bundle {}).suggestName("io") val myWire = Wire(new Bundle{ val a = UInt(w.W) }) } new TestTop(2) @@ -43,18 +45,18 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { } it should "pick the correct $outer instance for an anonymous inner bundle" in { - ChiselStage.elaborate { + elaborate { class Inner(val w: Int) extends Module { val io = IO(new Bundle{ val in = Input(UInt(w.W)) val out = Output(UInt(w.W)) - }) + }).suggestName("io") } class Outer(val w: Int) extends Module { val io = IO(new Bundle{ val in = Input(UInt(w.W)) val out = Output(UInt(w.W)) - }) + }).suggestName("io") val i = Module(new Inner(w)) val iw = Wire(chiselTypeOf(i.io)) iw <> io @@ -65,9 +67,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { } it should "clone an anonymous, bound, inner bundle of another bundle successfully" in { - ChiselStage.elaborate { + elaborate { class TestModule(w: Int) extends Module { - val io = IO(new BundleWithAnonymousInner(w) ) + val io = IO(new BundleWithAnonymousInner(w) ).suggestName("io") val w0 = WireDefault(io) val w1 = WireDefault(io.inner) } @@ -76,14 +78,14 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { } it should "clone an anonymous, inner bundle of a Module, bound to another bundle successfully" in { - ChiselStage.elaborate { + elaborate { class TestModule(w: Int) extends Module { val bun = new Bundle { val foo = UInt(w.W) } val io = IO(new Bundle { val inner = Input(bun) - }) + }).suggestName("io") val w0 = WireDefault(io) val w1 = WireDefault(io.inner) } @@ -92,31 +94,48 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { } it should "clone a double-nested anonymous Bundle" in { - ChiselStage.elaborate { + elaborate { class TestModule() extends Module { val io = IO(new Bundle { val inner = Input(new Bundle { val x = UInt(8.W) }) - }) + }).suggestName("io") } new TestModule() } } - // Test ignored because the compatibility null-inserting autoclonetype doesn't trip this - ignore should "fail on anonymous doubly-nested inner bundle with clear error" in { - intercept[ChiselException] { extractCause[ChiselException] { ChiselStage.elaborate { - class Outer(val w: Int) extends Module { - class Middle(val w: Int) { - def getIO: Bundle = new Bundle { - val in = Input(UInt(w.W)) + if (usingPlugin) { + // This works with the plugin, but is a null pointer exception when using reflective autoclonetype + it should "support an anonymous doubly-nested inner bundle" in { + elaborate { + class Outer(val w: Int) extends Module { + class Middle(val w: Int) { + def getIO: Bundle = new Bundle { + val in = Input(UInt(w.W)) + } } + val io = IO(new Bundle {}).suggestName("io") + val myWire = Wire((new Middle(w)).getIO) } - val io = IO(new Bundle {}) - val myWire = Wire((new Middle(w)).getIO) + new Outer(2) } - new Outer(2) - }}}.getMessage should include("Unable to determine instance") + } + + it should "support anonymous Inner bundles that capture type parameters from outer Bundles" in { + elaborate(new MultiIOModule { + class MyBundle[T <: Data](n: Int, gen: T) extends Bundle { + val foo = new Bundle { + val x = Input(Vec(n, gen)) + } + val bar = Output(Option(new { def mkBundle = new Bundle { val x = Vec(n, gen) }}).get.mkBundle) + } + val io = IO(new MyBundle(4, UInt(8.W))) + val myWire = WireInit(io.foo) + val myWire2 = WireInit(io.bar) + io.bar.x := io.foo.x + }) + } } } diff --git a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala b/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala deleted file mode 100644 index 28673495..00000000 --- a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chiselTests -import Chisel.ChiselException -import chisel3.stage.ChiselStage -import org.scalatest._ -import org.scalatest.matchers.should.Matchers - -class MissingCloneBindingExceptionSpec extends ChiselFlatSpec with Matchers with Utils { - behavior of "missing cloneType in Chisel3" - ( the [ChiselException] thrownBy extractCause[ChiselException] { - import chisel3._ - - class Test extends Module { - class TestIO(w: Int) extends Bundle { - val a = Input(Vec(4, UInt(w.W))) - } - - val io = IO(new TestIO(32)) - } - - class TestTop extends Module { - val io = IO(new Bundle {}) - - val subs = VecInit(Seq.fill(2) { - Module(new Test).io - }) - } - - ChiselStage.elaborate(new TestTop) - }).getMessage should include("make all parameters immutable") - - behavior of "missing cloneType in Chisel2" - ( the [ChiselException] thrownBy extractCause[ChiselException] { - import Chisel._ - - class Test extends Module { - class TestIO(w: Int) extends Bundle { - val a = Vec(4, UInt(width = w)).asInput - } - - val io = IO(new TestIO(32)) - } - - class TestTop extends Module { - val io = IO(new Bundle {}) - - val subs = Vec.fill(2) { - Module(new Test).io - } - } - - ChiselStage.elaborate(new TestTop) - }).getMessage should include("make all parameters immutable") -} -- cgit v1.2.3 From 3bea6167159737b379f37031c3beef27337be06d Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Fri, 12 Mar 2021 15:33:58 -0800 Subject: [plugin] Disable BundleComponent by default, add option to enable --- src/test/scala/chiselTests/AutoClonetypeSpec.scala | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index a3da109a..a6e5562a 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -4,6 +4,7 @@ package chiselTests import chisel3._ import chisel3.testers.TestUtils +import chisel3.util.QueueIO class BundleWithIntArg(val i: Int) extends Bundle { val out = UInt(i.W) @@ -65,6 +66,11 @@ class NestedAnonymousBundle extends Bundle { // Not necessarily good style (and not necessarily recommended), but allowed to preserve compatibility. class BundleWithArgumentField(val x: Data, val y: Data) extends Bundle +// Needs to be top-level so that reflective autoclonetype works +class InheritingBundle extends QueueIO(UInt(8.W), 8) { + val error = Output(Bool()) +} + // TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802 class AutoClonetypeSpec extends ChiselFlatSpec with Utils { val usingPlugin: Boolean = TestUtils.usingPlugin @@ -253,10 +259,21 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { elaborate(new MyModule(3)) } + behavior of "Compiler Plugin Autoclonetype" + + // Necessary test for 3.4.x, but we will break this (for non-plugin users) in 3.5 + it should "NOT break code that extends chisel3.util Bundles (whether they use the plugin or not)" in { + class MyModule extends MultiIOModule { + val io = IO(new InheritingBundle) + io.deq <> io.enq + io.count := 0.U + io.error := true.B + } + elaborate(new MyModule) + } + // New tests from the plugin if (usingPlugin) { - behavior of "Compiler Plugin Autoclonetype" - it should "support Bundles with non-val parameters" in { class MyBundle(i: Int) extends Bundle { val foo = UInt(i.W) -- cgit v1.2.3 From 96436ae018b3631decb8384ce404ada0daa5d645 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Mon, 15 Mar 2021 16:55:06 -0700 Subject: allowReflectiveAutoCloneType must work outside of Builder context (#1811) --- src/test/scala/chiselTests/AutoClonetypeSpec.scala | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index a6e5562a..e0e6b2f0 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -127,6 +127,10 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } } } + "Autoclonetype" should "work outside of a builder context" in { + new BundleWithIntArg(8).cloneType + } + def checkSubBundleInvalid() = { elaborate { new Module { val io = IO(new Bundle{}).suggestName("io") -- cgit v1.2.3 From 492a71d6d4d3acef39f29345835637bca028a089 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Wed, 17 Mar 2021 14:00:27 -0400 Subject: Fix incorrect usage of emitFirrtl in test (#1817) Change a test to use emitChirrtl instead of emitFirrtl. This test isn't supposed to be running the Scala FIRRTL Compiler, but the latter method causes this to happen. Signed-off-by: Schuyler Eldridge --- .../scala/chiselTests/experimental/verification/VerificationSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala index 52293abb..fe642156 100644 --- a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala +++ b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala @@ -28,7 +28,7 @@ class VerificationSpec extends ChiselPropSpec { } property("basic equality check should work") { - val fir = ChiselStage.emitFirrtl(new VerificationModule) + val fir = ChiselStage.emitChirrtl(new VerificationModule) val lines = fir.split("\n").map(_.trim) assertContains(lines, "cover(clock, _T, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 16:15]") assertContains(lines, "assume(clock, _T_2, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 18:18]") -- cgit v1.2.3 From 2a56c6540e914611ac12647e157aec4c5c595758 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Fri, 19 Mar 2021 01:28:56 +0800 Subject: Add toString method to BitPat (#1819) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/test/scala/chiselTests/util/BitPatSpec.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/scala/chiselTests/util/BitPatSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/BitPatSpec.scala b/src/test/scala/chiselTests/util/BitPatSpec.scala new file mode 100644 index 00000000..a6c0acf7 --- /dev/null +++ b/src/test/scala/chiselTests/util/BitPatSpec.scala @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.util + +import chisel3.util.BitPat +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + + +class BitPatSpec extends AnyFlatSpec with Matchers { + behavior of classOf[BitPat].toString + + it should "convert a BitPat to readable form" in { + val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32 + BitPat("b" + testPattern).toString should be (s"BitPat($testPattern)") + } +} -- cgit v1.2.3 From 26461d500f402310cb3adf914b636be3a3a8e442 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Tue, 23 Mar 2021 14:54:57 -0700 Subject: Make plugin autoclonetype always on (#1826) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/test/scala/chiselTests/AutoClonetypeSpec.scala | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index e0e6b2f0..57e00e99 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -265,19 +265,18 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { behavior of "Compiler Plugin Autoclonetype" - // Necessary test for 3.4.x, but we will break this (for non-plugin users) in 3.5 - it should "NOT break code that extends chisel3.util Bundles (whether they use the plugin or not)" in { - class MyModule extends MultiIOModule { - val io = IO(new InheritingBundle) - io.deq <> io.enq - io.count := 0.U - io.error := true.B - } - elaborate(new MyModule) - } - // New tests from the plugin if (usingPlugin) { + it should "NOT break code that extends chisel3.util Bundles if they use the plugin" in { + class MyModule extends MultiIOModule { + val io = IO(new InheritingBundle) + io.deq <> io.enq + io.count := 0.U + io.error := true.B + } + elaborate(new MyModule) + } + it should "support Bundles with non-val parameters" in { class MyBundle(i: Int) extends Bundle { val foo = UInt(i.W) -- cgit v1.2.3 From 2c7264a6d923e2d1dc645c8b7dec2add7fb6cfbc Mon Sep 17 00:00:00 2001 From: Deborah Soung Date: Wed, 21 Apr 2021 14:47:03 -0700 Subject: fixing context bug (#1874) --- src/test/scala/chiselTests/aop/InjectionSpec.scala | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/aop/InjectionSpec.scala b/src/test/scala/chiselTests/aop/InjectionSpec.scala index c9fa2e5e..a28501a5 100644 --- a/src/test/scala/chiselTests/aop/InjectionSpec.scala +++ b/src/test/scala/chiselTests/aop/InjectionSpec.scala @@ -5,6 +5,7 @@ package chiselTests.aop import chisel3.testers.{BasicTester, TesterDriver} import chiselTests.{ChiselFlatSpec, Utils} import chisel3._ +import chisel3.aop.Select import chisel3.aop.injecting.InjectingAspect import logger.{LogLevel, LogLevelAnnotation} @@ -14,6 +15,11 @@ object InjectionHierarchy { val moduleSubmoduleA = Module(new SubmoduleA) } + class MultiModuleInjectionTester extends BasicTester { + val subA0 = Module(new SubmoduleA) + val subA1 = Module(new SubmoduleA) + } + class SubmoduleA extends Module { val io = IO(new Bundle { val out = Output(Bool()) @@ -104,6 +110,17 @@ class InjectionSpec extends ChiselFlatSpec with Utils { } ) + val multiModuleInjectionAspect = InjectingAspect( + { top: MultiModuleInjectionTester => + Select.collectDeep(top) { case m: SubmoduleA => m } + }, + { m: Module => + val wire = Wire(Bool()) + wire := m.reset.asBool() + dontTouch(wire) + stop() + } + ) "Test" should "pass if inserted the correct values" in { assertTesterPasses{ new AspectTester(Seq(0, 1, 2)) } @@ -142,4 +159,12 @@ class InjectionSpec extends ChiselFlatSpec with Utils { Seq(addingExternalModules) ++ TesterDriver.verilatorOnly ) } + + "Injection into multiple submodules of the same class" should "work" in { + assertTesterPasses( + {new MultiModuleInjectionTester}, + Nil, + Seq(multiModuleInjectionAspect) ++ TesterDriver.verilatorOnly + ) + } } -- cgit v1.2.3 From 6deb379b1d8bafc81a605f60476bf0f24eac60b4 Mon Sep 17 00:00:00 2001 From: Chick Markley Date: Tue, 27 Apr 2021 12:17:17 -0700 Subject: Introduce VecLiterals (#1834) This PR provides for support for Vec literals. They can be one of two forms Inferred: ``` Vec.Lit(0x1.U, 0x2.U) ``` or explicit: ``` Vec(2, UInt(4.W)).Lit(0 -> 0x1.U, 1 -> 0x2.U) ``` - Explicit form allows for partial, or sparse, literals. - Vec literals can be used as Register initializers - Arbitrary nesting (consistent with type constraints is allowed)--- src/test/scala/chiselTests/BundleLiteralSpec.scala | 23 +- src/test/scala/chiselTests/ChiselSpec.scala | 25 +- src/test/scala/chiselTests/VecLiteralSpec.scala | 526 +++++++++++++++++++++ 3 files changed, 560 insertions(+), 14 deletions(-) create mode 100644 src/test/scala/chiselTests/VecLiteralSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/BundleLiteralSpec.scala b/src/test/scala/chiselTests/BundleLiteralSpec.scala index 2a3ce2c9..b4adde4a 100644 --- a/src/test/scala/chiselTests/BundleLiteralSpec.scala +++ b/src/test/scala/chiselTests/BundleLiteralSpec.scala @@ -6,9 +6,8 @@ import chisel3._ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.experimental.BundleLiterals._ -import chisel3.experimental.BundleLiteralException -import chisel3.experimental.ChiselEnum -import chisel3.experimental.FixedPoint +import chisel3.experimental.VecLiterals.AddVecLiteralConstructor +import chisel3.experimental.{BundleLiteralException, ChiselEnum, ChiselRange, FixedPoint, Interval} class BundleLiteralSpec extends ChiselFlatSpec with Utils { object MyEnum extends ChiselEnum { @@ -76,6 +75,24 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils { } } } + "bundle literals of vec literals" should "work" in { + assertTesterPasses(new BasicTester { + val range = range"[0,4].2" + val bundleWithVecs = new Bundle { + val a = Vec(2, UInt(4.W)) + val b = Vec(2, Interval(range)) + }.Lit( + _.a -> Vec(2, UInt(4.W)).Lit(0 -> 0xA.U, 1 -> 0xB.U), + _.b -> Vec(2, Interval(range)).Lit(0 -> (1.5).I(range), 1 -> (0.25).I(range)) + ) + chisel3.assert(bundleWithVecs.a(0) === 0xA.U) + chisel3.assert(bundleWithVecs.a(1) === 0xB.U) + chisel3.assert(bundleWithVecs.b(0) === (1.5).I(range)) + chisel3.assert(bundleWithVecs.b(1) === (0.25).I(range)) + stop() + }) + } + "partial bundle literals" should "work in RTL" in { assertTesterPasses{ new BasicTester{ val bundleLit = (new MyBundle).Lit(_.a -> 42.U) diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 8df680d6..9503089a 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -2,22 +2,22 @@ package chiselTests -import org.scalatest._ -import org.scalatest.prop._ -import org.scalatest.flatspec.AnyFlatSpec -import org.scalacheck._ import chisel3._ -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.aop.Aspect +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation} import chisel3.testers._ -import firrtl.{AnnotationSeq, CommonOptions, EmittedVerilogCircuitAnnotation, ExecutionOptionsManager, FirrtlExecutionFailure, FirrtlExecutionSuccess, HasFirrtlOptions} -import firrtl.annotations.{Annotation, DeletedAnnotation} +import firrtl.annotations.Annotation import firrtl.util.BackendCompilationUtilities +import firrtl.{AnnotationSeq, EmittedVerilogCircuitAnnotation} +import org.scalacheck._ +import org.scalatest._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.should._ +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + import java.io.ByteArrayOutputStream import java.security.Permission - -import chisel3.aop.Aspect -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation} -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scala.reflect.ClassTag /** Common utility functions for Chisel unit tests. */ @@ -90,6 +90,9 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { /** Spec base class for BDD-style testers. */ abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matchers +/** Spec base class for BDD-style testers. */ +abstract class ChiselFreeSpec extends AnyFreeSpec with ChiselRunners with Matchers + /** Spec base class for property-based testers. */ abstract class ChiselPropSpec extends PropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala new file mode 100644 index 00000000..d11289e1 --- /dev/null +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor +import chisel3.experimental.VecLiterals._ +import chisel3.experimental.{ChiselEnum, FixedPoint, VecLiteralException} +import chisel3.stage.ChiselStage +import chisel3.testers.BasicTester +import chisel3.util.Counter +import scala.language.reflectiveCalls + +class VecLiteralSpec extends ChiselFreeSpec with Utils { + object MyEnum extends ChiselEnum { + val sA, sB, sC = Value + } + object MyEnumB extends ChiselEnum { + val sA, sB = Value + } + + "Vec literals should work with chisel Enums" in { + val enumVec = Vec(3, MyEnum()).Lit(0 -> MyEnum.sA, 1 -> MyEnum.sB, 2-> MyEnum.sC) + enumVec(0).toString should include (MyEnum.sA.toString) + enumVec(1).toString should include (MyEnum.sB.toString) + enumVec(2).toString should include (MyEnum.sC.toString) + } + + "improperly constructed vec literals should be detected" - { + "indices in vec literal muse be greater than zero and less than length" in { + val e = intercept[VecLiteralException] { + Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 3 -> 4.U, -2 -> 7.U) + } + e.getMessage should include ( + "VecLiteral: The following indices (2,3,-2) are less than zero or greater or equal to than Vec length" + ) + } + + "indices in vec literals must not be repeated" in { + val e = intercept[VecLiteralException] { + Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 2 -> 3.U, 2 -> 3.U, 3 -> 4.U) + } + e.getMessage should include("VecLiteral: has duplicated indices 2(3 times)") + } + "lits must fit in vec element width" in { + val e = intercept[VecLiteralException] { + Vec(2, SInt(4.W)).Lit(0 -> 0xab.S, 1 -> 0xbc.S) + } + e.getMessage should include( + "VecLiteral: Vec[SInt<4>] has the following incorrectly typed or sized initializers: " + + "0 -> SInt<9>(171),1 -> SInt<9>(188)" + ) + } + + "all lits must be the same type but width can be equal or smaller than the Vec's element width" in { + val v = Vec(2, SInt(4.W)).Lit(0 -> 1.S, 1 -> -2.S) + v(0).toString should include(1.S(4.W).toString) + v(1).toString should include((-2).S(4.W).toString) + v.toString should include ("SInt<4>[2](0=SLit(1,<4>), 1=SLit(-2,<4>)") + } + + "all lits must be the same type but width cannot be greater than Vec's element width" in { + val e = intercept[VecLiteralException] { + val v = Vec(2, SInt(4.W)).Lit(0 -> 11.S, 1 -> -0xffff.S) + } + e.getMessage should include( + "VecLiteral: Vec[SInt<4>] has the following incorrectly typed or sized initializers: 0 -> SInt<5>(11),1 -> SInt<17>(-65535)" + ) + } + } + + //NOTE: I had problems where this would not work if this class declaration was inside test scope + class HasVecInit extends Module { + val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + val y = RegInit(initValue) + } + + "Vec literals should work when used to initialize a reg of vec" in { + val firrtl = (new ChiselStage).emitFirrtl(new HasVecInit, args = Array("--full-stacktrace")) + firrtl should include("""_y_WIRE[0] <= UInt<8>("hab")""") + firrtl should include("""_y_WIRE[1] <= UInt<8>("hcd")""") + firrtl should include("""_y_WIRE[2] <= UInt<8>("hef")""") + firrtl should include("""_y_WIRE[3] <= UInt<8>("hff")""") + firrtl should include(""" reset => (reset, _y_WIRE)""".stripMargin) + } + + //NOTE: I had problems where this would not work if this class declaration was inside test scope + class HasPartialVecInit extends Module { + val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + val y = RegInit(initValue) + } + + "Vec literals should work when used to partially initialize a reg of vec" in { + val firrtl = (new ChiselStage).emitFirrtl(new HasPartialVecInit, args = Array("--full-stacktrace")) + firrtl should include("""_y_WIRE[0] <= UInt<8>("hab")""") + firrtl should include("""_y_WIRE[1] is invalid""") + firrtl should include("""_y_WIRE[2] <= UInt<8>("hef")""") + firrtl should include("""_y_WIRE[3] <= UInt<8>("hff")""") + firrtl should include(""" reset => (reset, _y_WIRE)""".stripMargin) + } + + class ResetRegWithPartialVecLiteral extends Module { + val in = IO(Input(Vec(4, UInt(8.W)))) + val out = IO(Output(Vec(4, UInt(8.W)))) + val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + val y = RegInit(initValue) + when(in(1) > 0.U) { + y(1) := in(1) + } + when(in(2) > 0.U) { + y(2) := in(2) + } + out := y + } + + "Vec literals should only init specified fields when used to partially initialize a reg of vec" in { + println(ChiselStage.emitFirrtl(new ResetRegWithPartialVecLiteral)) + assertTesterPasses(new BasicTester { + val m = Module(new ResetRegWithPartialVecLiteral) + val (counter, wrapped) = Counter(true.B, 8) + m.in := DontCare + when(counter < 2.U) { + m.in(1) := 0xff.U + m.in(2) := 0xff.U + }.elsewhen(counter === 2.U) { + chisel3.assert(m.out(1) === 0xff.U) + chisel3.assert(m.out(2) === 0xff.U) + }.elsewhen(counter === 3.U) { + m.in(1) := 0.U + m.in(2) := 0.U + m.reset := true.B + }.elsewhen(counter > 2.U) { + // m.out(1) should not be reset, m.out(2) should be reset + chisel3.assert(m.out(1) === 0xff.U) + chisel3.assert(m.out(2) === 0xEF.U) + } + when(wrapped) { + stop() + } + }) + } + + "lowest of vec literal contains least significant bits and " in { + val y = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + y.litValue() should be(BigInt("FFEFCDAB", 16)) + } + + "the order lits are specified does not matter" in { + val y = Vec(4, UInt(8.W)).Lit(3 -> 0xFF.U(8.W), 2 -> 0xEF.U(8.W), 1 -> 0xCD.U(8.W), 0 -> 0xAB.U(8.W)) + y.litValue() should be(BigInt("FFEFCDAB", 16)) + } + + "regardless of the literals widths, packing should be done based on the width of the Vec's gen" in { + val z = Vec(4, UInt(8.W)).Lit(0 -> 0x2.U, 1 -> 0x2.U, 2 -> 0x2.U, 3 -> 0x3.U) + z.litValue() should be(BigInt("03020202", 16)) + } + + "packing sparse vec lits should not pack, litOption returns None" in { + // missing sub-listeral for index 2 + val z = Vec(4, UInt(8.W)).Lit(0 -> 0x2.U, 1 -> 0x2.U, 3 -> 0x3.U) + + z.litOption should be(None) + } + + "registers can be initialized with a Vec literal" in { + assertTesterPasses(new BasicTester { + val y = RegInit(Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))) + chisel3.assert(y.asUInt === BigInt("FFEFCDAB", 16).U) + stop() + }) + } + + "how does asUInt work" in { + assertTesterPasses(new BasicTester { + val vec1 = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + + val vec2 = VecInit(Seq(0xDD.U, 0xCC.U, 0xBB.U, 0xAA.U)) + printf("vec1 %x\n", vec1.asUInt()) + printf("vec2 %x\n", vec2.asUInt()) + stop() + }) + } + + "Vec literals uint conversion" in { + class M1 extends Module { + val out1 = IO(Output(UInt(64.W))) + val out2 = IO(Output(UInt(64.W))) + + val v1 = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + out1 := v1.asUInt + + val v2 = VecInit(0xDD.U(16.W), 0xCC.U, 0xBB.U, 0xAA.U) + out2 := v2.asUInt + } + + assertTesterPasses(new BasicTester { + val m = Module(new M1) + chisel3.assert(m.out1 === m.out2) + stop() + }) + } + + "VecLits should work properly with .asUInt" in { + val outsideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + + assertTesterPasses { + new BasicTester { + chisel3.assert(outsideVecLit(0) === 0xDD.U, s"v(0)") + stop() + } + } + } + + "bundle literals should work in RTL" in { + val outsideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + + assertTesterPasses { + new BasicTester { + chisel3.assert(outsideVecLit(0) === 0xDD.U, s"v(0)") + chisel3.assert(outsideVecLit(1) === 0xCC.U) + chisel3.assert(outsideVecLit(2) === 0xBB.U) + chisel3.assert(outsideVecLit(3) === 0xAA.U) + + chisel3.assert(outsideVecLit.litValue().U === outsideVecLit.asUInt()) + + val insideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + chisel3.assert(insideVecLit(0) === 0xDD.U) + chisel3.assert(insideVecLit(1) === 0xCC.U) + chisel3.assert(insideVecLit(2) === 0xBB.U) + chisel3.assert(insideVecLit(3) === 0xAA.U) + + chisel3.assert(insideVecLit(0) === outsideVecLit(0)) + chisel3.assert(insideVecLit(1) === outsideVecLit(1)) + chisel3.assert(insideVecLit(2) === outsideVecLit(2)) + chisel3.assert(insideVecLit(3) === outsideVecLit(3)) + + val vecWire1 = Wire(Vec(4, UInt(16.W))) + vecWire1 := outsideVecLit + + chisel3.assert(vecWire1(0) === 0xDD.U) + chisel3.assert(vecWire1(1) === 0xCC.U) + chisel3.assert(vecWire1(2) === 0xBB.U) + chisel3.assert(vecWire1(3) === 0xAA.U) + + val vecWire2 = Wire(Vec(4, UInt(16.W))) + vecWire2 := insideVecLit + + chisel3.assert(vecWire2(0) === 0xDD.U) + chisel3.assert(vecWire2(1) === 0xCC.U) + chisel3.assert(vecWire2(2) === 0xBB.U) + chisel3.assert(vecWire2(3) === 0xAA.U) + + stop() + } + } + } + + "partial vec literals should work in RTL" in { + assertTesterPasses{ new BasicTester{ + val vecLit = Vec(4, UInt(8.W)).Lit(0 -> 42.U, 2 -> 5.U) + chisel3.assert(vecLit(0) === 42.U) + chisel3.assert(vecLit(2) === 5.U) + + val vecWire = Wire(Vec(4, UInt(8.W))) + vecWire := vecLit + + chisel3.assert(vecWire(0) === 42.U) + chisel3.assert(vecWire(2) === 5.U) + + stop() + }} + } + + "nested vec literals should be constructable" in { + val outerVec = Vec(2, Vec(3, UInt(4.W))).Lit( + 0 -> Vec(3, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + 1 -> Vec(3, UInt(4.W)).Lit(0 -> 4.U, 1 -> 5.U, 2 -> 6.U) + ) + + outerVec.litValue() should be (BigInt("654321", 16)) + outerVec(0).litValue() should be (BigInt("321", 16)) + outerVec(1).litValue() should be (BigInt("654", 16)) + outerVec(0)(0).litValue() should be (BigInt(1)) + outerVec(0)(1).litValue() should be (BigInt(2)) + outerVec(0)(2).litValue() should be (BigInt(3)) + outerVec(1)(0).litValue() should be (BigInt(4)) + outerVec(1)(1).litValue() should be (BigInt(5)) + outerVec(1)(2).litValue() should be (BigInt(6)) + } + + "contained vecs should work" in { + assertTesterPasses{ new BasicTester { + val outerVec = Vec(2, Vec(3, UInt(4.W))).Lit( + 0 -> Vec(3, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + 1 -> Vec(3, UInt(4.W)).Lit(0 -> 4.U, 1 -> 5.U, 2 -> 6.U) + ) + + chisel3.assert(outerVec(0)(0) === 1.U) + chisel3.assert(outerVec(0)(1) === 2.U) + chisel3.assert(outerVec(0)(2) === 3.U) + chisel3.assert(outerVec(1)(0) === 4.U) + chisel3.assert(outerVec(1)(1) === 5.U) + chisel3.assert(outerVec(1)(2) === 6.U) + + val v0 = outerVec(0) + val v1 = outerVec(1) + chisel3.assert(v0(0) === 1.U) + chisel3.assert(v0(1) === 2.U) + chisel3.assert(v0(2) === 3.U) + chisel3.assert(v1(0) === 4.U) + chisel3.assert(v1(1) === 5.U) + chisel3.assert(v1(2) === 6.U) + + stop() + }} + } + + //TODO: decide what behavior here should be + "This doesn't work should it" ignore { + assertTesterPasses { + new BasicTester { + def vecFactory = Vec(2, FixedPoint(8.W, 4.BP)) + + val vecWire1 = Wire(Output(vecFactory)) + val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP)) + val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP)) + + vecWire1 := vecLit1 + vecWire1 := vecLit2 + printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt(), vecWire1(1).asUInt()) + chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP)) + chisel3.assert(vecWire1(1) === (3.25).F(8.W, 4.BP)) + stop() + } + } + } + + "partially initialized Vec literals should assign" in { + assertTesterPasses { + new BasicTester { + def vecFactory = Vec(2, FixedPoint(8.W, 4.BP)) + + val vecWire1 = Wire(Output(vecFactory)) + val vecWire2 = Wire(Output(vecFactory)) + val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP)) + val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP)) + + vecWire1 := vecLit1 + vecWire2 := vecLit2 + vecWire1(1) := (0.5).F(8.W, 4.BP) + printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt(), vecWire1(1).asUInt()) + chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP)) + chisel3.assert(vecWire1(1) === (0.5).F(8.W, 4.BP)) // Last connect won + chisel3.assert(vecWire2(1) === (3.25).F(8.W, 4.BP)) + stop() + } + } + } + + "Vec literals should work as register reset values" in { + assertTesterPasses { + new BasicTester { + val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 1 -> 0xB.U, 2 -> 0xC.U)) + r := (r.asUInt + 1.U).asTypeOf(Vec(3, UInt(11.W))) // prevent constprop + + // check reset values on first cycle out of reset + chisel3.assert(r(0) === 0xA.U) + chisel3.assert(r(1) === 0xB.U) + chisel3.assert(r(2) === 0xC.U) + stop() + } + } + } + + "partially initialized Vec literals should work as register reset values" in { + assertTesterPasses { + new BasicTester { + val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 2 -> 0xC.U)) + r := (r.asUInt + 1.U).asTypeOf(Vec(3, UInt(11.W))) // prevent constprop + // check reset values on first cycle out of reset + chisel3.assert(r(0) === 0xA.U) + chisel3.assert(r(2) === 0xC.U) + stop() + } + } + } + + "Fields extracted from Vec Literals should work as register reset values" in { + assertTesterPasses { + new BasicTester { + val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 2 -> 0xC.U).apply(0)) + r := r + 1.U // prevent const prop + chisel3.assert(r === 0xA.U) // coming out of reset + stop() + } + } + } + + "DontCare fields extracted from Vec Literals should work as register reset values" in { + assertTesterPasses { + new BasicTester { + val r = RegInit(Vec(3, Bool()).Lit(0 -> true.B).apply(2)) + r := reset.asBool + printf(p"r = $r\n") // Can't assert because reset value is DontCare + stop() + } + } + } + + "DontCare fields extracted from Vec Literals should work in other Expressions" in { + assertTesterPasses { + new BasicTester { + val x = Vec(3, Bool()).Lit(0 -> true.B).apply(2) || true.B + chisel3.assert(x === true.B) + stop() + } + } + } + + "vec literals with non-literal values should fail" in { + val exc = intercept[VecLiteralException] { + extractCause[VecLiteralException] { + ChiselStage.elaborate { + new RawModule { + (Vec(3, UInt(11.W)).Lit(0 -> UInt())) + } + } + } + } + exc.getMessage should include("field 0 specified with non-literal value UInt") + } + + "vec literals are instantiated on connect" in { + class VecExample5 extends RawModule { + val out = IO(Output(Vec(2, UInt(4.W)))) + val bundle = Vec(2, UInt(4.W)).Lit( + 0 -> 0xa.U, + 1 -> 0xb.U + ) + out := bundle + } + + val firrtl = (new chisel3.stage.ChiselStage).emitFirrtl(new VecExample5, args = Array("--full-stacktrace")) + firrtl should include("""out[0] <= UInt<4>("ha")""") + firrtl should include("""out[1] <= UInt<4>("hb")""") + } + + class SubBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(4.W) + } + + class VecExample extends RawModule { + val out = IO(Output(Vec(2, new SubBundle))) + val bundle = Vec(2, new SubBundle).Lit( + 0 -> (new SubBundle).Lit(_.foo -> 42.U, _.bar -> 22.U), + 1 -> (new SubBundle).Lit(_.foo -> 7.U, _.bar -> 3.U) + ) + out := bundle + } + + "vec literals can contain bundles" in { + val chirrtl = (new chisel3.stage.ChiselStage).emitChirrtl(new VecExample, args = Array("--full-stacktrace")) + chirrtl should include("""out[0].bar <= UInt<5>("h016")""") + chirrtl should include("""out[0].foo <= UInt<6>("h02a")""") + chirrtl should include("""out[1].bar <= UInt<2>("h03")""") + chirrtl should include("""out[1].foo <= UInt<3>("h07")""") + + } + + "vec literals can have bundle children" in { + val vec = Vec(2, new SubBundle).Lit( + 0 -> (new SubBundle).Lit(_.foo -> 0xab.U, _.bar -> 0xc.U), + 1 -> (new SubBundle).Lit(_.foo -> 0xde.U, _.bar -> 0xf.U) + ) + vec.litValue().toString(16) should be("defabc") + } + + "vec literals can have bundle children assembled incrementally" in { + val bundle1 = (new SubBundle).Lit(_.foo -> 0xab.U, _.bar -> 0xc.U) + val bundle2 = (new SubBundle).Lit(_.foo -> 0xde.U, _.bar -> 0xf.U) + + bundle1.litValue().toString(16) should be("abc") + bundle2.litValue().toString(16) should be("def") + + val vec = Vec(2, new SubBundle).Lit(0 -> bundle1, 1 -> bundle2) + + vec.litValue().toString(16) should be("defabc") + } + + "bundles can contain vec lits" in { + val vec1 = Vec(3, UInt(4.W)).Lit(0 -> 0xa.U, 1 -> 0xb.U, 2 -> 0xc.U) + val vec2 = Vec(2, UInt(4.W)).Lit(0 -> 0xd.U, 1 -> 0xe.U) + val bundle = (new Bundle { + val foo = Vec(3, UInt(4.W)) + val bar = Vec(2, UInt(4.W)) + }).Lit(_.foo -> vec1, _.bar -> vec2) + bundle.litValue().toString(16) should be("cbaed") + } + + "bundles can contain vec lits in-line" in { + val bundle = (new Bundle { + val foo = Vec(3, UInt(4.W)) + val bar = Vec(2, UInt(4.W)) + }).Lit( + _.foo -> Vec(3, UInt(4.W)).Lit(0 -> 0xa.U, 1 -> 0xb.U, 2 -> 0xc.U), + _.bar -> Vec(2, UInt(4.W)).Lit(0 -> 0xd.U, 1 -> 0xe.U) + ) + bundle.litValue().toString(16) should be("cbaed") + } + + "Vec.Lit is a trivial Vec literal factory" in { + val vec = Vec.Lit(0xa.U, 0xb.U) + vec(0).litValue() should be(0xa) + vec(1).litValue() should be(0xb) + } + + "Vec.Lit bases it's element width on the widest literal supplied" in { + val vec = Vec.Lit(0xa.U, 0xbbbb.U) + vec(0).litValue() should be(0xa) + vec(1).litValue() should be(0xbbbb) + vec.length should be(2) + vec.getWidth should be(16 * 2) + vec.litValue() should be(BigInt("bbbb000a", 16)) + } +} -- cgit v1.2.3 From 4d8fed00225d15221cf32177ea9147b20d0b91f7 Mon Sep 17 00:00:00 2001 From: Kevin Laeufer Date: Thu, 29 Apr 2021 11:52:20 -0700 Subject: verification: guard statements with module reset (#1891) --- .../experimental/verification/VerificationSpec.scala | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala index fe642156..86d6418c 100644 --- a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala +++ b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala @@ -30,8 +30,15 @@ class VerificationSpec extends ChiselPropSpec { property("basic equality check should work") { val fir = ChiselStage.emitChirrtl(new VerificationModule) val lines = fir.split("\n").map(_.trim) + + // reset guard around the verification statement + assertContains(lines, "when _T_2 : @[VerificationSpec.scala 16:15]") assertContains(lines, "cover(clock, _T, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 16:15]") - assertContains(lines, "assume(clock, _T_2, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 18:18]") - assertContains(lines, "assert(clock, _T_3, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 19:18]") + + assertContains(lines, "when _T_6 : @[VerificationSpec.scala 18:18]") + assertContains(lines, "assume(clock, _T_4, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 18:18]") + + assertContains(lines, "when _T_9 : @[VerificationSpec.scala 19:18]") + assertContains(lines, "assert(clock, _T_7, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 19:18]") } } -- cgit v1.2.3 From c5861176887bfa529277e686df09a42aeceb6cd7 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 29 Apr 2021 16:18:06 -0700 Subject: Scala 2.13 support (#1751) --- src/test/scala/chiselTests/ChiselSpec.scala | 2 +- src/test/scala/chiselTests/ExtModule.scala | 16 ++++++++-------- src/test/scala/examples/VendingMachineUtils.scala | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 9503089a..37c4a2b7 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -292,7 +292,7 @@ trait Utils { exceptions.collectFirst{ case a: A => a } match { case Some(a) => throw a case None => exceptions match { - case Nil => Unit + case Nil => () case h :: t => throw h } } diff --git a/src/test/scala/chiselTests/ExtModule.scala b/src/test/scala/chiselTests/ExtModule.scala index 0c3a0633..161b6f5f 100644 --- a/src/test/scala/chiselTests/ExtModule.scala +++ b/src/test/scala/chiselTests/ExtModule.scala @@ -9,7 +9,7 @@ import chisel3.testers.{BasicTester, TesterDriver} // Avoid collisions with regular BlackBox tests by putting ExtModule blackboxes // in their own scope. -package ExtModule { +package extmoduletests { import chisel3.experimental.ExtModule @@ -25,8 +25,8 @@ package ExtModule { } class ExtModuleTester extends BasicTester { - val blackBoxPos = Module(new ExtModule.BlackBoxInverter) - val blackBoxNeg = Module(new ExtModule.BlackBoxInverter) + val blackBoxPos = Module(new extmoduletests.BlackBoxInverter) + val blackBoxNeg = Module(new extmoduletests.BlackBoxInverter) blackBoxPos.in := 1.U blackBoxNeg.in := 0.U @@ -42,10 +42,10 @@ class ExtModuleTester extends BasicTester { */ class MultiExtModuleTester extends BasicTester { - val blackBoxInvPos = Module(new ExtModule.BlackBoxInverter) - val blackBoxInvNeg = Module(new ExtModule.BlackBoxInverter) - val blackBoxPassPos = Module(new ExtModule.BlackBoxPassthrough) - val blackBoxPassNeg = Module(new ExtModule.BlackBoxPassthrough) + val blackBoxInvPos = Module(new extmoduletests.BlackBoxInverter) + val blackBoxInvNeg = Module(new extmoduletests.BlackBoxInverter) + val blackBoxPassPos = Module(new extmoduletests.BlackBoxPassthrough) + val blackBoxPassNeg = Module(new extmoduletests.BlackBoxPassthrough) blackBoxInvPos.in := 1.U blackBoxInvNeg.in := 0.U @@ -71,7 +71,7 @@ class ExtModuleSpec extends ChiselFlatSpec { "DataMirror.modulePorts" should "work with ExtModule" in { ChiselStage.elaborate(new Module { val io = IO(new Bundle { }) - val m = Module(new ExtModule.BlackBoxPassthrough) + val m = Module(new extmoduletests.BlackBoxPassthrough) assert(DataMirror.modulePorts(m) == Seq( "in" -> m.in, "out" -> m.out)) }) diff --git a/src/test/scala/examples/VendingMachineUtils.scala b/src/test/scala/examples/VendingMachineUtils.scala index 131256f8..6847768a 100644 --- a/src/test/scala/examples/VendingMachineUtils.scala +++ b/src/test/scala/examples/VendingMachineUtils.scala @@ -34,6 +34,6 @@ object VendingMachineUtils { value += incValue } } - outputs + outputs.toSeq } } -- cgit v1.2.3 From 7dd2d7db355d8dd9e1fc49ed7cd479ce5273b691 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Fri, 30 Apr 2021 05:12:24 +0000 Subject: add helper function to convert chirrtl to firrtl. (#1854) * add convert(chirrtl: cir.Circuit): fir.Circuit to convert chirrtl to firrtl. * add scaladoc. * add test. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/test/scala/chiselTests/stage/ChiselStageSpec.scala | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala index 167e414b..7b6a2d39 100644 --- a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala +++ b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala @@ -88,6 +88,13 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils { catchWrites { ChiselStage.convert(new Foo) } shouldBe a[Right[_, _]] } + ignore should "generate a FIRRTL circuit from a CHIRRTL circuit" in { + info("no files were written") + catchWrites { + ChiselStage.convert(ChiselStage.elaborate(new Foo)) + } shouldBe a[Right[_, _]] + } + behavior of "ChiselStage$.emitChirrtl" ignore should "generate a CHIRRTL string from a Chisel module" in { -- cgit v1.2.3 From 365a51a8ce692c85df60427e0562e89945d9797d Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Wed, 5 May 2021 13:18:57 -0400 Subject: Remove chisel3.stage.phases.DriverCompatibility (#1772) --- .../stage/phases/DriverCompatibilitySpec.scala | 71 ---------------------- 1 file changed, 71 deletions(-) delete mode 100644 src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala (limited to 'src/test') diff --git a/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala b/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala deleted file mode 100644 index b80d5298..00000000 --- a/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3.stage.phases - - -import chisel3.stage.{NoRunFirrtlCompilerAnnotation, ChiselOutputFileAnnotation} - -import firrtl.options.{OutputAnnotationFileAnnotation, StageOptions} -import firrtl.options.Viewer.view -import firrtl.stage.phases.DriverCompatibility.TopNameAnnotation -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers - -class DriverCompatibilitySpec extends AnyFlatSpec with Matchers { - - behavior of classOf[DriverCompatibility.AddImplicitOutputFile].toString - - it should "do nothing if a ChiselOutputFileAnnotation is present" in { - val annotations = Seq( - ChiselOutputFileAnnotation("Foo"), - TopNameAnnotation("Bar") ) - (new DriverCompatibility.AddImplicitOutputFile).transform(annotations).toSeq should be (annotations) - } - - it should "add a ChiselOutputFileAnnotation derived from a TopNameAnnotation" in { - val annotations = Seq( TopNameAnnotation("Bar") ) - val expected = ChiselOutputFileAnnotation("Bar") +: annotations - (new DriverCompatibility.AddImplicitOutputFile).transform(annotations).toSeq should be (expected) - } - - behavior of classOf[DriverCompatibility.AddImplicitOutputAnnotationFile].toString - - it should "do nothing if an OutputAnnotationFileAnnotation is present" in { - val annotations = Seq( - OutputAnnotationFileAnnotation("Foo"), - TopNameAnnotation("Bar") ) - (new DriverCompatibility.AddImplicitOutputAnnotationFile).transform(annotations).toSeq should be (annotations) - } - - it should "add an OutputAnnotationFileAnnotation derived from a TopNameAnnotation" in { - val annotations = Seq( TopNameAnnotation("Bar") ) - val expected = OutputAnnotationFileAnnotation("Bar") +: annotations - (new DriverCompatibility.AddImplicitOutputAnnotationFile).transform(annotations).toSeq should be (expected) - } - - behavior of classOf[DriverCompatibility.DisableFirrtlStage].toString - - it should "add a NoRunFirrtlCompilerAnnotation if one does not exist" in { - val annos = Seq(NoRunFirrtlCompilerAnnotation) - val expected = DriverCompatibility.RunFirrtlCompilerAnnotation +: annos - (new DriverCompatibility.DisableFirrtlStage).transform(Seq.empty).toSeq should be (expected) - } - - it should "NOT add a NoRunFirrtlCompilerAnnotation if one already exists" in { - val annos = Seq(NoRunFirrtlCompilerAnnotation) - (new DriverCompatibility.DisableFirrtlStage).transform(annos).toSeq should be (annos) - } - - behavior of classOf[DriverCompatibility.ReEnableFirrtlStage].toString - - it should "NOT strip a NoRunFirrtlCompilerAnnotation if NO RunFirrtlCompilerAnnotation is present" in { - val annos = Seq(NoRunFirrtlCompilerAnnotation, DriverCompatibility.RunFirrtlCompilerAnnotation) - (new DriverCompatibility.ReEnableFirrtlStage).transform(annos).toSeq should be (Seq.empty) - } - - it should "strip a NoRunFirrtlCompilerAnnotation if a RunFirrtlCompilerAnnotation is present" in { - val annos = Seq(NoRunFirrtlCompilerAnnotation) - (new DriverCompatibility.ReEnableFirrtlStage).transform(annos).toSeq should be (annos) - } - -} -- cgit v1.2.3 From 361e4433ac6f0db8564415f07258ae151a48affe Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 6 May 2021 17:58:35 +0000 Subject: add ShiftRegisters to expose register inside ShiftRegister. (#1723) * add ShiftRegisters to expose register inside ShiftRegister. * use Seq.iter for oneline implementation.--- src/test/scala/chiselTests/Reg.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/Reg.scala b/src/test/scala/chiselTests/Reg.scala index d86fe8b4..21334aea 100644 --- a/src/test/scala/chiselTests/Reg.scala +++ b/src/test/scala/chiselTests/Reg.scala @@ -69,3 +69,21 @@ class ShiftRegisterSpec extends ChiselPropSpec { forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftResetTester(shift) } } } } + +class ShiftsTester(n: Int) extends BasicTester { + val (cntVal, done) = Counter(true.B, n) + val start = 23.U + val srs = ShiftRegisters(cntVal + start, n) + when(RegNext(done)) { + srs.zipWithIndex.foreach{ case (data, index) => + assert(data === (23 + n - 1 - index).U) + } + stop() + } +} + +class ShiftRegistersSpec extends ChiselPropSpec { + property("ShiftRegisters should shift") { + forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftsTester(shift) } } + } +} -- cgit v1.2.3 From c118facb82e43c010a6333c5281b956a7c9c7e20 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sun, 9 May 2021 13:32:48 +0000 Subject: Fix ShiftRegister with 0 delay. (#1903) * Add test to check ShiftRegister(s) with delay is 0. This should break ShiftRegister(x, 0) since last is not exist in a empty Seq. Originally, test only test 1 to 4, which missed a potential bug from #1723. * Fix ShiftRegister with 0 delay. if ShiftRegisters is empty, java will complain: ``` java.util.NoSuchElementException scala.collection.LinearSeqOptimized.last(LinearSeqOptimized.scala:150) ``` This fix this issue and return `in` directly when ShiftRegister size is 0. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/test/scala/chiselTests/Reg.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/Reg.scala b/src/test/scala/chiselTests/Reg.scala index 21334aea..a02e6fa5 100644 --- a/src/test/scala/chiselTests/Reg.scala +++ b/src/test/scala/chiselTests/Reg.scala @@ -7,6 +7,7 @@ import chisel3.util._ import chisel3.experimental.DataMirror import chisel3.stage.ChiselStage import chisel3.testers.BasicTester +import org.scalacheck.Gen class RegSpec extends ChiselFlatSpec { "Reg" should "be of the same type and width as t" in { @@ -55,18 +56,18 @@ class ShiftResetTester(n: Int) extends BasicTester { val start = 23.U val sr = ShiftRegister(cntVal + 23.U, n, 1.U, true.B) when(done) { - assert(sr === 1.U) + assert(sr === (if(n == 0) cntVal + 23.U else 1.U)) stop() } } class ShiftRegisterSpec extends ChiselPropSpec { property("ShiftRegister should shift") { - forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftTester(shift) } } + forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftTester(shift) } } } property("ShiftRegister should reset all values inside") { - forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftResetTester(shift) } } + forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftResetTester(shift) } } } } @@ -84,6 +85,6 @@ class ShiftsTester(n: Int) extends BasicTester { class ShiftRegistersSpec extends ChiselPropSpec { property("ShiftRegisters should shift") { - forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftsTester(shift) } } + forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftsTester(shift) } } } } -- cgit v1.2.3 From 8c9007365d038d23b94bb4d1a6a7f20448f951eb Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 20 May 2021 03:09:24 +0000 Subject: implement model checking API for chiseltest (#1910) * add os-lib to dependency. * implement EndToEndSMTBaseSpec * rename to SMTModelCheckingSpec * add documentation. * fix for review.--- .../scala/chiselTests/SMTModelCheckingSpec.scala | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/test/scala/chiselTests/SMTModelCheckingSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/SMTModelCheckingSpec.scala b/src/test/scala/chiselTests/SMTModelCheckingSpec.scala new file mode 100644 index 00000000..6a24ff10 --- /dev/null +++ b/src/test/scala/chiselTests/SMTModelCheckingSpec.scala @@ -0,0 +1,103 @@ +package chiselTests + +import chisel3.Module +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import firrtl.annotations.Annotation +import firrtl.options.{OutputAnnotationFileAnnotation, TargetDirAnnotation} +import firrtl.stage.OutputFileAnnotation +import firrtl.util.BackendCompilationUtilities.timeStamp +import logger.{LazyLogging, LogLevel, LogLevelAnnotation} +import org.scalatest.flatspec.AnyFlatSpec +import os._ + +/** [[SMTModelCheckingSpec]] use z3 and [[firrtl.backends.experimental.smt]] library + * to solve `assert/assume` in [[chisel3.experimental.verification]], + * It is a copy&paste version from `firrtl.backends.experimental.smt.end2end.EndToEndSMTBaseSpec` from firrtl + * Useful to check combinational logic and some small test. + */ +abstract class SMTModelCheckingSpec extends AnyFlatSpec { + def success = MCSuccess + + def fail(k: Int) = MCFail(k) + + def test(dut: () => Module, name: String, expected: MCResult, kmax: Int = 0, annos: Seq[Annotation] = Seq()): Unit = { + expected match { + case MCFail(k) => + assert(kmax >= k, s"Please set a kmax that includes the expected failing step! ($kmax < $expected)") + case _ => + } + // @todo rewrite BackendCompilationUtilities + val testBaseDir = os.pwd / "test_run_dir" / name + os.makeDir.all(testBaseDir) + val testDir = os.temp.dir(testBaseDir, timeStamp, deleteOnExit = false) + val res = (new ChiselStage).execute( + Array("-E", "experimental-smt2"), + Seq( + LogLevelAnnotation(LogLevel.Error), // silence warnings for tests + ChiselGeneratorAnnotation(dut), + TargetDirAnnotation(testDir.toString) + ) ++ annos + ) + val top = res.collectFirst{case OutputAnnotationFileAnnotation(top) => top}.get + assert(res.collectFirst { case _: OutputFileAnnotation => true }.isDefined) + val r = Z3ModelChecker.bmc(testDir, top, kmax) + assert(r == expected) + } +} + +private object Z3ModelChecker extends LazyLogging { + def bmc(testDir: Path, main: String, kmax: Int): MCResult = { + assert(kmax >= 0 && kmax < 50, "Trying to keep kmax in a reasonable range.") + Seq.tabulate(kmax + 1) { k => + val stepFile = testDir / s"${main}_step$k.smt2" + os.copy(testDir / s"$main.smt2", stepFile) + os.write.append(stepFile, + s"""${step(main, k)} + |(check-sat) + |""".stripMargin) + val success = executeStep(stepFile) + if (!success) return MCFail(k) + } + MCSuccess + } + + private def executeStep(path: Path): Boolean = { + val (out, ret) = executeCmd(path.toString) + assert(ret == 0, s"expected success (0), not $ret: `$out`\nz3 ${path.toString}") + assert(out == "sat\n" || out == "unsat\n", s"Unexpected output: $out") + out == "unsat\n" + } + + private def executeCmd(cmd: String): (String, Int) = { + val process = os.proc("z3", cmd).call(stderr = ProcessOutput.Readlines(logger.warn(_))) + (process.out.chunks.mkString, process.exitCode) + } + + private def step(main: String, k: Int): String = { + // define all states + (0 to k).map(ii => s"(declare-fun s$ii () $main$StateTpe)") ++ + // assert that init holds in state 0 + List(s"(assert ($main$Init s0))") ++ + // assert transition relation + (0 until k).map(ii => s"(assert ($main$Transition s$ii s${ii + 1}))") ++ + // assert that assumptions hold in all states + (0 to k).map(ii => s"(assert ($main$Assumes s$ii))") ++ + // assert that assertions hold for all but last state + (0 until k).map(ii => s"(assert ($main$Asserts s$ii))") ++ + // check to see if we can violate the assertions in the last state + List(s"(assert (not ($main$Asserts s$k)))") + }.mkString("\n") + + // the following suffixes have to match the ones in [[SMTTransitionSystemEncoder]] + private val Transition = "_t" + private val Init = "_i" + private val Asserts = "_a" + private val Assumes = "_u" + private val StateTpe = "_s" +} +sealed trait MCResult + +case object MCSuccess extends MCResult + +case class MCFail(k: Int) extends MCResult + -- cgit v1.2.3 From 1c1a4d7217574a938ad0ce529803fb991f9903f0 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 20 May 2021 06:50:10 +0000 Subject: Implement PLA (#1912) * implement pla * implement test for pla * implement inverter matrix of PLA generator * fix for review. Co-authored-by: Boyang Han --- .../chiselTests/util/experimental/PlaSpec.scala | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/test/scala/chiselTests/util/experimental/PlaSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/PlaSpec.scala b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala new file mode 100644 index 00000000..45ac012e --- /dev/null +++ b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala @@ -0,0 +1,80 @@ +package chiselTests.util.experimental + +import chisel3._ +import chisel3.stage.PrintFullStackTraceAnnotation +import chisel3.testers.BasicTester +import chisel3.util.{BitPat, pla} +import chiselTests.ChiselFlatSpec + +class PlaSpec extends ChiselFlatSpec { + "A 1-of-8 decoder (eg. 74xx138 without enables)" should "be generated correctly" in { + assertTesterPasses(new BasicTester { + val table = Seq( + (BitPat("b000"), BitPat("b00000001")), + (BitPat("b001"), BitPat("b00000010")), + (BitPat("b010"), BitPat("b00000100")), + (BitPat("b011"), BitPat("b00001000")), + (BitPat("b100"), BitPat("b00010000")), + (BitPat("b101"), BitPat("b00100000")), + (BitPat("b110"), BitPat("b01000000")), + (BitPat("b111"), BitPat("b10000000")), + ) + table.foreach { case (i, o) => + val (plaIn, plaOut) = pla(table) + plaIn := WireDefault(i.value.U(3.W)) + chisel3.assert(plaOut === o.value.U(8.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut) + } + stop() + }) + } + + "An active-low 1-of-8 decoder (eg. inverted 74xx138 without enables)" should "be generated correctly" in { + assertTesterPasses(new BasicTester { + val table = Seq( + (BitPat("b000"), BitPat("b00000001")), + (BitPat("b001"), BitPat("b00000010")), + (BitPat("b010"), BitPat("b00000100")), + (BitPat("b011"), BitPat("b00001000")), + (BitPat("b100"), BitPat("b00010000")), + (BitPat("b101"), BitPat("b00100000")), + (BitPat("b110"), BitPat("b01000000")), + (BitPat("b111"), BitPat("b10000000")), + ) + table.foreach { case (i, o) => + val (plaIn, plaOut) = pla(table, BitPat("b11111111")) + plaIn := WireDefault(i.value.U(3.W)) + chisel3.assert(plaOut === ~o.value.U(8.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut) + } + stop() + }) + } + + "A simple PLA" should "be generated correctly" in { + assertTesterPasses(new BasicTester { + val table = Seq( + (BitPat("b0000"), BitPat("b1")), + (BitPat("b0001"), BitPat("b1")), + (BitPat("b0010"), BitPat("b0")), + (BitPat("b0011"), BitPat("b1")), + (BitPat("b0100"), BitPat("b1")), + (BitPat("b0101"), BitPat("b0")), + (BitPat("b0110"), BitPat("b0")), + (BitPat("b0111"), BitPat("b0")), + (BitPat("b1000"), BitPat("b0")), + (BitPat("b1001"), BitPat("b0")), + (BitPat("b1010"), BitPat("b1")), + (BitPat("b1011"), BitPat("b0")), + (BitPat("b1100"), BitPat("b0")), + (BitPat("b1101"), BitPat("b1")), + (BitPat("b1110"), BitPat("b1")), + (BitPat("b1111"), BitPat("b1")), + ) + table.foreach { case (i, o) => + val (plaIn, plaOut) = pla(table) + plaIn := WireDefault(i.value.U(4.W)) + chisel3.assert(plaOut === o.value.U(1.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut) + } + stop() + }) + } +} -- cgit v1.2.3 From 6ccff4848fcf55cb1e865738a67f2bec25988ac8 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Wed, 26 May 2021 02:35:34 +0800 Subject: throw exception if BitPat width is 0 (#1920) * spot a bug when BitPat width is 0 * fix #1919 Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/test/scala/chiselTests/util/BitPatSpec.scala | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/BitPatSpec.scala b/src/test/scala/chiselTests/util/BitPatSpec.scala index a6c0acf7..ca8dd85c 100644 --- a/src/test/scala/chiselTests/util/BitPatSpec.scala +++ b/src/test/scala/chiselTests/util/BitPatSpec.scala @@ -14,4 +14,8 @@ class BitPatSpec extends AnyFlatSpec with Matchers { val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32 BitPat("b" + testPattern).toString should be (s"BitPat($testPattern)") } + + it should "not fail if BitPat width is 0" in { + intercept[IllegalArgumentException]{BitPat("b")} + } } -- cgit v1.2.3 From 820200b75242dde2a66c8103fd53eb10afc7ff6b Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 10 Jun 2021 17:32:08 -0400 Subject: Stop Emitting BlackBoxResourceAnno (#1954) * Change HasBlackBoxResource to Resolve Resources Change HasBlackBoxResource to resolve resources immediately and emit BlackBoxInlineAnno instead of a BlackBoxResourceAnno. This removes the need for a FIRRTL compiler to grok the Java Resource API in order to handle BlackBoxResourceAnno. Emit BlackBoxInlineAnno from HasExtModuleResource instead of BlackBoxResourceAnno. Signed-off-by: Schuyler Eldridge --- src/test/scala/chiselTests/BlackBoxImpl.scala | 15 +++++++++++++++ src/test/scala/chiselTests/ExtModuleImpl.scala | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/BlackBoxImpl.scala b/src/test/scala/chiselTests/BlackBoxImpl.scala index f8e16ad7..a9a6fa29 100644 --- a/src/test/scala/chiselTests/BlackBoxImpl.scala +++ b/src/test/scala/chiselTests/BlackBoxImpl.scala @@ -8,6 +8,7 @@ import chisel3._ import chisel3.util.{HasBlackBoxInline, HasBlackBoxResource, HasBlackBoxPath} import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import firrtl.FirrtlExecutionSuccess +import firrtl.transforms.BlackBoxNotFoundException import org.scalacheck.Test.Failed import org.scalatest.Succeeded import org.scalatest.freespec.AnyFreeSpec @@ -88,6 +89,15 @@ class UsesBlackBoxMinusViaPath extends Module { io.out := mod0.io.out } +class BlackBoxResourceNotFound extends HasBlackBoxResource { + val io = IO(new Bundle{}) + addResource("/missing.resource") +} + +class UsesMissingBlackBoxResource extends RawModule { + val foo = Module(new BlackBoxResourceNotFound) +} + class BlackBoxImplSpec extends AnyFreeSpec with Matchers { val targetDir = "test_run_dir" val stage = new ChiselStage @@ -114,5 +124,10 @@ class BlackBoxImplSpec extends AnyFreeSpec with Matchers { verilogOutput.delete() Succeeded } + "Resource files that do not exist produce Chisel errors" in { + assertThrows[BlackBoxNotFoundException]{ + ChiselStage.emitChirrtl(new UsesMissingBlackBoxResource) + } + } } } diff --git a/src/test/scala/chiselTests/ExtModuleImpl.scala b/src/test/scala/chiselTests/ExtModuleImpl.scala index f71a1335..e0a76201 100644 --- a/src/test/scala/chiselTests/ExtModuleImpl.scala +++ b/src/test/scala/chiselTests/ExtModuleImpl.scala @@ -11,6 +11,7 @@ import chisel3.util.{HasExtModuleInline, HasExtModulePath, HasExtModuleResource} import firrtl.FirrtlExecutionSuccess import firrtl.options.TargetDirAnnotation import firrtl.stage.FirrtlCircuitAnnotation +import firrtl.transforms.BlackBoxNotFoundException import org.scalacheck.Test.Failed import org.scalatest.{FreeSpec, Matchers, Succeeded} @@ -92,6 +93,15 @@ class UsesExtModuleMinusViaPath extends Module { io.out := mod0.io.out } +class ExtModuleResourceNotFound extends HasExtModuleResource { + val io = IO(new Bundle{}) + addResource("/missing.resource") +} + +class UsesMissingExtModuleResource extends RawModule { + val foo = Module(new ExtModuleResourceNotFound) +} + class ExtModuleImplSpec extends FreeSpec with Matchers { "ExtModule can have verilator source implementation" - { @@ -137,5 +147,11 @@ class ExtModuleImplSpec extends FreeSpec with Matchers { verilogOutput.exists() should be(true) verilogOutput.delete() } + + "Resource files that do not exist produce Chisel errors" in { + assertThrows[BlackBoxNotFoundException]{ + ChiselStage.emitChirrtl(new UsesMissingExtModuleResource) + } + } } } -- cgit v1.2.3 From 71575609ae7242585ed1008b8473acae1a42165e Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 6 May 2021 16:18:58 +0000 Subject: implement TruthTable to represent a decode table. --- .../util/experimental/TruthTableSpec.scala | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala new file mode 100644 index 00000000..ed79f7f5 --- /dev/null +++ b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.util.experimental + +import chisel3.util.BitPat +import chisel3.util.experimental.decode.TruthTable +import org.scalatest.flatspec.AnyFlatSpec + +class TruthTableSpec extends AnyFlatSpec { + val table = TruthTable( + Map( + // BitPat("b000") -> BitPat("b0"), + BitPat("b001") -> BitPat("b?"), + BitPat("b010") -> BitPat("b?"), + // BitPat("b011") -> BitPat("b0"), + BitPat("b100") -> BitPat("b1"), + BitPat("b101") -> BitPat("b1"), + // BitPat("b110") -> BitPat("b0"), + BitPat("b111") -> BitPat("b1") + ), + BitPat("b0") + ) + val str = """001->? + |010->? + |100->1 + |101->1 + |111->1 + |0""".stripMargin + "TruthTable" should "serialize" in { + assert(table.toString contains "001->?") + assert(table.toString contains "010->?") + assert(table.toString contains "100->1") + assert(table.toString contains "111->1") + assert(table.toString contains " 0") + } + "TruthTable" should "deserialize" in { + assert(TruthTable(str) == table) + } +} -- cgit v1.2.3 From 28eef17430d8bbca2765b5a2b0ab0337f7484840 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sun, 23 May 2021 07:55:48 +0000 Subject: TruthTable can merge same inputs now. --- .../util/experimental/TruthTableSpec.scala | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala index ed79f7f5..743a3cd8 100644 --- a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala @@ -36,4 +36,28 @@ class TruthTableSpec extends AnyFlatSpec { "TruthTable" should "deserialize" in { assert(TruthTable(str) == table) } + "TruthTable" should "merge same key" in { + assert( + TruthTable( + """001100->??1 + |001100->1?? + |??? + |""".stripMargin + ) == TruthTable( + """001100->1?1 + |??? + |""".stripMargin + ) + ) + } + "TruthTable" should "crash when merging 0 and 1" in { + intercept[IllegalArgumentException] { + TruthTable( + """0->0 + |0->1 + |??? + |""".stripMargin + ) + } + } } -- cgit v1.2.3 From b0c76525ed53c20dbbe4bd8eea4a9676d7247ec7 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Tue, 18 May 2021 14:53:59 +0000 Subject: test decode cache. --- .../util/experimental/DecoderSpec.scala | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/test/scala/chiselTests/util/experimental/DecoderSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala b/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala new file mode 100644 index 00000000..3c9d490d --- /dev/null +++ b/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.util.experimental + +import chisel3.util.experimental.decode.{DecodeTableAnnotation, Minimizer, QMCMinimizer, TruthTable} +import chiselTests.SMTModelCheckingSpec +import chiselTests.util.experimental.minimizer.DecodeTestModule +import firrtl.annotations.ReferenceTarget + +class DecoderSpec extends SMTModelCheckingSpec { + val xor = TruthTable( + """10->1 + |01->1 + | 0""".stripMargin) + + def minimizer: Minimizer = QMCMinimizer + + "decoder" should "pass without DecodeTableAnnotation" in { + test( + () => new DecodeTestModule(minimizer, table = xor), + s"${minimizer.getClass.getSimpleName}.noAnno", + success + ) + } + + "decoder" should "fail with a incorrect DecodeTableAnnotation" in { + test( + () => new DecodeTestModule(minimizer, table = xor), + s"${minimizer.getClass.getSimpleName}.incorrectAnno", + fail(0), + annos = Seq( + DecodeTableAnnotation(ReferenceTarget("", "", Nil, "", Nil), + """10->1 + |01->1 + | 0""".stripMargin, + """10->1 + | 0""".stripMargin + ) + ) + ) + } + + "decoder" should "success with a correct DecodeTableAnnotation" in { + test( + () => new DecodeTestModule(minimizer, table = xor), + s"${minimizer.getClass.getSimpleName}.correctAnno", + success, + annos = Seq( + DecodeTableAnnotation(ReferenceTarget("", "", Nil, "", Nil), + """10->1 + |01->1 + | 0""".stripMargin, + QMCMinimizer.minimize(TruthTable( + """10->1 + |01->1 + | 0""".stripMargin)).toString + ) + ) + ) + } +} -- cgit v1.2.3 From 3a74d433560c8aca8bf9b2734a3ee620a3442117 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 6 May 2021 16:20:09 +0000 Subject: use z3 formal check minimized circuit and reference model. --- .../experimental/minimizer/MinimizerSpec.scala | 104 +++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala new file mode 100644 index 00000000..9651f241 --- /dev/null +++ b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.util.experimental.minimizer + +import chisel3._ +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.util._ +import chisel3.util.experimental.decode._ +import chisel3.util.pla +import firrtl.backends.experimental.smt.EmittedSMTModelAnnotation +import firrtl.options.TargetDirAnnotation +import firrtl.util.BackendCompilationUtilities +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class MinimizerSpec extends AnyFlatSpec with Matchers { + class DecodeTestModule(minimizer: Minimizer, table: TruthTable, reference: TruthTable) extends Module { + val i = IO(Input(UInt(table.table.head._1.getWidth.W))) + val (plaI, plaO) = pla(reference.table.toSeq, BitPat(reference.default.value.U)) + plaI := i + chisel3.experimental.verification.assert((decoder(minimizer, i, table) === plaO) | (i === reference.default)) + } + + def test(minimizer: Minimizer, testcase: (TruthTable, TruthTable)) = { + val testDir = os.pwd / "test_run_dir" / "MinimizerSpec" / BackendCompilationUtilities.timeStamp + os.makeDir.all(testDir) + val checkFile = testDir / "check.smt" + os.write(checkFile, + (new ChiselStage).execute( + Array("-E", "experimental-smt2"), + Seq( + ChiselGeneratorAnnotation(() => new DecodeTestModule(minimizer, table = testcase._1, testcase._2)), + TargetDirAnnotation(testDir.toString) + ) + ).collectFirst { + case EmittedSMTModelAnnotation(_, smt, _) => smt + }.get + + """; combinational logic check + |(declare-fun s0 () DecodeTestModule_s) + |(assert (not (DecodeTestModule_a s0))) + |(check-sat) + |""".stripMargin + ) + os.proc("z3", checkFile).call().out.toString + } + + val case0 = ( + TruthTable( + Map( + BitPat("b000") -> BitPat("b0"), + // (BitPat("b001") -> BitPat("b?")), // same as default, can be omitted + // (BitPat("b010") -> BitPat("b?")), // same as default, can be omitted + BitPat("b011") -> BitPat("b0"), + BitPat("b100") -> BitPat("b1"), + BitPat("b101") -> BitPat("b1"), + BitPat("b110") -> BitPat("b0"), + BitPat("b111") -> BitPat("b1") + ), + BitPat("b?") + ), + TruthTable( + Map( + BitPat("b000") -> BitPat("b0"), + // (BitPat("b001") -> BitPat("b?")), // same as default, can be omitted + // (BitPat("b010") -> BitPat("b?")), // same as default, can be omitted + BitPat("b011") -> BitPat("b0"), + BitPat("b100") -> BitPat("b1"), + BitPat("b101") -> BitPat("b1"), + BitPat("b110") -> BitPat("b0"), + BitPat("b111") -> BitPat("b1") + ), + BitPat("b?") + ) + ) + + val case1 = ( + TruthTable( + Map( + BitPat("b000") -> BitPat("b0"), + BitPat("b001") -> BitPat("b0"), + // (BitPat("b010") -> BitPat("b?")), // same as default, can be omitted + (BitPat("b011") -> BitPat("b0")), + // (BitPat("b100") -> BitPat("b?")), // same as default, can be omitted + // (BitPat("b101") -> BitPat("b?")), // same as default, can be omitted + (BitPat("b110") -> BitPat("b1")), + (BitPat("b111") -> BitPat("b0")) + ), + BitPat("b?") + ), + TruthTable( + Map( + BitPat("b000") -> BitPat("b0"), + BitPat("b001") -> BitPat("b0"), + // (BitPat("b010") -> BitPat("b?")), // same as default, can be omitted + (BitPat("b011") -> BitPat("b0")), + // (BitPat("b100") -> BitPat("b?")), // same as default, can be omitted + // (BitPat("b101") -> BitPat("b?")), // same as default, can be omitted + (BitPat("b110") -> BitPat("b1")), + (BitPat("b111") -> BitPat("b0")) + ), + BitPat("b?") + ), + ) +} -- cgit v1.2.3 From 38321a2eecfd7dc463ba640b2e97113043235b88 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Tue, 11 May 2021 05:55:38 -0700 Subject: Add minimized form of test cases --- .../experimental/minimizer/MinimizerSpec.scala | 34 +++++++--------------- 1 file changed, 11 insertions(+), 23 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala index 9651f241..0db6c0e2 100644 --- a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala @@ -48,8 +48,8 @@ class MinimizerSpec extends AnyFlatSpec with Matchers { TruthTable( Map( BitPat("b000") -> BitPat("b0"), - // (BitPat("b001") -> BitPat("b?")), // same as default, can be omitted - // (BitPat("b010") -> BitPat("b?")), // same as default, can be omitted + // BitPat("b001") -> BitPat("b?"), // same as default, can be omitted + // BitPat("b010") -> BitPat("b?"), // same as default, can be omitted BitPat("b011") -> BitPat("b0"), BitPat("b100") -> BitPat("b1"), BitPat("b101") -> BitPat("b1"), @@ -60,14 +60,8 @@ class MinimizerSpec extends AnyFlatSpec with Matchers { ), TruthTable( Map( - BitPat("b000") -> BitPat("b0"), - // (BitPat("b001") -> BitPat("b?")), // same as default, can be omitted - // (BitPat("b010") -> BitPat("b?")), // same as default, can be omitted - BitPat("b011") -> BitPat("b0"), - BitPat("b100") -> BitPat("b1"), - BitPat("b101") -> BitPat("b1"), - BitPat("b110") -> BitPat("b0"), - BitPat("b111") -> BitPat("b1") + BitPat("b10?") -> BitPat("b1"), + BitPat("b1?1") -> BitPat("b1"), ), BitPat("b?") ) @@ -78,25 +72,19 @@ class MinimizerSpec extends AnyFlatSpec with Matchers { Map( BitPat("b000") -> BitPat("b0"), BitPat("b001") -> BitPat("b0"), - // (BitPat("b010") -> BitPat("b?")), // same as default, can be omitted + // BitPat("b010") -> BitPat("b?"), // same as default, can be omitted (BitPat("b011") -> BitPat("b0")), - // (BitPat("b100") -> BitPat("b?")), // same as default, can be omitted - // (BitPat("b101") -> BitPat("b?")), // same as default, can be omitted - (BitPat("b110") -> BitPat("b1")), - (BitPat("b111") -> BitPat("b0")) + // BitPat("b100") -> BitPat("b?"), // same as default, can be omitted + // BitPat("b101") -> BitPat("b?"), // same as default, can be omitted + BitPat("b110") -> BitPat("b1"), + BitPat("b111") -> BitPat("b0") ), BitPat("b?") ), TruthTable( Map( - BitPat("b000") -> BitPat("b0"), - BitPat("b001") -> BitPat("b0"), - // (BitPat("b010") -> BitPat("b?")), // same as default, can be omitted - (BitPat("b011") -> BitPat("b0")), - // (BitPat("b100") -> BitPat("b?")), // same as default, can be omitted - // (BitPat("b101") -> BitPat("b?")), // same as default, can be omitted - (BitPat("b110") -> BitPat("b1")), - (BitPat("b111") -> BitPat("b0")) + BitPat("b?10") -> BitPat("b1"), + // BitPat("b1?0") -> BitPat("b1"), // both are ok ), BitPat("b?") ), -- cgit v1.2.3 From ce755215d2657c81b4a2a161e0f04b1f6c59d5a1 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Wed, 12 May 2021 13:12:43 +0000 Subject: switch to EndToEndSMTBaseSpec --- .../experimental/minimizer/MinimizerSpec.scala | 78 +++++++--------------- 1 file changed, 24 insertions(+), 54 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala index 0db6c0e2..6513dc64 100644 --- a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala @@ -3,49 +3,36 @@ package chiselTests.util.experimental.minimizer import chisel3._ -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util._ import chisel3.util.experimental.decode._ import chisel3.util.pla -import firrtl.backends.experimental.smt.EmittedSMTModelAnnotation -import firrtl.options.TargetDirAnnotation -import firrtl.util.BackendCompilationUtilities -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers +import chiselTests.SMTModelCheckingSpec -class MinimizerSpec extends AnyFlatSpec with Matchers { - class DecodeTestModule(minimizer: Minimizer, table: TruthTable, reference: TruthTable) extends Module { - val i = IO(Input(UInt(table.table.head._1.getWidth.W))) - val (plaI, plaO) = pla(reference.table.toSeq, BitPat(reference.default.value.U)) - plaI := i - chisel3.experimental.verification.assert((decoder(minimizer, i, table) === plaO) | (i === reference.default)) - } +class DecodeTestModule(minimizer: Minimizer, table: TruthTable) extends Module { + val i = IO(Input(UInt(table.table.head._1.getWidth.W))) + val (unminimizedI, unminimizedO) = pla(table.table.toSeq) + unminimizedI := i + val minimizedO: UInt = decoder(minimizer, i, table) + + chisel3.experimental.verification.assert( + // for each instruction, if input matches, output should match, not no matched, fallback to default + (table.table.map { case (key, value) => (i === key) && (minimizedO === value) } ++ + Seq(table.table.keys.map(i =/= _).reduce(_ && _) && minimizedO === table.default)).reduce(_ || _) + ) +} - def test(minimizer: Minimizer, testcase: (TruthTable, TruthTable)) = { - val testDir = os.pwd / "test_run_dir" / "MinimizerSpec" / BackendCompilationUtilities.timeStamp - os.makeDir.all(testDir) - val checkFile = testDir / "check.smt" - os.write(checkFile, - (new ChiselStage).execute( - Array("-E", "experimental-smt2"), - Seq( - ChiselGeneratorAnnotation(() => new DecodeTestModule(minimizer, table = testcase._1, testcase._2)), - TargetDirAnnotation(testDir.toString) - ) - ).collectFirst { - case EmittedSMTModelAnnotation(_, smt, _) => smt - }.get + - """; combinational logic check - |(declare-fun s0 () DecodeTestModule_s) - |(assert (not (DecodeTestModule_a s0))) - |(check-sat) - |""".stripMargin +trait MinimizerSpec extends SMTModelCheckingSpec { + def minimizer: Minimizer + + def minimizerTest(testcase: TruthTable, caseName: String) = { + test( + () => new DecodeTestModule(minimizer, table = testcase), + s"${minimizer.getClass.getSimpleName}.$caseName", + success ) - os.proc("z3", checkFile).call().out.toString } - val case0 = ( - TruthTable( + val case0 = TruthTable( Map( BitPat("b000") -> BitPat("b0"), // BitPat("b001") -> BitPat("b?"), // same as default, can be omitted @@ -57,18 +44,9 @@ class MinimizerSpec extends AnyFlatSpec with Matchers { BitPat("b111") -> BitPat("b1") ), BitPat("b?") - ), - TruthTable( - Map( - BitPat("b10?") -> BitPat("b1"), - BitPat("b1?1") -> BitPat("b1"), - ), - BitPat("b?") ) - ) - val case1 = ( - TruthTable( + val case1 = TruthTable( Map( BitPat("b000") -> BitPat("b0"), BitPat("b001") -> BitPat("b0"), @@ -80,13 +58,5 @@ class MinimizerSpec extends AnyFlatSpec with Matchers { BitPat("b111") -> BitPat("b0") ), BitPat("b?") - ), - TruthTable( - Map( - BitPat("b?10") -> BitPat("b1"), - // BitPat("b1?0") -> BitPat("b1"), // both are ok - ), - BitPat("b?") - ), - ) + ) } -- cgit v1.2.3 From 37ec9eb3c3008939605bddfad9096d9b2b47ccdc Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sat, 15 May 2021 15:45:26 +0000 Subject: Add test cases. --- .../experimental/minimizer/MinimizerSpec.scala | 227 +++++++++++++++++++-- 1 file changed, 212 insertions(+), 15 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala index 6513dc64..a9e56800 100644 --- a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala @@ -32,11 +32,47 @@ trait MinimizerSpec extends SMTModelCheckingSpec { ) } - val case0 = TruthTable( + // Term that being commented out is the result of which is same as default, + // making optimization opportunities to decoder algorithms + + "case0" should "pass" in { + minimizerTest(TruthTable( + Map( + // BitPat("b000") -> BitPat("b0"), + BitPat("b001") -> BitPat("b?"), + BitPat("b010") -> BitPat("b?"), + // BitPat("b011") -> BitPat("b0"), + BitPat("b100") -> BitPat("b1"), + BitPat("b101") -> BitPat("b1"), + // BitPat("b110") -> BitPat("b0"), + BitPat("b111") -> BitPat("b1") + ), + BitPat("b0") + ), "case0") + } + + "case1" should "pass" in { + minimizerTest(TruthTable( Map( BitPat("b000") -> BitPat("b0"), - // BitPat("b001") -> BitPat("b?"), // same as default, can be omitted - // BitPat("b010") -> BitPat("b?"), // same as default, can be omitted + BitPat("b001") -> BitPat("b?"), + BitPat("b010") -> BitPat("b?"), + BitPat("b011") -> BitPat("b0"), + // BitPat("b100") -> BitPat("b1"), + // BitPat("b101") -> BitPat("b1"), + BitPat("b110") -> BitPat("b0"), + // BitPat("b111") -> BitPat("b1") + ), + BitPat("b1") + ), "case1") + } + + "caseX" should "pass" in { + minimizerTest(TruthTable( + Map( + BitPat("b000") -> BitPat("b0"), + // BitPat("b001") -> BitPat("b?"), + // BitPat("b010") -> BitPat("b?"), BitPat("b011") -> BitPat("b0"), BitPat("b100") -> BitPat("b1"), BitPat("b101") -> BitPat("b1"), @@ -44,19 +80,180 @@ trait MinimizerSpec extends SMTModelCheckingSpec { BitPat("b111") -> BitPat("b1") ), BitPat("b?") - ) + ), "caseX") + } - val case1 = TruthTable( + "caseMultiDefault" should "pass" in { + minimizerTest(TruthTable( Map( - BitPat("b000") -> BitPat("b0"), - BitPat("b001") -> BitPat("b0"), - // BitPat("b010") -> BitPat("b?"), // same as default, can be omitted - (BitPat("b011") -> BitPat("b0")), - // BitPat("b100") -> BitPat("b?"), // same as default, can be omitted - // BitPat("b101") -> BitPat("b?"), // same as default, can be omitted - BitPat("b110") -> BitPat("b1"), - BitPat("b111") -> BitPat("b0") + BitPat("b000") -> BitPat("b0100"), + BitPat("b001") -> BitPat("b?111"), + BitPat("b010") -> BitPat("b?000"), + BitPat("b011") -> BitPat("b0101"), + BitPat("b111") -> BitPat("b1101") ), - BitPat("b?") - ) + BitPat("b?100") + ), "caseMultiDefault") + } + + // A simple RV32I decode table example + "caseRV32I" should "pass" in { + val BEQ = "?????????????????000?????1100011" + val BNE = "?????????????????001?????1100011" + val BLT = "?????????????????100?????1100011" + val BGE = "?????????????????101?????1100011" + val BLTU = "?????????????????110?????1100011" + val BGEU = "?????????????????111?????1100011" + val JALR = "?????????????????000?????1100111" + val JAL = "?????????????????????????1101111" + val LUI = "?????????????????????????0110111" + val AUIPC = "?????????????????????????0010111" + val ADDI = "?????????????????000?????0010011" + val SLTI = "?????????????????010?????0010011" + val SLTIU = "?????????????????011?????0010011" + val XORI = "?????????????????100?????0010011" + val ORI = "?????????????????110?????0010011" + val ANDI = "?????????????????111?????0010011" + val ADD = "0000000??????????000?????0110011" + val SUB = "0100000??????????000?????0110011" + val SLL = "0000000??????????001?????0110011" + val SLT = "0000000??????????010?????0110011" + val SLTU = "0000000??????????011?????0110011" + val XOR = "0000000??????????100?????0110011" + val SRL = "0000000??????????101?????0110011" + val SRA = "0100000??????????101?????0110011" + val OR = "0000000??????????110?????0110011" + val AND = "0000000??????????111?????0110011" + val LB = "?????????????????000?????0000011" + val LH = "?????????????????001?????0000011" + val LW = "?????????????????010?????0000011" + val LBU = "?????????????????100?????0000011" + val LHU = "?????????????????101?????0000011" + val SB = "?????????????????000?????0100011" + val SH = "?????????????????001?????0100011" + val SW = "?????????????????010?????0100011" + val FENCE = "?????????????????000?????0001111" + val MRET = "00110000001000000000000001110011" + val WFI = "00010000010100000000000001110011" + val CEASE = "00110000010100000000000001110011" + val CSRRW = "?????????????????001?????1110011" + val CSRRS = "?????????????????010?????1110011" + val CSRRC = "?????????????????011?????1110011" + val CSRRWI = "?????????????????101?????1110011" + val CSRRSI = "?????????????????110?????1110011" + val CSRRCI = "?????????????????111?????1110011" + val SCALL = "00000000000000000000000001110011" + val SBREAK = "00000000000100000000000001110011" + val SLLI_RV32 = "0000000??????????001?????0010011" + val SRLI_RV32 = "0000000??????????101?????0010011" + val SRAI_RV32 = "0100000??????????101?????0010011" + + val A1_X = "??" + val A1_ZERO = "00" + val A1_RS1 = "01" + val A1_PC = "10" + + val IMM_X = "???" + val IMM_S = "000" + val IMM_SB = "001" + val IMM_U = "010" + val IMM_UJ = "011" + val IMM_I = "100" + val IMM_Z = "101" + + val A2_X = "??" + val A2_ZERO = "00" + val A2_SIZE = "01" + val A2_RS2 = "10" + val A2_IMM = "11" + + val X = "?" + val N = "0" + val Y = "1" + + val DW_X = X + val DW_XPR = Y + + val M_X = "?????" + val M_XRD = "00000" + val M_XWR = "00001" + + val CSR_X = "???" + val CSR_N = "000" + val CSR_I = "100" + val CSR_W = "101" + val CSR_S = "110" + val CSR_C = "111" + + val FN_X = "????" + val FN_ADD = "0000" + val FN_SL = "0001" + val FN_SEQ = "0010" + val FN_SNE = "0011" + val FN_XOR = "0100" + val FN_SR = "0101" + val FN_OR = "0110" + val FN_AND = "0111" + val FN_SUB = "1010" + val FN_SRA = "1011" + val FN_SLT = "1100" + val FN_SGE = "1101" + val FN_SLTU = "1110" + val FN_SGEU = "1111" + + minimizerTest(TruthTable( + Map( + BNE -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SNE, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + BEQ -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SEQ, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + BLT -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SLT, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + BLTU -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SLTU, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + BGE -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SGE, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + BGEU -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SGEU, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + JAL -> Seq(Y, N, N, N, Y, N, N, N, N, A2_SIZE, A1_PC, IMM_UJ, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + JALR -> Seq(Y, N, N, N, N, Y, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + AUIPC -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_PC, IMM_U, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + LB -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + LH -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + LW -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + LBU -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + LHU -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SB -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_IMM, A1_RS1, IMM_S, DW_XPR, FN_ADD, Y, M_XWR, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + SH -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_IMM, A1_RS1, IMM_S, DW_XPR, FN_ADD, Y, M_XWR, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + SW -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_IMM, A1_RS1, IMM_S, DW_XPR, FN_ADD, Y, M_XWR, N, N, N, N, N, N, N, CSR_N, N, N, N, N), + LUI -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_ZERO, IMM_U, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + ADDI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SLTI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SLT, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SLTIU -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SLTU, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + ANDI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_AND, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + ORI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_OR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + XORI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_XOR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + ADD -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SUB -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SUB, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SLT -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SLT, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SLTU -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SLTU, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + AND -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_AND, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + OR -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_OR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + XOR -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_XOR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SLL -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SL, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SRL -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SRA -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SRA, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + FENCE -> Seq(Y, N, N, N, N, N, N, N, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_N, N, Y, N, N), + SCALL -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), + SBREAK -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), + MRET -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), + WFI -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), + CEASE -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), + CSRRW -> Seq(Y, N, N, N, N, N, N, Y, N, A2_ZERO, A1_RS1, IMM_X, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_W, N, N, N, N), + CSRRS -> Seq(Y, N, N, N, N, N, N, Y, N, A2_ZERO, A1_RS1, IMM_X, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_S, N, N, N, N), + CSRRC -> Seq(Y, N, N, N, N, N, N, Y, N, A2_ZERO, A1_RS1, IMM_X, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_C, N, N, N, N), + CSRRWI -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_ZERO, IMM_Z, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_W, N, N, N, N), + CSRRSI -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_ZERO, IMM_Z, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_S, N, N, N, N), + CSRRCI -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_ZERO, IMM_Z, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_C, N, N, N, N), + SLLI_RV32 -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SL, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SRLI_RV32 -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + SRAI_RV32 -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SRA, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), + ).map { case (k, v) => BitPat(s"b$k") -> BitPat(s"b${v.reduce(_ + _)}") }, + BitPat(s"b${Seq(N, X, X, X, X, X, X, X, X, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, X, X, X, X, X, X, X, CSR_X, X, X, X, X).reduce(_ + _)}") + ), "rv32i") + } } -- cgit v1.2.3 From b860a814c6e5a9333d6c94b6c5fbee6daad7ba5c Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sat, 15 May 2021 15:46:08 +0000 Subject: implement test for qmc --- .../scala/chiselTests/util/experimental/minimizer/QMCSpec.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/test/scala/chiselTests/util/experimental/minimizer/QMCSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/QMCSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/QMCSpec.scala new file mode 100644 index 00000000..fc770202 --- /dev/null +++ b/src/test/scala/chiselTests/util/experimental/minimizer/QMCSpec.scala @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.util.experimental.minimizer +import chisel3.util.experimental.decode.Minimizer +import chisel3.util.experimental.decode.QMCMinimizer + +class QMCSpec extends MinimizerSpec { + override def minimizer: Minimizer = QMCMinimizer +} -- cgit v1.2.3 From a3ddd4b98049b624080422717c6822ec9ab43e07 Mon Sep 17 00:00:00 2001 From: Martin Schoeberl Date: Wed, 16 Jun 2021 19:33:26 +0200 Subject: getVerilog in Chisel3 (#1921) --- src/test/scala/chiselTests/Module.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/Module.scala b/src/test/scala/chiselTests/Module.scala index bc9c524a..7703e876 100644 --- a/src/test/scala/chiselTests/Module.scala +++ b/src/test/scala/chiselTests/Module.scala @@ -8,6 +8,8 @@ import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompile import firrtl.annotations.NoTargetAnnotation import firrtl.options.Unserializable +import scala.io.Source + class SimpleIO extends Bundle { val in = Input(UInt(32.W)) val out = Output(UInt(32.W)) @@ -187,4 +189,15 @@ class ModuleSpec extends ChiselPropSpec with Utils { } ChiselStage.elaborate(new RawModule with Foo) } + + property("getVerilogString(new PlusOne() should produce a valid Verilog string") { + val s = getVerilogString(new PlusOne()) + assert(s.contains("assign io_out = io_in + 32'h1")) + } + + property("emitVerilog((new PlusOne()..) shall produce a valid Verilog file in a subfolder") { + emitVerilog(new PlusOne(), Array("--target-dir", "generated")) + val s = Source.fromFile("generated/PlusOne.v").mkString("") + assert(s.contains("assign io_out = io_in + 32'h1")) + } } -- cgit v1.2.3 From 942218432a80de1546ed53a4cfb10a8683ff4f27 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Mon, 21 Jun 2021 16:58:15 -0700 Subject: Bump scalatest to 3.2.9 (#1965) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/test/scala/chiselTests/ChiselSpec.scala | 5 +++-- src/test/scala/chiselTests/ExtModuleImpl.scala | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 37c4a2b7..a4192c5e 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -13,7 +13,8 @@ import org.scalacheck._ import org.scalatest._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.freespec.AnyFreeSpec -import org.scalatest.matchers.should._ +import org.scalatest.propspec.AnyPropSpec +import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import java.io.ByteArrayOutputStream @@ -94,7 +95,7 @@ abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matche abstract class ChiselFreeSpec extends AnyFreeSpec with ChiselRunners with Matchers /** Spec base class for property-based testers. */ -abstract class ChiselPropSpec extends PropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { +abstract class ChiselPropSpec extends AnyPropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { // Constrain the default number of instances generated for every use of forAll. implicit override val generatorDrivenConfig: PropertyCheckConfiguration = diff --git a/src/test/scala/chiselTests/ExtModuleImpl.scala b/src/test/scala/chiselTests/ExtModuleImpl.scala index e0a76201..c6cd4a9f 100644 --- a/src/test/scala/chiselTests/ExtModuleImpl.scala +++ b/src/test/scala/chiselTests/ExtModuleImpl.scala @@ -8,12 +8,11 @@ import chisel3._ import chisel3.experimental.ExtModule import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util.{HasExtModuleInline, HasExtModulePath, HasExtModuleResource} -import firrtl.FirrtlExecutionSuccess import firrtl.options.TargetDirAnnotation import firrtl.stage.FirrtlCircuitAnnotation import firrtl.transforms.BlackBoxNotFoundException -import org.scalacheck.Test.Failed -import org.scalatest.{FreeSpec, Matchers, Succeeded} +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.should.Matchers //scalastyle:off magic.number @@ -102,7 +101,7 @@ class UsesMissingExtModuleResource extends RawModule { val foo = Module(new ExtModuleResourceNotFound) } -class ExtModuleImplSpec extends FreeSpec with Matchers { +class ExtModuleImplSpec extends AnyFreeSpec with Matchers { "ExtModule can have verilator source implementation" - { "Implementations can be contained in-line" in { -- cgit v1.2.3 From aaccf17799fdbbb9810e424ab8b246d9a5ff5c70 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Wed, 23 Jun 2021 17:14:03 +0800 Subject: Replace hard coded line separators with system specific ones --- src/test/scala/chiselTests/SMTModelCheckingSpec.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/SMTModelCheckingSpec.scala b/src/test/scala/chiselTests/SMTModelCheckingSpec.scala index 6a24ff10..0a752b10 100644 --- a/src/test/scala/chiselTests/SMTModelCheckingSpec.scala +++ b/src/test/scala/chiselTests/SMTModelCheckingSpec.scala @@ -9,6 +9,7 @@ import firrtl.util.BackendCompilationUtilities.timeStamp import logger.{LazyLogging, LogLevel, LogLevelAnnotation} import org.scalatest.flatspec.AnyFlatSpec import os._ +import scala.util.Properties /** [[SMTModelCheckingSpec]] use z3 and [[firrtl.backends.experimental.smt]] library * to solve `assert/assume` in [[chisel3.experimental.verification]], @@ -64,8 +65,8 @@ private object Z3ModelChecker extends LazyLogging { private def executeStep(path: Path): Boolean = { val (out, ret) = executeCmd(path.toString) assert(ret == 0, s"expected success (0), not $ret: `$out`\nz3 ${path.toString}") - assert(out == "sat\n" || out == "unsat\n", s"Unexpected output: $out") - out == "unsat\n" + assert(out == "sat" + Properties.lineSeparator || out == "unsat" + Properties.lineSeparator, s"Unexpected output: $out") + out == "unsat" + Properties.lineSeparator } private def executeCmd(cmd: String): (String, Int) = { -- cgit v1.2.3 From f8053db3d20b733e0119b77595f0cdfcdab71057 Mon Sep 17 00:00:00 2001 From: Deborah Soung Date: Thu, 24 Jun 2021 14:03:28 -0700 Subject: create and extend annotatable BaseSim class for verification nodes (#1968) * prototype annotating verif constructs * switch to final class * name emissions * moving BaseSim to experimental * adding name tests * fixing quotation escapes * emitting names, but everything has a default name * only name things with provided/suggested names * name every BaseSim node * removing msg, unused imports * fixing file exist calls--- .../verification/VerificationSpec.scala | 134 +++++++++++++++++++-- 1 file changed, 122 insertions(+), 12 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala index 86d6418c..a1fc2a1d 100644 --- a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala +++ b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala @@ -3,11 +3,15 @@ package chiselTests.experimental.verification import chisel3._ -import chisel3.experimental.{verification => formal} +import chisel3.experimental.{ChiselAnnotation, verification => formal} import chisel3.stage.ChiselStage import chiselTests.ChiselPropSpec +import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation} -class VerificationModule extends Module { +import java.io.File +import org.scalatest.matchers.should.Matchers + +class SimpleTest extends Module { val io = IO(new Bundle{ val in = Input(UInt(8.W)) val out = Output(UInt(8.W)) @@ -20,25 +24,131 @@ class VerificationModule extends Module { } } -class VerificationSpec extends ChiselPropSpec { +/** Dummy verification annotation. + * @param target target of component to be annotated + */ +case class VerifAnnotation(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] { + def duplicate(n: ReferenceTarget): VerifAnnotation = this.copy(target = n) +} + +object VerifAnnotation { + /** Create annotation for a given verification component. + * @param c component to be annotated + */ + def annotate(c: experimental.BaseSim): Unit = { + chisel3.experimental.annotate(new ChiselAnnotation { + def toFirrtl: VerifAnnotation = VerifAnnotation(c.toTarget) + }) + } +} + +class VerificationSpec extends ChiselPropSpec with Matchers { - def assertContains[T](s: Seq[T], x: T): Unit = { - val containsLine = s.map(_ == x).reduce(_ || _) + def assertContains(s: Seq[String], x: String): Unit = { + val containsLine = s.map(_.contains(x)).reduce(_ || _) assert(containsLine, s"\n $x\nwas not found in`\n ${s.mkString("\n ")}``") } property("basic equality check should work") { - val fir = ChiselStage.emitChirrtl(new VerificationModule) + val fir = ChiselStage.emitChirrtl(new SimpleTest) val lines = fir.split("\n").map(_.trim) // reset guard around the verification statement - assertContains(lines, "when _T_2 : @[VerificationSpec.scala 16:15]") - assertContains(lines, "cover(clock, _T, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 16:15]") + assertContains(lines, "when _T_2 : @[VerificationSpec.scala") + assertContains(lines, "cover(clock, _T, UInt<1>(\"h1\"), \"\")") + + assertContains(lines, "when _T_6 : @[VerificationSpec.scala") + assertContains(lines, "assume(clock, _T_4, UInt<1>(\"h1\"), \"\")") + + assertContains(lines, "when _T_9 : @[VerificationSpec.scala") + assertContains(lines, "assert(clock, _T_7, UInt<1>(\"h1\"), \"\")") + } + + property("annotation of verification constructs should work") { + /** Circuit that contains and annotates verification nodes. */ + class AnnotationTest extends Module { + val io = IO(new Bundle{ + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + io.out := io.in + val cov = formal.cover(io.in === 3.U) + val assm = formal.assume(io.in =/= 2.U) + val asst = formal.assert(io.out === io.in) + VerifAnnotation.annotate(cov) + VerifAnnotation.annotate(assm) + VerifAnnotation.annotate(asst) + } + + // compile circuit + val testDir = new File("test_run_dir", "VerificationAnnotationTests") + (new ChiselStage).emitSystemVerilog( + gen = new AnnotationTest, + args = Array("-td", testDir.getPath) + ) + + // read in annotation file + val annoFile = new File(testDir, "AnnotationTest.anno.json") + annoFile should exist + val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList + + // check for expected verification annotations + exactly(3, annoLines) should include ("chiselTests.experimental.verification.VerifAnnotation") + exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>asst") + exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>assm") + exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>cov") + + // read in FIRRTL file + val firFile = new File(testDir, "AnnotationTest.fir") + firFile should exist + 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>(1), \"\") : cov") + exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(1), \"\") : assm") + exactly(1, firLines) should include ("assert(clock, _T_6, UInt<1>(1), \"\") : asst") + } + + property("annotation of verification constructs with suggested name should work") { + /** Circuit that annotates a renamed verification nodes. */ + class AnnotationRenameTest extends Module { + val io = IO(new Bundle{ + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + io.out := io.in + + val goodbye = formal.assert(io.in === 1.U) + goodbye.suggestName("hello") + VerifAnnotation.annotate(goodbye) + + VerifAnnotation.annotate(formal.assume(io.in =/= 2.U).suggestName("howdy")) + } + + // compile circuit + val testDir = new File("test_run_dir", "VerificationAnnotationRenameTests") + (new ChiselStage).emitSystemVerilog( + gen = new AnnotationRenameTest, + args = Array("-td", testDir.getPath) + ) + + // read in annotation file + val annoFile = new File(testDir, "AnnotationRenameTest.anno.json") + annoFile should exist + val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList + + // check for expected verification annotations + exactly(2, annoLines) should include ("chiselTests.experimental.verification.VerifAnnotation") + exactly(1, annoLines) should include ("~AnnotationRenameTest|AnnotationRenameTest>hello") + exactly(1, annoLines) should include ("~AnnotationRenameTest|AnnotationRenameTest>howdy") - assertContains(lines, "when _T_6 : @[VerificationSpec.scala 18:18]") - assertContains(lines, "assume(clock, _T_4, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 18:18]") + // read in FIRRTL file + val firFile = new File(testDir, "AnnotationRenameTest.fir") + firFile should exist + val firLines = scala.io.Source.fromFile(firFile).getLines.toList - assertContains(lines, "when _T_9 : @[VerificationSpec.scala 19:18]") - assertContains(lines, "assert(clock, _T_7, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 19:18]") + // check that verification components have expected names + exactly(1, firLines) should include ("assert(clock, _T, UInt<1>(1), \"\") : hello") + exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(1), \"\") : howdy") } } -- cgit v1.2.3 From d3e13ce24956871d2f0fd01ca3a7d89317e3db68 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Wed, 23 Jun 2021 17:11:22 -0700 Subject: Fix CloneModuleAsRecord support for .toTarget --- src/test/scala/chiselTests/CloneModuleSpec.scala | 66 +++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/CloneModuleSpec.scala b/src/test/scala/chiselTests/CloneModuleSpec.scala index e54ef1c2..8359bc28 100644 --- a/src/test/scala/chiselTests/CloneModuleSpec.scala +++ b/src/test/scala/chiselTests/CloneModuleSpec.scala @@ -4,7 +4,7 @@ package chiselTests import chisel3._ import chisel3.stage.ChiselStage -import chisel3.util.{Queue, EnqIO, DeqIO, QueueIO, log2Ceil} +import chisel3.util.{Decoupled, Queue, EnqIO, DeqIO, QueueIO, log2Ceil} import chisel3.experimental.{CloneModuleAsRecord, IO} import chisel3.testers.BasicTester @@ -57,6 +57,26 @@ class QueueCloneTester(x: Int, multiIO: Boolean = false) extends BasicTester { } } +class CloneModuleAsRecordAnnotate extends Module { + override def desiredName = "Top" + val in = IO(Flipped(Decoupled(UInt(8.W)))) + val out = IO(Decoupled(UInt(8.W))) + + val q1 = Module(new Queue(UInt(8.W), 4)) + val q2 = CloneModuleAsRecord(q1) + val q2_io = q2("io").asInstanceOf[q1.io.type] + // Also make a wire to check that cloning works, can be connected to, and annotated + val q2_wire = { + val w = Wire(chiselTypeOf(q2)) + w <> q2 + w + } + // But connect to the original (using last connect semantics to override connects to wire + q1.io.enq <> in + q2_io.enq <> q1.io.deq + out <> q2_io.deq +} + class CloneModuleSpec extends ChiselPropSpec { val xVals = Table( @@ -87,4 +107,48 @@ class CloneModuleSpec extends ChiselPropSpec { assert(c.modules.length == 3) } + property("Cloned Modules should annotate correctly") { + // Hackily get the actually Module object out + var mod: CloneModuleAsRecordAnnotate = null + val res = ChiselStage.convert { + mod = new CloneModuleAsRecordAnnotate + mod + } + // ********** Checking the output of CloneModuleAsRecord ********** + // Note that we overrode desiredName so that Top is named "Top" + mod.q1.io.enq.toTarget.serialize should be ("~Top|Queue>io.enq") + mod.q2_io.deq.toTarget.serialize should be ("~Top|Queue>io.deq") + mod.q1.io.enq.toAbsoluteTarget.serialize should be ("~Top|Top/q1:Queue>io.enq") + mod.q2_io.deq.toAbsoluteTarget.serialize should be ("~Top|Top/q2:Queue>io.deq") + // Legacy APIs that nevertheless were tricky to get right + mod.q1.io.enq.toNamed.serialize should be ("Top.Queue.io.enq") + mod.q2_io.deq.toNamed.serialize should be ("Top.Queue.io.deq") + mod.q1.io.enq.instanceName should be ("io.enq") + mod.q2_io.deq.instanceName should be ("io.deq") + mod.q1.io.enq.pathName should be ("Top.q1.io.enq") + mod.q2_io.deq.pathName should be ("Top.q2.io.deq") + mod.q1.io.enq.parentPathName should be ("Top.q1") + mod.q2_io.deq.parentPathName should be ("Top.q2") + mod.q1.io.enq.parentModName should be ("Queue") + mod.q2_io.deq.parentModName should be ("Queue") + + // ********** Checking the wire cloned from the output of CloneModuleAsRecord ********** + val wire_io = mod.q2_wire("io").asInstanceOf[QueueIO[UInt]] + mod.q2_wire.toTarget.serialize should be ("~Top|Top>q2_wire") + wire_io.enq.toTarget.serialize should be ("~Top|Top>q2_wire.io.enq") + mod.q2_wire.toAbsoluteTarget.serialize should be ("~Top|Top>q2_wire") + wire_io.enq.toAbsoluteTarget.serialize should be ("~Top|Top>q2_wire.io.enq") + // Legacy APIs + mod.q2_wire.toNamed.serialize should be ("Top.Top.q2_wire") + wire_io.enq.toNamed.serialize should be ("Top.Top.q2_wire.io.enq") + mod.q2_wire.instanceName should be ("q2_wire") + wire_io.enq.instanceName should be ("q2_wire.io.enq") + mod.q2_wire.pathName should be ("Top.q2_wire") + wire_io.enq.pathName should be ("Top.q2_wire.io.enq") + mod.q2_wire.parentPathName should be ("Top") + wire_io.enq.parentPathName should be ("Top") + mod.q2_wire.parentModName should be ("Top") + wire_io.enq.parentModName should be ("Top") + } + } -- cgit v1.2.3 From 0531cb53d3cedaff33c2a280e34418f6af5bc6a1 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Tue, 29 Jun 2021 15:34:18 -0700 Subject: Restore aop.Select behavior for CloneModuleAsRecord --- src/test/scala/chiselTests/aop/SelectSpec.scala | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala index 91353f5a..d34f4391 100644 --- a/src/test/scala/chiselTests/aop/SelectSpec.scala +++ b/src/test/scala/chiselTests/aop/SelectSpec.scala @@ -153,5 +153,30 @@ class SelectSpec extends ChiselFlatSpec { assert(bbs.size == 1) } + "CloneModuleAsRecord" should "show up in Select aspects as duplicates" in { + import chisel3.experimental.CloneModuleAsRecord + class Child extends RawModule { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := in + } + class Top extends MultiIOModule { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + val inst0 = Module(new Child) + val inst1 = CloneModuleAsRecord(inst0) + inst0.in := in + inst1("in") := inst0.out + out := inst1("out") + } + val top = ChiselGeneratorAnnotation(() => { + new Top() + }).elaborate + .collectFirst { case DesignAnnotation(design: Top) => design } + .get + val mods = Select.collectDeep(top) { case mod => mod } + mods should equal (Seq(top, top.inst0, top.inst0)) + } + } -- cgit v1.2.3 From 25a84b5667614ea3f437b656f1939caba57e6f66 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Tue, 29 Jun 2021 16:39:45 -0700 Subject: Change behavior of aop.Select to not include CloneModuleAsRecord Previously, CloneModuleAsRecord clones would result in the same BaseModule object coming up multiple times when using APIs like .instances, .collectDeep, and .getDeep. This was not the intended behavior and can lead to very subtle bugs. --- src/test/scala/chiselTests/aop/SelectSpec.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala index d34f4391..14ae202d 100644 --- a/src/test/scala/chiselTests/aop/SelectSpec.scala +++ b/src/test/scala/chiselTests/aop/SelectSpec.scala @@ -153,7 +153,7 @@ class SelectSpec extends ChiselFlatSpec { assert(bbs.size == 1) } - "CloneModuleAsRecord" should "show up in Select aspects as duplicates" in { + "CloneModuleAsRecord" should "NOT show up in Select aspects" in { import chisel3.experimental.CloneModuleAsRecord class Child extends RawModule { val in = IO(Input(UInt(8.W))) @@ -174,8 +174,9 @@ class SelectSpec extends ChiselFlatSpec { }).elaborate .collectFirst { case DesignAnnotation(design: Top) => design } .get - val mods = Select.collectDeep(top) { case mod => mod } - mods should equal (Seq(top, top.inst0, top.inst0)) + Select.collectDeep(top) { case x => x } should equal (Seq(top, top.inst0)) + Select.getDeep(top)(x => Seq(x)) should equal (Seq(top, top.inst0)) + Select.instances(top) should equal (Seq(top.inst0)) } } -- cgit v1.2.3 From 1e40c7e2a2fd227c752e4e4dc29925004f790f7d Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Wed, 23 Jun 2021 17:37:27 +0800 Subject: Add 7 segment display decoder test case --- .../util/experimental/minimizer/MinimizerSpec.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala index a9e56800..5e3be9a6 100644 --- a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala @@ -96,6 +96,24 @@ trait MinimizerSpec extends SMTModelCheckingSpec { ), "caseMultiDefault") } + "case7SegDecoder" should "pass" in { + minimizerTest(TruthTable( + Map( + BitPat("b0000") -> BitPat("b111111001"), + BitPat("b0001") -> BitPat("b011000001"), + BitPat("b0010") -> BitPat("b110110101"), + BitPat("b0011") -> BitPat("b111100101"), + BitPat("b0100") -> BitPat("b011001101"), + BitPat("b0101") -> BitPat("b101101101"), + BitPat("b0110") -> BitPat("b101111101"), + BitPat("b0111") -> BitPat("b111000001"), + BitPat("b1000") -> BitPat("b111111101"), + BitPat("b1001") -> BitPat("b111101101"), + ), + BitPat("b???????10") + ), "case7SegDecoder") + } + // A simple RV32I decode table example "caseRV32I" should "pass" in { val BEQ = "?????????????????000?????1100011" -- cgit v1.2.3 From 5fe539c707c88eedbb112f5c6bcea1dfe1d52169 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 1 Jul 2021 16:34:48 -0700 Subject: Add ChiselEnum.safe factory method and avoid warning Previously, ChiselEnum would warn any time a UInt is converted to an Enum. There was no way to suppress this warning. Now there is a factory method (`.safe`) that does not warn and returns (Enum, Bool) where the Bool is the result of calling .isValid on an Enum object. The regular UInt cast is also now smarter and will not warn if all bitvectors of the width of the Enum are legal states. --- src/test/scala/chiselTests/ChiselSpec.scala | 17 ++++- src/test/scala/chiselTests/StrongEnum.scala | 97 +++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index a4192c5e..e513189e 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -9,6 +9,7 @@ import chisel3.testers._ import firrtl.annotations.Annotation import firrtl.util.BackendCompilationUtilities import firrtl.{AnnotationSeq, EmittedVerilogCircuitAnnotation} +import _root_.logger.Logger import org.scalacheck._ import org.scalatest._ import org.scalatest.flatspec.AnyFlatSpec @@ -17,7 +18,7 @@ import org.scalatest.propspec.AnyPropSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import java.io.ByteArrayOutputStream +import java.io.{ByteArrayOutputStream, PrintStream} import java.security.Permission import scala.reflect.ClassTag @@ -172,6 +173,20 @@ trait Utils { (stdout.toString, stderr.toString, ret) } + /** Run some Scala thunk and return all logged messages as Strings + * @param thunk some Scala code + * @return a tuple containing LOGGED, and what the thunk returns + */ + def grabLog[T](thunk: => T): (String, T) = { + val baos = new ByteArrayOutputStream() + val stream = new PrintStream(baos, true, "utf-8") + val ret = Logger.makeScope(Nil) { + Logger.setOutput(stream) + thunk + } + (baos.toString, ret) + } + /** Encodes a System.exit exit code * @param status the exit code */ diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala index bf0eb2fe..e59a5398 100644 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ b/src/test/scala/chiselTests/StrongEnum.scala @@ -74,6 +74,18 @@ class CastFromNonLit extends Module { io.valid := io.out.isValid } +class SafeCastFromNonLit extends Module { + val io = IO(new Bundle { + val in = Input(UInt(EnumExample.getWidth.W)) + val out = Output(EnumExample()) + val valid = Output(Bool()) + }) + + val (enum, valid) = EnumExample.safe(io.in) + io.out := enum + io.valid := valid +} + class CastFromNonLitWidth(w: Option[Int] = None) extends Module { val width = if (w.isDefined) w.get.W else UnknownWidth() @@ -191,6 +203,28 @@ class CastFromNonLitTester extends BasicTester { stop() } +class SafeCastFromNonLitTester extends BasicTester { + for ((enum,lit) <- EnumExample.all zip EnumExample.litValues) { + val mod = Module(new SafeCastFromNonLit) + mod.io.in := lit + assert(mod.io.out === enum) + assert(mod.io.valid === true.B) + } + + val invalid_values = (1 until (1 << EnumExample.getWidth)). + filter(!EnumExample.litValues.map(_.litValue).contains(_)). + map(_.U) + + for (invalid_val <- invalid_values) { + val mod = Module(new SafeCastFromNonLit) + mod.io.in := invalid_val + + assert(mod.io.valid === false.B) + } + + stop() +} + class CastToInvalidEnumTester extends BasicTester { val invalid_value: UInt = EnumExample.litValues.last + 1.U Module(new CastFromLit(invalid_value)) @@ -320,6 +354,10 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils { assertTesterPasses(new CastFromNonLitTester) } + it should "safely cast non-literal UInts to enums correctly and detect illegal casts" in { + assertTesterPasses(new SafeCastFromNonLitTester) + } + it should "prevent illegal literal casts to enums" in { a [ChiselException] should be thrownBy extractCause[ChiselException] { ChiselStage.elaborate(new CastToInvalidEnumTester) @@ -377,6 +415,65 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils { "StrongEnum FSM" should "work" in { assertTesterPasses(new StrongEnumFSMTester) } + + "Casting a UInt to an Enum" should "warn if the UInt can express illegal states" in { + object MyEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(MyEnum())) + out := MyEnum(in) + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + log should include ("warn") + log should include ("Casting non-literal UInt") + } + + it should "NOT warn if the Enum is total" in { + object TotalEnum extends ChiselEnum { + val e0, e1, e2, e3 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(TotalEnum())) + out := TotalEnum(in) + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + log should not include ("warn") + } + + "Casting a UInt to an Enum with .safe" should "NOT warn" in { + object MyEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(MyEnum())) + out := MyEnum.safe(in)._1 + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + log should not include ("warn") + } + + it should "NOT generate any validity logic if the Enum is total" in { + object TotalEnum extends ChiselEnum { + val e0, e1, e2, e3 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(TotalEnum())) + val (res, valid) = TotalEnum.safe(in) + assert(valid.litToBoolean, "It should be true.B") + out := res + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + log should not include ("warn") + } } class StrongEnumAnnotator extends Module { -- cgit v1.2.3 From 503ae520e7f997bcbc639b79869c9a4214d402ed Mon Sep 17 00:00:00 2001 From: Deborah Soung Date: Tue, 6 Jul 2021 14:40:59 -0700 Subject: Make printf return BaseSim subclass so it can be named/annotated (#1992) --- src/test/scala/chiselTests/PrintableSpec.scala | 66 +++++++++++++++++++++++++ src/test/scala/chiselTests/aop/SelectSpec.scala | 11 +++-- 2 files changed, 73 insertions(+), 4 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/PrintableSpec.scala b/src/test/scala/chiselTests/PrintableSpec.scala index c76b26de..0325d3bc 100644 --- a/src/test/scala/chiselTests/PrintableSpec.scala +++ b/src/test/scala/chiselTests/PrintableSpec.scala @@ -3,11 +3,33 @@ package chiselTests import chisel3._ +import chisel3.experimental.{BaseSim, ChiselAnnotation} import chisel3.stage.ChiselStage import chisel3.testers.BasicTester +import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers +import java.io.File + +/** Dummy [[printf]] annotation. + * @param target target of component to be annotated + */ +case class PrintfAnnotation(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] { + def duplicate(n: ReferenceTarget): PrintfAnnotation = this.copy(target = n) +} + +object PrintfAnnotation { + /** Create annotation for a given [[printf]]. + * @param c component to be annotated + */ + def annotate(c: BaseSim): Unit = { + chisel3.experimental.annotate(new ChiselAnnotation { + def toFirrtl: PrintfAnnotation = PrintfAnnotation(c.toTarget) + }) + } +} + /* Printable Tests */ class PrintableSpec extends AnyFlatSpec with Matchers { // This regex is brittle, it specifically finds the clock and enable signals followed by commas @@ -194,4 +216,48 @@ class PrintableSpec extends AnyFlatSpec with Matchers { case e => fail() } } + it should "get emitted with a name and annotated" in { + + /** Test circuit containing annotated and renamed [[printf]]s. */ + class PrintfAnnotationTest extends Module { + val myBun = Wire(new Bundle { + val foo = UInt(32.W) + val bar = UInt(32.W) + }) + myBun.foo := 0.U + myBun.bar := 0.U + val howdy = printf(p"hello ${myBun}") + PrintfAnnotation.annotate(howdy) + PrintfAnnotation.annotate(printf(p"goodbye $myBun")) + PrintfAnnotation.annotate(printf(p"adieu $myBun").suggestName("farewell")) + } + + // compile circuit + val testDir = new File("test_run_dir", "PrintfAnnotationTest") + (new ChiselStage).emitSystemVerilog( + gen = new PrintfAnnotationTest, + args = Array("-td", testDir.getPath) + ) + + // read in annotation file + val annoFile = new File(testDir, "PrintfAnnotationTest.anno.json") + annoFile should exist + val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList + + // check for expected annotations + exactly(3, annoLines) should include ("chiselTests.PrintfAnnotation") + exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>farewell") + exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>SIM") + exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>howdy") + + // read in FIRRTL file + val firFile = new File(testDir, "PrintfAnnotationTest.fir") + firFile should exist + val firLines = scala.io.Source.fromFile(firFile).getLines.toList + + // check that verification components have expected names + exactly(1, firLines) should include ("""printf(clock, UInt<1>(1), "hello AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar): howdy""") + exactly(1, firLines) should include ("""printf(clock, UInt<1>(1), "goodbye AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar): SIM""") + exactly(1, firLines) should include ("""printf(clock, UInt<1>(1), "adieu AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar): farewell""") + } } diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala index 14ae202d..46c62d67 100644 --- a/src/test/scala/chiselTests/aop/SelectSpec.scala +++ b/src/test/scala/chiselTests/aop/SelectSpec.scala @@ -22,14 +22,16 @@ class SelectTester(results: Seq[Int]) extends BasicTester { val nreset = reset.asBool() === false.B val selected = values(counter) val zero = 0.U + 0.U + var p: printf.Printf = null when(overflow) { counter := zero stop() }.otherwise { when(nreset) { assert(counter === values(counter)) - printf("values(%d) = %d\n", counter, selected) + p = printf("values(%d) = %d\n", counter, selected) } + } } @@ -81,17 +83,18 @@ class SelectSpec extends ChiselFlatSpec { "Test" should "pass if selecting correct printfs" in { execute( () => new SelectTester(Seq(0, 1, 2)), - { dut: SelectTester => Seq(Select.printfs(dut).last) }, + { dut: SelectTester => Seq(Select.printfs(dut).last.toString) }, { dut: SelectTester => Seq(Select.Printf( + dut.p, Seq( When(Select.ops("eq")(dut).last.asInstanceOf[Bool]), When(dut.nreset), WhenNot(dut.overflow) ), - Printable.pack("values(%d) = %d\n", dut.counter, dut.selected), + dut.p.pable, dut.clock - )) + ).toString) } ) } -- cgit v1.2.3 From bb520b8573328fda5f7b3c3892e6995fbe1b4239 Mon Sep 17 00:00:00 2001 From: Verneri Hirvonen Date: Thu, 8 Jul 2021 18:59:12 +0300 Subject: Add `isOneOf` method to `ChiselEnum` (#1966) * Add @ekiwi's code as a starting point * Add test for ChiselEnum isOneOf method * Make isOneOfTester naming consistent with other testers * Add scaladoc comments for isOneOf * Add isOneOf tests that use the method that takes variable number of args * Add guide level documentation example for isOneOf--- src/test/scala/chiselTests/StrongEnum.scala | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala index e59a5398..d7dea571 100644 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ b/src/test/scala/chiselTests/StrongEnum.scala @@ -304,6 +304,41 @@ class StrongEnumFSMTester extends BasicTester { } } +class IsOneOfTester extends BasicTester { + import EnumExample._ + + // is one of itself + assert(e0.isOneOf(e0)) + + // is one of Seq of itself + assert(e0.isOneOf(Seq(e0))) + assert(e0.isOneOf(Seq(e0, e0, e0, e0))) + assert(e0.isOneOf(e0, e0, e0, e0)) + + // is one of Seq of multiple elements + val subset = Seq(e0, e1, e2) + assert(e0.isOneOf(subset)) + assert(e1.isOneOf(subset)) + assert(e2.isOneOf(subset)) + + // is not element not in subset + assert(!e100.isOneOf(subset)) + assert(!e101.isOneOf(subset)) + + // test multiple elements with variable number of arguments + assert(e0.isOneOf(e0, e1, e2)) + assert(e1.isOneOf(e0, e1, e2)) + assert(e2.isOneOf(e0, e1, e2)) + assert(!e100.isOneOf(e0, e1, e2)) + assert(!e101.isOneOf(e0, e1, e2)) + + // is not another value + assert(!e0.isOneOf(e1)) + assert(!e2.isOneOf(e101)) + + stop() +} + class StrongEnumSpec extends ChiselFlatSpec with Utils { import chisel3.internal.ChiselException @@ -474,6 +509,10 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils { val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) log should not include ("warn") } + + it should "correctly check if the enumeration is one of the values in a given sequence" in { + assertTesterPasses(new IsOneOfTester) + } } class StrongEnumAnnotator extends Module { -- cgit v1.2.3 From 16c0b53e04f3a78ddaaa382936cd660523a57199 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 8 Jul 2021 15:30:28 -0700 Subject: Fix chisel3 <> for Bundles that contain compatibility Bundles (#2023) BiConnect in chisel3 delegates to FIRRTL <- semantics whenever it hits a Bundle defined in `import Chisel._`. Because chisel3 <> is commutative it needs to be mindful of flippedness when emitting a FIRRTL <- (which is *not* commutative).--- .../CompatibilityInteroperabilitySpec.scala | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index cfcc4608..8e9f9e7e 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -289,5 +289,43 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { } } } + + "A chisel3 Bundle that instantiates a Chisel Bundle" should "bulk connect correctly" in { + compile { + object Compat { + import Chisel._ + class Foo extends Bundle { + val a = Input(UInt(8.W)) + val b = Output(UInt(8.W)) + } + } + import chisel3._ + import Compat._ + class Bar extends Bundle { + val foo1 = new Foo + val foo2 = Flipped(new Foo) + } + // Check every connection both ways to see that chisel3 <>'s commutativity holds + class Child extends RawModule { + val deq = IO(new Bar) + val enq = IO(Flipped(new Bar)) + enq <> deq + deq <> enq + } + new RawModule { + val deq = IO(new Bar) + val enq = IO(Flipped(new Bar)) + // Also important to check connections to child ports + val c1 = Module(new Child) + val c2 = Module(new Child) + c1.enq <> enq + enq <> c1.enq + c2.enq <> c1.deq + c1.deq <> c2.enq + deq <> c2.deq + c2.deq <> deq + } + } + } } -- cgit v1.2.3 From 4b7b771eeced366345779a75987ce552558a1c7e Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 8 Jul 2021 17:07:24 -0700 Subject: Make it legal for concrete resets to drive abstract reset (#2018) This has been legal in FIRRTL since v1.2.3 (when reset inference started using a unification-style algorithm) but was never exposed in the Chisel API. Also delete the overridden connects in AsyncReset and ResetType which just duplicate logic from MonoConnect.--- src/test/scala/chiselTests/ResetSpec.scala | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/ResetSpec.scala b/src/test/scala/chiselTests/ResetSpec.scala index 0e535964..7a5d444d 100644 --- a/src/test/scala/chiselTests/ResetSpec.scala +++ b/src/test/scala/chiselTests/ResetSpec.scala @@ -44,6 +44,26 @@ class ResetSpec extends ChiselFlatSpec with Utils { ChiselStage.elaborate(new AbstractResetDontCareModule) } + it should "be able to drive Bool" in { + ChiselStage.emitVerilog(new RawModule { + val in = IO(Input(Bool())) + val out = IO(Output(Bool())) + val w = Wire(Reset()) + w := in + out := w + }) + } + + it should "be able to drive AsyncReset" in { + ChiselStage.emitVerilog(new RawModule { + val in = IO(Input(AsyncReset())) + val out = IO(Output(AsyncReset())) + val w = Wire(Reset()) + w := in + out := w + }) + } + it should "allow writing modules that are reset agnostic" in { val sync = compile(new Module { val io = IO(new Bundle { -- cgit v1.2.3 From 5183ef888274c1d9cc2e22aef95c0e90d86e5122 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Fri, 9 Jul 2021 14:29:45 -0700 Subject: Fix chisel3 <> for Bundles that contain compatibility Bundles (Take 2) (#2031) PR #2023 fixed a composition issue for chisel3 biconnects delegating to FIRRTL partial connect when compatibility mode Bundles are elements of chisel3 Bundles. It missed an important case though that caused previously working code to break. The bug is fixed by doing the automatic flipping for compatibility mode Bundles that have "Input" as a direction in addition to those that are "Flipped".--- .../scala/chiselTests/CompatibilityInteroperabilitySpec.scala | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index 8e9f9e7e..28b8bc80 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -294,16 +294,21 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { compile { object Compat { import Chisel._ - class Foo extends Bundle { + class BiDir extends Bundle { val a = Input(UInt(8.W)) val b = Output(UInt(8.W)) } + class Struct extends Bundle { + val a = UInt(8.W) + } } import chisel3._ import Compat._ class Bar extends Bundle { - val foo1 = new Foo - val foo2 = Flipped(new Foo) + val bidir1 = new BiDir + val bidir2 = Flipped(new BiDir) + val struct1 = Output(new Struct) + val struct2 = Input(new Struct) } // Check every connection both ways to see that chisel3 <>'s commutativity holds class Child extends RawModule { -- cgit v1.2.3 From 695864f5716626a15a7798dae048d8301940a2db Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 15 Jul 2021 02:32:06 +0800 Subject: Espresso Decoder (#1964) Co-authored-by: Haoran Yuan Co-authored-by: Boyang Han --- .../chiselTests/util/experimental/minimizer/EspressoSpec.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/test/scala/chiselTests/util/experimental/minimizer/EspressoSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/EspressoSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/EspressoSpec.scala new file mode 100644 index 00000000..f3270cae --- /dev/null +++ b/src/test/scala/chiselTests/util/experimental/minimizer/EspressoSpec.scala @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.util.experimental.minimizer +import chisel3.util.experimental.decode.EspressoMinimizer +import chisel3.util.experimental.decode.Minimizer + +class EspressoSpec extends MinimizerSpec { + override def minimizer: Minimizer = EspressoMinimizer +} -- cgit v1.2.3 From e6c902eaf6f63413ff8f3b12ad8993cf34447413 Mon Sep 17 00:00:00 2001 From: Leway Colin Date: Thu, 15 Jul 2021 07:22:20 +0800 Subject: Fix Cat rename signal (#2011) Co-authored-by: Jack Koenig --- src/test/scala/chiselTests/CustomBundle.scala | 24 +++++++++++++++++++++ src/test/scala/chiselTests/RecordSpec.scala | 19 +---------------- src/test/scala/chiselTests/util/CatSpec.scala | 30 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 src/test/scala/chiselTests/CustomBundle.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/CustomBundle.scala b/src/test/scala/chiselTests/CustomBundle.scala new file mode 100644 index 00000000..b04dcc59 --- /dev/null +++ b/src/test/scala/chiselTests/CustomBundle.scala @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.experimental.{DataMirror, requireIsChiselType} +import scala.collection.immutable.ListMap + +// An example of how Record might be extended +// In this case, CustomBundle is a Record constructed from a Tuple of (String, Data) +// it is a possible implementation of a programmatic "Bundle" +// (and can by connected to MyBundle below) +final class CustomBundle(elts: (String, Data)*) extends Record { + val elements = ListMap(elts map { case (field, elt) => + requireIsChiselType(elt) + field -> elt + }: _*) + def apply(elt: String): Data = elements(elt) + override def cloneType: this.type = { + val cloned = elts.map { case (n, d) => n -> DataMirror.internal.chiselTypeClone(d) } + (new CustomBundle(cloned: _*)).asInstanceOf[this.type] + } +} + diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index c34c2bf4..c21d455c 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -6,24 +6,7 @@ import chisel3._ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.util.{Counter, Queue} -import chisel3.experimental.{DataMirror, requireIsChiselType} -import scala.collection.immutable.ListMap - -// An example of how Record might be extended -// In this case, CustomBundle is a Record constructed from a Tuple of (String, Data) -// it is a possible implementation of a programmatic "Bundle" -// (and can by connected to MyBundle below) -final class CustomBundle(elts: (String, Data)*) extends Record { - val elements = ListMap(elts map { case (field, elt) => - requireIsChiselType(elt) - field -> elt - }: _*) - def apply(elt: String): Data = elements(elt) - override def cloneType: this.type = { - val cloned = elts.map { case (n, d) => n -> DataMirror.internal.chiselTypeClone(d) } - (new CustomBundle(cloned: _*)).asInstanceOf[this.type] - } -} +import chisel3.experimental.DataMirror trait RecordSpecUtils { class MyBundle extends Bundle { diff --git a/src/test/scala/chiselTests/util/CatSpec.scala b/src/test/scala/chiselTests/util/CatSpec.scala index 5565ca51..79d2c027 100644 --- a/src/test/scala/chiselTests/util/CatSpec.scala +++ b/src/test/scala/chiselTests/util/CatSpec.scala @@ -5,6 +5,7 @@ package chiselTests.util import chisel3._ import chisel3.stage.ChiselStage import chisel3.util.Cat +import chisel3.experimental.noPrefix import chiselTests.ChiselFlatSpec @@ -31,4 +32,33 @@ class CatSpec extends ChiselFlatSpec { } + it should "not override the names of its arguments" in { + class MyModule extends RawModule { + val a, b, c, d = IO(Input(UInt(8.W))) + val out = IO(Output(UInt())) + + out := Cat(a, b, c, d) + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + for (name <- Seq("a", "b", "c", "d")) { + chirrtl should include (s"input $name : UInt<8>") + } + } + + it should "have prefixed naming" in { + class MyModule extends RawModule { + val in = IO(Input(Vec(8, UInt(8.W)))) + val out = IO(Output(UInt())) + + // noPrefix to avoid `out` as prefix + out := noPrefix(Cat(in)) + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include ("node lo_lo = cat(in[6], in[7])") + chirrtl should include ("node lo_hi = cat(in[4], in[5])") + chirrtl should include ("node hi_lo = cat(in[2], in[3])") + chirrtl should include ("node hi_hi = cat(in[0], in[1])") + } + + } -- cgit v1.2.3 From 0666933b3e902192ef57723a92b57d41d50b3f8e Mon Sep 17 00:00:00 2001 From: Jared Barocsi Date: Wed, 28 Jul 2021 14:53:04 -0700 Subject: Bundles can no longer be instantiated with bound hardware (#2046) Co-authored-by: Jack Koenig --- src/test/scala/chiselTests/BundleSpec.scala | 34 ++++++++++++++++++----------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/BundleSpec.scala b/src/test/scala/chiselTests/BundleSpec.scala index 5d3f23ec..1d392f5c 100644 --- a/src/test/scala/chiselTests/BundleSpec.scala +++ b/src/test/scala/chiselTests/BundleSpec.scala @@ -129,6 +129,27 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils { }).getMessage should include("aliased fields") } + "Bundles" should "not have bound hardware" in { + (the[ChiselException] thrownBy extractCause[ChiselException] { + ChiselStage.elaborate { new Module { + class MyBundle(val foo: UInt) extends Bundle + val in = IO(Input(new MyBundle(123.U))) // This should error: value passed in instead of type + val out = IO(Output(new MyBundle(UInt(8.W)))) + + out := in + } } + }).getMessage should include("must be a Chisel type, not hardware") + } + "Bundles" should "not recursively contain aggregates with bound hardware" in { + (the[ChiselException] thrownBy extractCause[ChiselException] { + ChiselStage.elaborate { new Module { + class MyBundle(val foo: UInt) extends Bundle + val out = IO(Output(Vec(2, UInt(8.W)))) + val in = IO(Input(new MyBundle(out(0)))) // This should error: Bound aggregate passed + out := in + } } + }).getMessage should include("must be a Chisel type, not hardware") + } "Unbound bundles sharing a field" should "not error" in { ChiselStage.elaborate { new RawModule { @@ -141,17 +162,4 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils { } } } - - "Bound Data" should "have priority in setting ref over unbound Data" in { - class MyModule extends RawModule { - val foo = IO(new Bundle { - val x = Output(UInt(8.W)) - }) - foo.x := 0.U // getRef on foo.x is None.get without fix - val bar = new Bundle { - val y = foo.x - } - } - ChiselStage.emitChirrtl(new MyModule) - } } -- cgit v1.2.3 From da923f317ff325a93cee6289552ccfa413c35f98 Mon Sep 17 00:00:00 2001 From: anniej-sifive Date: Tue, 3 Aug 2021 16:09:19 -0700 Subject: Added flush capability to Queue (#2030) Co-authored-by: Megan Wachs Co-authored-by: Deborah Soung --- src/test/scala/chiselTests/QueueFlushSpec.scala | 259 ++++++++++++++++++++++++ src/test/scala/chiselTests/QueueSpec.scala | 8 +- 2 files changed, 263 insertions(+), 4 deletions(-) create mode 100644 src/test/scala/chiselTests/QueueFlushSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/QueueFlushSpec.scala b/src/test/scala/chiselTests/QueueFlushSpec.scala new file mode 100644 index 00000000..11a411a8 --- /dev/null +++ b/src/test/scala/chiselTests/QueueFlushSpec.scala @@ -0,0 +1,259 @@ +package chiselTests + +import org.scalacheck._ + +import chisel3._ +import chisel3.testers.{BasicTester, TesterDriver} +import chisel3.util._ +import chisel3.util.random.LFSR +import treadle.WriteVcdAnnotation + +/** Test elements can be enqueued and dequeued when flush is tied to false + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class ThingsPassThroughFlushQueueTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends ThingsPassThroughTester(elements, queueDepth, bitWidth, tap, useSyncReadMem, hasFlush = true) + +/** Generic flush queue tester base class + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +abstract class FlushQueueTesterBase(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, hasFlush = true)) + val elems = VecInit(elements.map(_.U)) + val inCnt = Counter(elements.length + 1) + val outCnt = RegInit(0.U(log2Ceil(elements.length).W)) + val currQCnt = RegInit(0.U(log2Ceil(5).W)) + + val flush: Bool = WireInit(false.B) + val flushRegister = RegNext(flush, init = false.B) + q.io.flush.get := flush + q.io.enq.valid := (inCnt.value < elements.length.U) + q.io.deq.ready := LFSR(16)(tap) + + q.io.enq.bits := elems(inCnt.value) + when(q.io.enq.fire()) { + inCnt.inc() + currQCnt := currQCnt + 1.U //counts how many items have been enqueued + } + when(q.io.deq.fire()) { + assert(flushRegister === false.B) //check queue isn't flushed (can't dequeue an empty queue) + } + when(flushRegister) { //Internal signal maybe_full is a register so some signals update on the next cycle + //check that queue gets flushed when queue is full + assert(q.io.count === 0.U) + assert(!q.io.deq.valid, "Expected to not be able to dequeue when flush is asserted the previous cycle") + assert(q.io.enq.ready, "Expected enqueue to be ready when flush was asserted the previous cycle because queue should be empty") + } + when(inCnt.value === elements.length.U) { //stop when all entries are enqueued + stop() + } +} + +/** Test queue can flush at random times + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class QueueGetsFlushedTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + flush := LFSR(16)((tap + 3) % 16) //testing a flush when flush is called randomly + val halfCnt = (queueDepth + 1)/2 + + when(q.io.deq.fire()) { + //ensure that what comes out is what comes in + assert(currQCnt <= queueDepth.U) + assert(elems(outCnt) === q.io.deq.bits) + outCnt := outCnt + 1.U + when (currQCnt > 0.U) { + currQCnt := Mux(q.io.enq.fire(), currQCnt, (currQCnt - 1.U)) + } + } + when(flush) { + assert(currQCnt === 0.U || q.io.deq.valid) + outCnt := outCnt + Mux(q.io.enq.fire(), (currQCnt + 1.U), currQCnt) + currQCnt := 0.U //resets the number of items currently inside queue + } +} + +/** Test queue can flush when empty + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class EmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + val cycleCounter = Counter(elements.length + 1) + cycleCounter.inc() //counts every cycle + + //testing a flush when queue is empty + flush := (cycleCounter.value === 0.U && inCnt.value === 0.U) //flushed only before anything is enqueued + q.io.enq.valid := (inCnt.value < elements.length.U) && !flush + + when(q.io.deq.fire()) { + assert(elems(outCnt) === q.io.deq.bits) + outCnt := outCnt + 1.U + } +} + +/** Test queue can enqueue during a flush + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class EnqueueEmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + val cycleCounter = Counter(elements.length + 1) + val outCounter = Counter(elements.length + 1) + + //testing an enqueue during a flush + flush := (cycleCounter.value === 0.U && inCnt.value === 0.U) //flushed only before anything is enqueued + cycleCounter.inc() //counts every cycle + + when(q.io.deq.fire()) { + //flush and enqueue were both active on the first cycle, + //so that element is flushed immediately which makes outCnt off by one + assert(elems(outCounter.value + 1.U) === q.io.deq.bits) //ensure that what comes out is what comes in + outCounter.inc() + } +} + +/** Test queue can flush when full + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class FullQueueFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + + //testing a flush when queue is full + flush := (currQCnt === queueDepth.U) + + when(q.io.deq.fire()) { + //ensure that what comes out is what comes in + assert(currQCnt <= queueDepth.U) + assert(elems(outCnt) === q.io.deq.bits) + outCnt := outCnt + 1.U + when (currQCnt > 0.U) { + currQCnt := currQCnt - 1.U + } + } + when(flush) { + outCnt := outCnt + currQCnt + currQCnt := 0.U //resets the number of items currently inside queue + assert(currQCnt === 0.U || q.io.deq.valid) + } +} + +/** Test queue can dequeue on the same cycle as a flush + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class DequeueFullQueueEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + //Queue should be able to dequeue when queue is not empty and flush is high + + //testing a flush when dequeue is called + flush := currQCnt === (queueDepth/2).U + q.io.enq.valid := !flushRegister + q.io.deq.ready := flush + + when(q.io.deq.fire()) { + //ensure that what comes out is what comes in + assert(currQCnt <= queueDepth.U) + assert(elems(outCnt) === q.io.deq.bits) + assert(currQCnt > 0.U) + } + when(flush) { + //The outcount register is one count behind because the dequeue happens at the same time as the flush + outCnt := outCnt + currQCnt + 1.U + currQCnt := 0.U //resets the number of items currently inside queue + assert(currQCnt === 0.U || q.io.deq.valid) + } + when(flushRegister) { + //check that queue gets flushed when queue is full + assert(q.io.deq.fire() === false.B) + } + +} + +class QueueFlushSpec extends ChiselPropSpec { + // Disable shrinking on error. + implicit val noShrinkListVal = Shrink[List[Int]](_ => Stream.empty) + implicit val noShrinkInt = Shrink[Int](_ => Stream.empty) + + property("Queue should have things pass through") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new ThingsPassThroughFlushQueueTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Queue should flush when requested") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new QueueGetsFlushedTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Queue flush when queue is empty") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new EmptyFlushEdgecaseTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Test queue can enqueue during a flush") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new EnqueueEmptyFlushEdgecaseTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Queue flush when queue is full") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new FullQueueFlushEdgecaseTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Queue should be able to dequeue when flush is high") { + forAll(Gen.choose(3, 5), safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses ( + new DequeueFullQueueEdgecaseTester(se._2, depth, se._1, tap, isSync), + annotations = Seq(WriteVcdAnnotation) + ) + } + } + } +} diff --git a/src/test/scala/chiselTests/QueueSpec.scala b/src/test/scala/chiselTests/QueueSpec.scala index 3555a13c..51b899cb 100644 --- a/src/test/scala/chiselTests/QueueSpec.scala +++ b/src/test/scala/chiselTests/QueueSpec.scala @@ -9,8 +9,8 @@ import chisel3.testers.BasicTester import chisel3.util._ import chisel3.util.random.LFSR -class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem)) +class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean, hasFlush: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem, hasFlush = hasFlush)) val elems = VecInit(elements.map { _.asUInt() }) @@ -19,7 +19,7 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int q.io.enq.valid := (inCnt.value < elements.length.U) q.io.deq.ready := LFSR(16)(tap) - + q.io.flush.foreach { _ := false.B } //Flush behavior is tested in QueueFlushSpec q.io.enq.bits := elems(inCnt.value) when(q.io.enq.fire()) { inCnt.inc() @@ -205,7 +205,7 @@ class QueueSpec extends ChiselPropSpec { forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { assertTesterPasses { - new ThingsPassThroughTester(se._2, depth, se._1, tap, isSync) + new ThingsPassThroughTester(se._2, depth, se._1, tap, isSync, false) } } } -- cgit v1.2.3 From 5a9b6487fbc5ccf0243f9755ee3cd88ec6036e2c Mon Sep 17 00:00:00 2001 From: anniej-sifive Date: Wed, 4 Aug 2021 12:11:58 -0700 Subject: Added VecInit factory methods (fill,iterate) (#2059) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/test/scala/chiselTests/Vec.scala | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala index 1327913d..f3160c2e 100644 --- a/src/test/scala/chiselTests/Vec.scala +++ b/src/test/scala/chiselTests/Vec.scala @@ -2,6 +2,8 @@ package chiselTests +import org.scalacheck._ + import chisel3._ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester @@ -104,6 +106,21 @@ class TabulateTester(n: Int) extends BasicTester { stop() } +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") + stop() +} + +class IterateTester(start: Int, len: Int)(f: UInt => UInt) extends BasicTester { + val controlVec = VecInit(Seq.iterate(start.U, len)(f)) + 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") + stop() +} + class ShiftRegisterTester(n: Int) extends BasicTester { val (cnt, wrap) = Counter(true.B, n*2) val shifter = Reg(Vec(n, UInt((log2Ceil(n) max 1).W))) @@ -160,7 +177,6 @@ class PassthroughModuleTester extends Module { assert(io.out === 123.U) } - class ModuleIODynamicIndexTester(n: Int) extends BasicTester { val duts = VecInit(Seq.fill(n)(Module(new PassthroughModule).io)) val tester = Module(new PassthroughModuleTester) @@ -219,10 +235,18 @@ class VecSpec extends ChiselPropSpec with Utils { } } - property("Vecs should tabulate correctly") { + property("VecInit should tabulate correctly") { forAll(smallPosInts) { (n: Int) => assertTesterPasses{ new TabulateTester(n) } } } + property("VecInit should fill correctly") { + forAll(smallPosInts, Gen.choose(0, 50)) { (n: Int, value: Int) => assertTesterPasses{ new FillTester(n, value) } } + } + + property("VecInit should iterate correctly") { + forAll(Gen.choose(1, 10), smallPosInts) { (start: Int, len: Int) => assertTesterPasses{ new IterateTester(start, len)(x => x + 50.U)}} + } + property("Regs of vecs should be usable as shift registers") { forAll(smallPosInts) { (n: Int) => assertTesterPasses{ new ShiftRegisterTester(n) } } } -- cgit v1.2.3 From 1ceb974c55c6785c21ab3934fa750ade0702e276 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 12 Aug 2021 17:04:11 -0700 Subject: Add DataView (#1955) DataView is a mechanism for "viewing" Scala objects as a subtype of `Data`. Often, this is useful for viewing one subtype of `Data`, as another. One can think about a DataView as a cross between a customizable cast and an untagged union. A DataView has a Target type `T`, and a View type `V`. DataView requires that an implementation of `DataProduct` is available for Target types. DataProduct is a type class that provides a way to iterate on `Data` children of objects of implementing types. If a DataView is provided for a type T to a type V, then the function .viewAs[V] (of type T => V) is available. The object (of type T) returned by .viewAs is called a "View" and can be used as both an rvalue and an lvalue. Unlike when using an .asTypeOf cast, connecting to a "View" will connect to the associated field or fields of the underlying Target. DataView also enables .viewAsSupertype which is available for viewing Bundles as a parent Bundle type. It is similar to .viewAs but requires a prototype object of the Target type which will be cloned in order to create the returned View. .viewAsSupertype maps between the corresponding fields of the parent and child Bundle types.--- src/test/scala/chiselTests/ChiselSpec.scala | 29 ++ .../scala/chiselTests/experimental/DataView.scala | 542 +++++++++++++++++++++ .../experimental/DataViewIntegrationSpec.scala | 57 +++ .../experimental/DataViewTargetSpec.scala | 169 +++++++ .../experimental/ModuleDataProductSpec.scala | 91 ++++ .../chiselTests/stage/ChiselOptionsViewSpec.scala | 3 +- 6 files changed, 890 insertions(+), 1 deletion(-) create mode 100644 src/test/scala/chiselTests/experimental/DataView.scala create mode 100644 src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala create mode 100644 src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala create mode 100644 src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index e513189e..8e35273d 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -7,9 +7,11 @@ import chisel3.aop.Aspect import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation} import chisel3.testers._ import firrtl.annotations.Annotation +import firrtl.ir.Circuit import firrtl.util.BackendCompilationUtilities import firrtl.{AnnotationSeq, EmittedVerilogCircuitAnnotation} import _root_.logger.Logger +import firrtl.stage.FirrtlCircuitAnnotation import org.scalacheck._ import org.scalatest._ import org.scalatest.flatspec.AnyFlatSpec @@ -87,6 +89,33 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { case EmittedVerilogCircuitAnnotation(a) => a.value }.getOrElse(fail("No Verilog circuit was emitted by the FIRRTL compiler!")) } + + def elaborateAndGetModule[A <: RawModule](t: => A): A = { + var res: Any = null + ChiselStage.elaborate { + res = t + res.asInstanceOf[A] + } + res.asInstanceOf[A] + } + + /** Compiles a Chisel Module to FIRRTL + * NOTE: This uses the "test_run_dir" as the default directory for generated code. + * @param t the generator for the module + * @return The FIRRTL Circuit and Annotations _before_ FIRRTL compilation + */ + def getFirrtlAndAnnos(t: => RawModule): (Circuit, Seq[Annotation]) = { + val args = Array( + "--target-dir", + createTestDirectory(this.getClass.getSimpleName).toString, + "--no-run-firrtl" + ) + val annos = (new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => t))) + val circuit = annos.collectFirst { + case FirrtlCircuitAnnotation(c) => c + }.getOrElse(fail("No FIRRTL Circuit found!!")) + (circuit, annos) + } } /** Spec base class for BDD-style testers. */ diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala new file mode 100644 index 00000000..381cfeb5 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -0,0 +1,542 @@ +// See LICENSE for license details. + +package chiselTests.experimental + +import chiselTests.ChiselFlatSpec +import chisel3._ +import chisel3.experimental.dataview._ +import chisel3.experimental.DataMirror.internal.chiselTypeClone +import chisel3.stage.ChiselStage +import chisel3.util.{Decoupled, DecoupledIO} + +object SimpleBundleDataView { + class BundleA(val w: Int) extends Bundle { + val foo = UInt(w.W) + } + class BundleB(val w: Int) extends Bundle { + val bar = UInt(w.W) + } + implicit val v1 = DataView[BundleA, BundleB](a => new BundleB(a.w), _.foo -> _.bar) + implicit val v2 = v1.invert(b => new BundleA(b.w)) +} + +object VecBundleDataView { + class MyBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + } + implicit val v1: DataView[MyBundle, Vec[UInt]] = DataView(_ => Vec(2, UInt(8.W)), _.foo -> _(1), _.bar -> _(0)) + implicit val v2 = v1.invert(_ => new MyBundle) +} + +// This should become part of Chisel in a later PR +object Tuple2DataProduct { + implicit def tuple2DataProduct[A : DataProduct, B : DataProduct] = new DataProduct[(A, B)] { + def dataIterator(tup: (A, B), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val (a, b) = tup + dpa.dataIterator(a, s"$path._1") ++ dpb.dataIterator(b, s"$path._2") + } + } +} + +// This should become part of Chisel in a later PR +object HWTuple { + import Tuple2DataProduct._ + + class HWTuple2[A <: Data, B <: Data](val _1: A, val _2: B) extends Bundle + + implicit def view[T1 : DataProduct, T2 : DataProduct, V1 <: Data, V2 <: Data]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2] + ): DataView[(T1, T2), HWTuple2[V1, V2]] = + DataView.mapping( + { case (a, b) => new HWTuple2(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType)}, + { case ((a, b), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2) + } + ) + + implicit def tuple2hwtuple[T1 : DataProduct, T2 : DataProduct, V1 <: Data, V2 <: Data]( + tup: (T1, T2))(implicit v1: DataView[T1, V1], v2: DataView[T2, V2] + ): HWTuple2[V1, V2] = tup.viewAs[HWTuple2[V1, V2]] +} + +// This should become part of Chisel in a later PR +object SeqDataProduct { + // Should we special case Seq[Data]? + implicit def seqDataProduct[A : DataProduct]: DataProduct[Seq[A]] = new DataProduct[Seq[A]] { + def dataIterator(a: Seq[A], path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + a.iterator + .zipWithIndex + .flatMap { case (elt, idx) => + dpa.dataIterator(elt, s"$path[$idx]") + } + } + } +} + +object SeqToVec { + import SeqDataProduct._ + + // TODO this would need a better way to determine the prototype for the Vec + implicit def seqVec[A : DataProduct, B <: Data](implicit dv: DataView[A, B]): DataView[Seq[A], Vec[B]] = + DataView.mapping[Seq[A], Vec[B]]( + xs => Vec(xs.size, chiselTypeClone(xs.head.viewAs[B])), // xs.head is not correct in general + { case (s, v) => s.zip(v).map { case (a, b) => a.viewAs[B] -> b } } + ) + + implicit def seq2Vec[A : DataProduct, B <: Data](xs: Seq[A])(implicit dv: DataView[A, B]): Vec[B] = + xs.viewAs[Vec[B]] +} + +class DataViewSpec extends ChiselFlatSpec { + + behavior of "DataView" + + it should "support simple Bundle viewing" in { + import SimpleBundleDataView._ + class MyModule extends Module { + val in = IO(Input(new BundleA(8))) + val out = IO(Output(new BundleB(8))) + out := in.viewAs[BundleB] + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("out.bar <= in.foo") + } + + it should "be a bidirectional mapping" in { + import SimpleBundleDataView._ + class MyModule extends Module { + val in = IO(Input(new BundleA(8))) + val out = IO(Output(new BundleB(8))) + out.viewAs[BundleA] := in + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("out.bar <= in.foo") + } + + it should "handle viewing UInts as UInts" in { + class MyModule extends Module { + val in = IO(Input(UInt(8.W))) + val foo = IO(Output(UInt(8.W))) + val bar = IO(Output(UInt(8.W))) + foo := in.viewAs[UInt] + bar.viewAs[UInt] := in + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("foo <= in") + chirrtl should include("bar <= in") + } + + it should "handle viewing Bundles as their same concrete type" in { + class MyBundle extends Bundle { + val foo = UInt(8.W) + } + class MyModule extends Module { + val in = IO(Input(new MyBundle)) + val fizz = IO(Output(new MyBundle)) + val buzz = IO(Output(new MyBundle)) + fizz := in.viewAs[MyBundle] + buzz.viewAs[MyBundle] := in + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("fizz.foo <= in.foo") + chirrtl should include("buzz.foo <= in.foo") + } + + it should "handle viewing Vecs as their same concrete type" in { + class MyModule extends Module { + val in = IO(Input(Vec(1, UInt(8.W)))) + val fizz = IO(Output(Vec(1, UInt(8.W)))) + val buzz = IO(Output(Vec(1, UInt(8.W)))) + fizz := in.viewAs[Vec[UInt]] + buzz.viewAs[Vec[UInt]] := in + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("fizz[0] <= in[0]") + chirrtl should include("buzz[0] <= in[0]") + } + + it should "handle viewing Vecs as Bundles and vice versa" in { + import VecBundleDataView._ + class MyModule extends Module { + val in = IO(Input(new MyBundle)) + val out = IO(Output(Vec(2, UInt(8.W)))) + val out2 = IO(Output(Vec(2, UInt(8.W)))) + out := in.viewAs[Vec[UInt]] + out2.viewAs[MyBundle] := in + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("out[0] <= in.bar") + chirrtl should include("out[1] <= in.foo") + chirrtl should include("out2[0] <= in.bar") + chirrtl should include("out2[1] <= in.foo") + } + + it should "work with bidirectional connections for nested types" in { + class FizzBuzz extends Bundle { + val fizz = UInt(8.W) + val buzz = UInt(8.W) + } + class FlatDecoupled extends Bundle { + val valid = Output(Bool()) + val ready = Input(Bool()) + val fizz = Output(UInt(8.W)) + val buzz = Output(UInt(8.W)) + } + implicit val view = DataView[FlatDecoupled, DecoupledIO[FizzBuzz]]( + _ => Decoupled(new FizzBuzz), + _.valid -> _.valid, + _.ready -> _.ready, + _.fizz -> _.bits.fizz, + _.buzz -> _.bits.buzz + ) + implicit val view2 = view.invert(_ => new FlatDecoupled) + class MyModule extends Module { + val enq = IO(Flipped(Decoupled(new FizzBuzz))) + val deq = IO(new FlatDecoupled) + val deq2 = IO(new FlatDecoupled) + deq <> enq.viewAs[FlatDecoupled] + deq2.viewAs[DecoupledIO[FizzBuzz]] <> enq + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("deq.valid <= enq.valid") + chirrtl should include("enq.ready <= deq.ready") + chirrtl should include("deq.fizz <= enq.bits.fizz") + chirrtl should include("deq.buzz <= enq.bits.buzz") + chirrtl should include("deq2.valid <= enq.valid") + chirrtl should include("enq.ready <= deq2.ready") + chirrtl should include("deq2.fizz <= enq.bits.fizz") + chirrtl should include("deq2.buzz <= enq.bits.buzz") + } + + it should "support viewing a Bundle as a Parent Bundle type" in { + class Foo extends Bundle { + val foo = UInt(8.W) + } + class Bar extends Foo { + val bar = UInt(8.W) + } + class MyModule extends Module { + val fooIn = IO(Input(new Foo)) + val barOut = IO(Output(new Bar)) + barOut.viewAsSupertype(new Foo) := fooIn + + val barIn = IO(Input(new Bar)) + val fooOut = IO(Output(new Foo)) + fooOut := barIn.viewAsSupertype(new 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 { + val foo = UInt(8.W) + } + class Bar extends Foo { + val bar = UInt(8.W) + } + class MyModule extends Module { + val barIn = IO(Input(new Bar)) + val fooOut = IO(Output(new Foo)) + fooOut.viewAs(new Bar) := barIn + } + """) + } + + it should "work in UInt operations" in { + class MyBundle extends Bundle { + val value = UInt(8.W) + } + class MyModule extends Module { + val a = IO(Input(UInt(8.W))) + val b = IO(Input(new MyBundle)) + val cond = IO(Input(Bool())) + val and, mux, bitsCat = IO(Output(UInt(8.W))) + // Chisel unconditionally emits a node, so name it at least + val x = a.viewAs[UInt] & b.viewAs[MyBundle].value + and.viewAs[UInt] := x + + val y = Mux(cond.viewAs[Bool], a.viewAs[UInt], b.value.viewAs[UInt]) + mux.viewAs[UInt] := y + + // TODO should we have a macro so that we don't need .apply? + val aBits = a.viewAs[UInt].apply(3, 0) + val bBits = b.viewAs[MyBundle].value(3, 0) + val abCat = aBits.viewAs[UInt] ## bBits.viewAs[UInt] + bitsCat := abCat + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + val expected = List( + "node x = and(a, b.value)", + "and <= x", + "node y = mux(cond, a, b.value)", + "mux <= y", + "node aBits = bits(a, 3, 0)", + "node bBits = bits(b.value, 3, 0)", + "node abCat = cat(aBits, bBits)", + "bitsCat <= abCat" + ) + for (line <- expected) { + chirrtl should include(line) + } + } + + it should "support .asUInt of Views" in { + import VecBundleDataView._ + class MyModule extends Module { + val barIn = IO(Input(new MyBundle)) + val fooOut = IO(Output(UInt())) + val cat = barIn.viewAs[Vec[UInt]].asUInt + fooOut := cat + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include ("node cat = cat(barIn.foo, barIn.bar)") + chirrtl should include ("fooOut <= cat") + } + + it should "be composable" in { + // Given DataView[A, B] and DataView[B, C], derive DataView[A, C] + class Foo(val foo: UInt) extends Bundle + class Bar(val bar: UInt) extends Bundle + class Fizz(val fizz: UInt) extends Bundle + + implicit val foo2bar = DataView[Foo, Bar](f => new Bar(chiselTypeClone(f.foo)), _.foo -> _.bar) + implicit val bar2fizz = DataView[Bar, Fizz](b => new Fizz(chiselTypeClone(b.bar)), _.bar -> _.fizz) + + implicit val foo2fizz: DataView[Foo, Fizz] = foo2bar.andThen(bar2fizz) + + class MyModule extends Module { + val a, b = IO(Input(new Foo(UInt(8.W)))) + val y, z = IO(Output(new Fizz(UInt(8.W)))) + y := a.viewAs[Fizz] + z := b.viewAs[Bar].viewAs[Fizz] + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include ("y.fizz <= a.foo") + chirrtl should include ("z.fizz <= b.foo") + } + + // This example should be turned into a built-in feature + it should "enable implementing \"HardwareTuple\"" in { + import HWTuple._ + + class MyModule extends Module { + val a, b, c, d = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val y, z = IO(Output(UInt(8.W))) + (y, z) := Mux(sel, (a, b), (c, d)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + verilog should include ("assign y = sel ? a : c;") + verilog should include ("assign z = sel ? b : d;") + } + + it should "support nesting of tuples" in { + import Tuple2DataProduct._ + import HWTuple._ + + class MyModule extends Module { + val a, b, c, d = IO(Input(UInt(8.W))) + val w, x, y, z = IO(Output(UInt(8.W))) + ((w, x), (y, z)) := ((a, b), (c, d)) + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include ("w <= a") + chirrtl should include ("x <= b") + chirrtl should include ("y <= c") + chirrtl should include ("z <= d") + } + + // This example should be turned into a built-in feature + it should "enable viewing Seqs as Vecs" in { + import SeqToVec._ + + class MyModule extends Module { + val a, b, c = IO(Input(UInt(8.W))) + val x, y, z = IO(Output(UInt(8.W))) + Seq(x, y, z) := VecInit(a, b, c) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + verilog should include ("assign x = a;") + verilog should include ("assign y = b;") + verilog should include ("assign z = c;") + } + + it should "support recursive composition of views" in { + import Tuple2DataProduct._ + import SeqDataProduct._ + import SeqToVec._ + import HWTuple._ + + class MyModule extends Module { + val a, b, c, d = IO(Input(UInt(8.W))) + val w, x, y, z = IO(Output(UInt(8.W))) + + // A little annoying that we need the type annotation on VecInit to get the implicit conversion to work + // Note that one can just use the Seq on the RHS so there is an alternative (may lack discoverability) + // We could also overload `VecInit` instead of relying on the implicit conversion + Seq((w, x), (y, z)) := VecInit[HWTuple2[UInt, UInt]]((a, b), (c, d)) + } + val verilog = ChiselStage.emitVerilog(new MyModule) + verilog should include ("assign w = a;") + verilog should include ("assign x = b;") + verilog should include ("assign y = c;") + verilog should include ("assign z = d;") + } + + it should "error if you try to dynamically index a Vec view" in { + import SeqDataProduct._ + import SeqToVec._ + import Tuple2DataProduct._ + import HWTuple._ + + class MyModule extends Module { + val inA, inB = IO(Input(UInt(8.W))) + val outA, outB = IO(Output(UInt(8.W))) + val idx = IO(Input(UInt(1.W))) + + val a, b, c, d = RegInit(0.U) + + // Dynamic indexing is more of a "generator" in Chisel3 than an individual node + val selected = Seq((a, b), (c, d)).apply(idx) + selected := (inA, inB) + (outA, outB) := selected + } + (the [InvalidViewException] thrownBy { + ChiselStage.emitChirrtl(new MyModule) + }).getMessage should include ("Dynamic indexing of Views is not yet supported") + } + + it should "error if the mapping is non-total in the view" in { + class MyBundle(val foo: UInt, val bar: UInt) extends Bundle + implicit val dv = DataView[UInt, MyBundle](_ => new MyBundle(UInt(), UInt()), _ -> _.bar) + class MyModule extends Module { + val tpe = new MyBundle(UInt(8.W), UInt(8.W)) + val in = IO(Input(UInt(8.W))) + val out = IO(Output(tpe)) + out := in.viewAs[MyBundle] + } + val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule)) + err.toString should include ("View field '_.foo' is missing") + } + + it should "error if the mapping is non-total in the target" in { + import Tuple2DataProduct._ + implicit val dv = DataView[(UInt, UInt), UInt](_ => UInt(), _._1 -> _) + class MyModule extends Module { + val a, b = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := (a, b).viewAs[UInt] + } + val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule)) + err.toString should include ("Target field '_._2' is missing") + } + + it should "error if the mapping contains Data that are not part of the Target" in { + class BundleA extends Bundle { + val foo = UInt(8.W) + } + class BundleB extends Bundle { + val fizz = UInt(8.W) + val buzz = UInt(8.W) + } + implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.fizz, (_, b) => (3.U, b.buzz)) + class MyModule extends Module { + val in = IO(Input(new BundleA)) + val out = IO(Output(new BundleB)) + out := in.viewAs[BundleB] + } + val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule)) + err.toString should include ("View mapping must only contain Elements within the Target") + } + + it should "error if the mapping contains Data that are not part of the View" in { + class BundleA extends Bundle { + val foo = UInt(8.W) + } + class BundleB extends Bundle { + val fizz = UInt(8.W) + val buzz = UInt(8.W) + } + implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.fizz, (_, b) => (3.U, b.buzz)) + implicit val dv2 = dv.invert(_ => new BundleA) + class MyModule extends Module { + val in = IO(Input(new BundleA)) + val out = IO(Output(new BundleB)) + out.viewAs[BundleA] := in + } + val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule)) + err.toString should include ("View mapping must only contain Elements within the View") + } + + it should "error if a view has a width that does not match the target" in { + class BundleA extends Bundle { + val foo = UInt(8.W) + } + class BundleB extends Bundle { + val bar = UInt(4.W) + } + implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.bar) + class MyModule extends Module { + val in = IO(Input(new BundleA)) + val out = IO(Output(new BundleB)) + out := in.viewAs[BundleB] + } + val err = the [InvalidViewException] thrownBy ChiselStage.emitChirrtl(new MyModule) + val expected = """View field _\.bar UInt<4> has width <4> that is incompatible with target value .+'s width <8>""".r + err.getMessage should fullyMatch regex expected + } + + it should "error if a view has a known width when the target width is unknown" in { + class BundleA extends Bundle { + val foo = UInt() + } + class BundleB extends Bundle { + val bar = UInt(4.W) + } + implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.bar) + class MyModule extends Module { + val in = IO(Input(new BundleA)) + val out = IO(Output(new BundleB)) + out := in.viewAs[BundleB] + } + val err = the [InvalidViewException] thrownBy ChiselStage.emitChirrtl(new MyModule) + val expected = """View field _\.bar UInt<4> has width <4> that is incompatible with target value .+'s width """.r + err.getMessage should fullyMatch regex expected + } + + behavior of "PartialDataView" + + it should "still error if the mapping is non-total in the view" in { + class MyBundle(val foo: UInt, val bar: UInt) extends Bundle + implicit val dv = PartialDataView[UInt, MyBundle](_ => new MyBundle(UInt(), UInt()), _ -> _.bar) + class MyModule extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(new MyBundle(UInt(8.W), UInt(8.W)))) + out := in.viewAs[MyBundle] + } + val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule)) + err.toString should include ("View field '_.foo' is missing") + } + + it should "NOT error if the mapping is non-total in the target" in { + import Tuple2DataProduct._ + implicit val dv = PartialDataView[(UInt, UInt), UInt](_ => UInt(), _._2 -> _) + class MyModule extends Module { + val a, b = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := (a, b).viewAs[UInt] + } + val verilog = ChiselStage.emitVerilog(new MyModule) + verilog should include ("assign out = b;") + } +} diff --git a/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala b/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala new file mode 100644 index 00000000..3f149f75 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala @@ -0,0 +1,57 @@ +// See LICENSE for license details. + +package chiselTests.experimental + +import chisel3._ +import chisel3.experimental.{BaseModule, ExtModule} +import chisel3.experimental.dataview._ +import chisel3.util.{Decoupled, DecoupledIO, Queue, QueueIO, log2Ceil} +import chiselTests.ChiselFlatSpec +import firrtl.transforms.DontTouchAnnotation + +// Let's put it all together! +object DataViewIntegrationSpec { + + class QueueIntf[T <: Data](gen: T, entries: Int) extends Bundle { + val ports = new QueueIO(gen, entries) + // Let's grab a reference to something internal too + // Output because can't have directioned and undirectioned stuff + val enq_ptr = Output(UInt(log2Ceil(entries).W)) + } + + // It's not clear if a view of a Module ever _can_ be total since internal nodes are part of the Module + implicit def queueView[T <: Data] = PartialDataView[Queue[T], QueueIntf[T]]( + q => new QueueIntf(q.gen, q.entries), + _.io -> _.ports, + // Some token internal signal + _.enq_ptr.value -> _.enq_ptr + ) + + object MyQueue { + def apply[T <: Data](enq: DecoupledIO[T], n: Int): QueueIntf[T] = { + val queue = Module(new Queue[T](enq.bits.cloneType, n)) + val view = queue.viewAs[QueueIntf[T]] + view.ports.enq <> enq + view + } + } + + class MyModule extends Module { + val enq = IO(Flipped(Decoupled(UInt(8.W)))) + val deq = IO(Decoupled(UInt(8.W))) + + val queue = MyQueue(enq, 4) + deq <> queue.ports.deq + dontTouch(queue.enq_ptr) + } +} + +class DataViewIntegrationSpec extends ChiselFlatSpec { + import DataViewIntegrationSpec.MyModule + + "Users" should "be able to view and annotate Modules" in { + val (_, annos) = getFirrtlAndAnnos(new MyModule) + val ts = annos.collect { case DontTouchAnnotation(t) => t.serialize } + ts should equal (Seq("~MyModule|Queue>enq_ptr_value")) + } +} diff --git a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala new file mode 100644 index 00000000..41636da7 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala @@ -0,0 +1,169 @@ +// See LICENSE for license details. + +package chiselTests.experimental + +import chisel3._ +import chisel3.experimental.dataview._ +import chisel3.experimental.{ChiselAnnotation, annotate} +import chisel3.stage.ChiselStage +import chiselTests.ChiselFlatSpec + +object DataViewTargetSpec { + import firrtl.annotations._ + private case class DummyAnno(target: ReferenceTarget, id: Int) extends SingleTargetAnnotation[ReferenceTarget] { + override def duplicate(n: ReferenceTarget) = this.copy(target = n) + } + private def mark(d: Data, id: Int) = annotate(new ChiselAnnotation { + override def toFirrtl: Annotation = DummyAnno(d.toTarget, id) + }) + private def markAbs(d: Data, id: Int) = annotate(new ChiselAnnotation { + override def toFirrtl: Annotation = DummyAnno(d.toAbsoluteTarget, id) + }) +} + +class DataViewTargetSpec extends ChiselFlatSpec { + import DataViewTargetSpec._ + private val checks: Seq[Data => String] = Seq( + _.toTarget.toString, + _.toAbsoluteTarget.toString, + _.instanceName, + _.pathName, + _.parentPathName, + _.parentModName, + ) + + // Check helpers + private def checkAll(impl: Data, refs: String*): Unit = { + refs.size should be (checks.size) + for ((check, value) <- checks.zip(refs)) { + check(impl) should be (value) + } + } + private def checkSameAs(impl: Data, refs: Data*): Unit = + for (ref <- refs) { + checkAll(impl, checks.map(_(ref)):_*) + } + + behavior of "DataView Naming" + + it should "support views of Elements" in { + class MyChild extends Module { + val out = IO(Output(UInt(8.W))) + val insideView = out.viewAs[UInt] + out := 0.U + } + class MyParent extends Module { + val out = IO(Output(UInt(8.W))) + val inst = Module(new MyChild) + out := inst.out + } + val m = elaborateAndGetModule(new MyParent) + val outsideView = m.inst.out.viewAs[UInt] + checkSameAs(m.inst.out, m.inst.insideView, outsideView) + } + + it should "support 1:1 mappings of Aggregates and their children" in { + class MyBundle extends Bundle { + val foo = UInt(8.W) + val bars = Vec(2, UInt(8.W)) + } + implicit val dv = DataView[MyBundle, Vec[UInt]](_ => Vec(3, UInt(8.W)), _.foo -> _(0), _.bars(0) -> _(1), _.bars(1) -> _(2)) + class MyChild extends Module { + val out = IO(Output(new MyBundle)) + val outView = out.viewAs[Vec[UInt]] // Note different type + val outFooView = out.foo.viewAs[UInt] + val outBarsView = out.bars.viewAs[Vec[UInt]] + val outBars0View = out.bars(0).viewAs[UInt] + out := 0.U.asTypeOf(new MyBundle) + } + class MyParent extends Module { + val out = IO(Output(new MyBundle)) + val inst = Module(new MyChild) + out := inst.out + } + val m = elaborateAndGetModule(new MyParent) + val outView = m.inst.out.viewAs[Vec[UInt]]// Note different type + val outFooView = m.inst.out.foo.viewAs[UInt] + val outBarsView = m.inst.out.bars.viewAs[Vec[UInt]] + val outBars0View = m.inst.out.bars(0).viewAs[UInt] + + checkSameAs(m.inst.out, m.inst.outView, outView) + checkSameAs(m.inst.out.foo, m.inst.outFooView, m.inst.outView(0), outFooView, outView(0)) + checkSameAs(m.inst.out.bars, m.inst.outBarsView, outBarsView) + checkSameAs(m.inst.out.bars(0), m.inst.outBars0View, outBars0View, m.inst.outView(1), outView(1), + m.inst.outBarsView(0), outBarsView(0)) + } + + // Ideally this would work 1:1 but that requires changing the binding + it should "support annotation renaming of Aggregate children of Aggregate views" in { + class MyBundle extends Bundle { + val foo = Vec(2, UInt(8.W)) + } + class MyChild extends Module { + val out = IO(Output(new MyBundle)) + val outView = out.viewAs[MyBundle] + mark(out.foo, 0) + mark(outView.foo, 1) + markAbs(out.foo, 2) + markAbs(outView, 3) + out := 0.U.asTypeOf(new MyBundle) + } + class MyParent extends Module { + val out = IO(Output(new MyBundle)) + val inst = Module(new MyChild) + out := inst.out + } + val (_, annos) = getFirrtlAndAnnos(new MyParent) + val pairs = annos.collect { case DummyAnno(t, idx) => (idx, t.toString) }.sortBy(_._1) + val expected = Seq( + 0 -> "~MyParent|MyChild>out.foo", + // The child of the view that was itself an Aggregate got split because 1:1 is lacking here + 1 -> "~MyParent|MyChild>out.foo[0]", + 1 -> "~MyParent|MyChild>out.foo[1]", + 2 -> "~MyParent|MyParent/inst:MyChild>out.foo", + 3 -> "~MyParent|MyParent/inst:MyChild>out" + ) + pairs should equal (expected) + } + + it should "support annotating views that cannot be mapped to a single ReferenceTarget" in { + import HWTuple._ + class MyBundle extends Bundle { + val a, b = Input(UInt(8.W)) + val c, d = Output(UInt(8.W)) + } + // Note that each use of a Tuple as Data causes an implicit conversion creating a View + class MyChild extends Module { + val io = IO(new MyBundle) + (io.c, io.d) := (io.a, io.b) + // The type annotations create the views via the implicit conversion + val view1: Data = (io.a, io.b) + val view2: Data = (io.c, io.d) + mark(view1, 0) + mark(view2, 1) + markAbs(view1, 2) + markAbs(view2, 3) + mark((io.b, io.d), 4) // Mix it up for fun + } + class MyParent extends Module { + val io = IO(new MyBundle) + val inst = Module(new MyChild) + io <> inst.io + } + val (_, annos) = getFirrtlAndAnnos(new MyParent) + val pairs = annos.collect { case DummyAnno(t, idx) => (idx, t.toString) }.sorted + val expected = Seq( + 0 -> "~MyParent|MyChild>io.a", + 0 -> "~MyParent|MyChild>io.b", + 1 -> "~MyParent|MyChild>io.c", + 1 -> "~MyParent|MyChild>io.d", + 2 -> "~MyParent|MyParent/inst:MyChild>io.a", + 2 -> "~MyParent|MyParent/inst:MyChild>io.b", + 3 -> "~MyParent|MyParent/inst:MyChild>io.c", + 3 -> "~MyParent|MyParent/inst:MyChild>io.d", + 4 -> "~MyParent|MyChild>io.b", + 4 -> "~MyParent|MyChild>io.d", + ) + pairs should equal (expected) + } +} diff --git a/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala b/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala new file mode 100644 index 00000000..78986517 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala @@ -0,0 +1,91 @@ +// See LICENSE for license details. + +package chiselTests.experimental + +import chisel3._ +import chisel3.experimental.{BaseModule, ExtModule} +import chisel3.experimental.dataview.DataProduct +import chiselTests.ChiselFlatSpec + +object ModuleDataProductSpec { + class MyBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + } + trait MyIntf extends BaseModule { + val in = IO(Input(new MyBundle)) + val out = IO(Output(new MyBundle)) + } + class Passthrough extends RawModule { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := in + } + class MyUserModule extends Module with MyIntf { + val inst = Module(new Passthrough) + inst.in := in.foo + val r = RegNext(in) + out := r + } + + class MyExtModule extends ExtModule with MyIntf + class MyExtModuleWrapper extends RawModule with MyIntf { + val inst = Module(new MyExtModule) + inst.in := in + out := inst.out + } +} + +class ModuleDataProductSpec extends ChiselFlatSpec { + import ModuleDataProductSpec._ + + behavior of "DataProduct" + + it should "work for UserModules (recursively)" in { + val m = elaborateAndGetModule(new MyUserModule) + val expected = Seq( + m.clock -> "m.clock", + m.reset -> "m.reset", + m.in -> "m.in", + m.in.foo -> "m.in.foo", + m.in.bar -> "m.in.bar", + m.out -> "m.out", + m.out.foo -> "m.out.foo", + m.out.bar -> "m.out.bar", + m.r -> "m.r", + m.r.foo -> "m.r.foo", + m.r.bar -> "m.r.bar", + m.inst.in -> "m.inst.in", + m.inst.out -> "m.inst.out" + ) + + val impl = implicitly[DataProduct[MyUserModule]] + val set = impl.dataSet(m) + for ((d, _) <- expected) { + set(d) should be (true) + } + val it = impl.dataIterator(m, "m") + it.toList should contain theSameElementsAs (expected) + } + + it should "work for (wrapped) ExtModules" in { + val m = elaborateAndGetModule(new MyExtModuleWrapper).inst + val expected = Seq( + m.in -> "m.in", + m.in.foo -> "m.in.foo", + m.in.bar -> "m.in.bar", + m.out -> "m.out", + m.out.foo -> "m.out.foo", + m.out.bar -> "m.out.bar" + ) + + val impl = implicitly[DataProduct[MyExtModule]] + val set = impl.dataSet(m) + for ((d, _) <- expected) { + set(d) should be (true) + } + val it = impl.dataIterator(m, "m") + it.toList should contain theSameElementsAs (expected) + } + +} diff --git a/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala b/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala index 35e354a6..99c0f7c0 100644 --- a/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala +++ b/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala @@ -4,6 +4,7 @@ package chiselTests.stage import firrtl.options.Viewer.view +import firrtl.RenameMap import chisel3.stage._ import chisel3.internal.firrtl.Circuit @@ -15,7 +16,7 @@ class ChiselOptionsViewSpec extends AnyFlatSpec with Matchers { behavior of ChiselOptionsView.getClass.getName it should "construct a view from an AnnotationSeq" in { - val bar = Circuit("bar", Seq.empty, Seq.empty) + val bar = Circuit("bar", Seq.empty, Seq.empty, RenameMap()) val annotations = Seq( NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation, -- cgit v1.2.3 From f50ce19406e45982390162777fb62c8563c962c7 Mon Sep 17 00:00:00 2001 From: anniej-sifive Date: Mon, 23 Aug 2021 14:37:09 -0700 Subject: Add multiple dimensions to VecInit fill and iterate (#2065) Co-authored-by: Jack Koenig --- src/test/scala/chiselTests/Vec.scala | 150 ++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala index f3160c2e..97aea909 100644 --- a/src/test/scala/chiselTests/Vec.scala +++ b/src/test/scala/chiselTests/Vec.scala @@ -9,6 +9,7 @@ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.util._ import org.scalacheck.Shrink +import scala.annotation.tailrec class LitTesterMod(vecSize: Int) extends Module { val io = IO(new Bundle { @@ -114,6 +115,121 @@ class FillTester(n: Int, value: Int) extends BasicTester { stop() } +object VecMultiDimTester { + + @tailrec + private def assert2DIsCorrect(n: Int, arr: Vec[Vec[UInt]], compArr: Seq[Seq[Int]]): Unit = { + val compareRow = arr(n) zip compArr(n) + compareRow.foreach (x => assert(x._1 === x._2.U)) + if (n != 0) assert2DIsCorrect(n-1, arr, compArr) + } + + @tailrec + private def assert3DIsCorrect(n: Int, m: Int, arr: Vec[Vec[Vec[UInt]]], compArr: Seq[Seq[Seq[Int]]]): Unit = { + assert2DIsCorrect(m-1, arr(n), compArr(n)) + if (n != 0) assert3DIsCorrect(n-1, m, arr, compArr) + } + + class TabulateTester2D(n: Int, m: Int) extends BasicTester { + def gen(x: Int, y: Int): UInt = (x+y).asUInt + def genCompVec(x: Int, y:Int): Int = x+y + val vec = VecInit.tabulate(n, m){ gen } + val compArr = Seq.tabulate(n,m){ genCompVec } + + assert2DIsCorrect(n-1, vec, compArr) + stop() + } + + class TabulateTester3D(n: Int, m: Int, p: Int) extends BasicTester { + def gen(x: Int, y: Int, z: Int): UInt = (x+y+z).asUInt + def genCompVec(x: Int, y:Int, z: Int): Int = x+y+z + val vec = VecInit.tabulate(n, m, p){ gen } + val compArr = Seq.tabulate(n, m, p){ genCompVec } + + assert3DIsCorrect(n-1, m, vec, compArr) + stop() + } + + class Fill2DTester(n: Int, m: Int, value: Int) extends BasicTester { + val u = VecInit.fill(n,m)(value.U) + val compareArr = Seq.fill(n,m)(value) + + assert2DIsCorrect(n-1, u, compareArr) + stop() + } + + class Fill3DTester(n: Int, m: Int, p: Int, value: Int) extends BasicTester { + val u = VecInit.fill(n,m,p)(value.U) + val compareArr = Seq.fill(n,m,p)(value) + + assert3DIsCorrect(n-1, m, u, compareArr) + stop() + } + + class BidirectionalTester2DFill(n: Int, m: Int) extends BasicTester { + val mod = Module(new PassthroughModule) + val vec2D = VecInit.fill(n, m)(mod.io) + for { + vec1D <- vec2D + module <- vec1D + } yield { + module <> Module(new PassthroughModuleTester).io + } + stop() + } + + class BidirectionalTester3DFill(n: Int, m: Int, p: Int) extends BasicTester { + val mod = Module(new PassthroughModule) + val vec3D = VecInit.fill(n, m, p)(mod.io) + + for { + vec2D <- vec3D + vec1D <- vec2D + module <- vec1D + } yield { + module <> (Module(new PassthroughModuleTester).io) + } + stop() + } + + class TabulateModuleTester(value: Int) extends Module { + val io = IO(Flipped(new PassthroughModuleIO)) + // This drives the input of a PassthroughModule + io.in := value.U + } + + class BidirectionalTester2DTabulate(n: Int, m: Int) extends BasicTester { + val vec2D = VecInit.tabulate(n, m) { (x, y) => Module(new TabulateModuleTester(x + y + 1)).io} + + for { + x <- 0 until n + y <- 0 until m + } yield { + val value = x + y + 1 + val receiveMod = Module(new PassthroughModule).io + vec2D(x)(y) <> receiveMod + assert(receiveMod.out === value.U) + } + stop() + } + + class BidirectionalTester3DTabulate(n: Int, m: Int, p: Int) extends BasicTester { + val vec3D = VecInit.tabulate(n, m, p) { (x, y, z) => Module(new TabulateModuleTester(x + y + z + 1)).io } + + for { + x <- 0 until n + y <- 0 until m + z <- 0 until p + } yield { + val value = x + y + z + 1 + val receiveMod = Module(new PassthroughModule).io + vec3D(x)(y)(z) <> receiveMod + assert(receiveMod.out === value.U) + } + stop() + } +} + class IterateTester(start: Int, len: Int)(f: UInt => UInt) extends BasicTester { val controlVec = VecInit(Seq.iterate(start.U, len)(f)) val testVec = VecInit.iterate(start.U, len)(f) @@ -178,7 +294,7 @@ class PassthroughModuleTester extends Module { } class ModuleIODynamicIndexTester(n: Int) extends BasicTester { - val duts = VecInit(Seq.fill(n)(Module(new PassthroughModule).io)) + val duts = VecInit.fill(n)(Module(new PassthroughModule).io) val tester = Module(new PassthroughModuleTester) val (cycle, done) = Counter(true.B, n) @@ -239,10 +355,42 @@ class VecSpec extends ChiselPropSpec with Utils { forAll(smallPosInts) { (n: Int) => assertTesterPasses{ new TabulateTester(n) } } } + property("VecInit should tabulate 2D vec correctly") { + forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses { new VecMultiDimTester.TabulateTester2D(n, m) } } + } + + property("VecInit should tabulate 3D vec correctly") { + forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.TabulateTester3D(n, m, p) } } + } + property("VecInit should fill correctly") { forAll(smallPosInts, Gen.choose(0, 50)) { (n: Int, value: Int) => assertTesterPasses{ new FillTester(n, value) } } } + property("VecInit should fill 2D vec correctly") { + forAll(smallPosInts, smallPosInts, Gen.choose(0, 50)) { (n: Int, m: Int, value: Int) => assertTesterPasses{ new VecMultiDimTester.Fill2DTester(n, m, value) } } + } + + property("VecInit should fill 3D vec correctly") { + forAll(smallPosInts, smallPosInts, smallPosInts, Gen.choose(0, 50)) { (n: Int, m: Int, p: Int, value: Int) => assertTesterPasses{ new VecMultiDimTester.Fill3DTester(n, m, p, value) } } + } + + property("VecInit should support 2D fill bidirectional wire connection") { + forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester2DFill(n, m) }} + } + + property("VecInit should support 3D fill bidirectional wire connection") { + forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester3DFill(n, m, p) }} + } + + property("VecInit should support 2D tabulate bidirectional wire connection") { + forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester2DTabulate(n, m) }} + } + + property("VecInit should support 3D tabulate bidirectional wire connection") { + forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester3DTabulate(n, m, p) }} + } + property("VecInit should iterate correctly") { forAll(Gen.choose(1, 10), smallPosInts) { (start: Int, len: Int) => assertTesterPasses{ new IterateTester(start, len)(x => x + 50.U)}} } -- cgit v1.2.3 From 73bd4ee6b9b510725b692c33e075362a19512d2c Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Fri, 20 Aug 2021 11:30:27 -0700 Subject: Remove chisel3's own firrtl Emitter, use firrtl Serializer This will be slightly slower as it involves converting from Chisel modules to FIRRTL modules before turning them into Strings. This cost is somewhat mitigated by doing that conversion lazily such that we never materialize the entire firrtl Circuit in memory, only 1 module at a time. --- src/test/scala/chiselTests/PrintableSpec.scala | 7 +++---- src/test/scala/chiselTests/VecLiteralSpec.scala | 8 ++++---- .../experimental/verification/VerificationSpec.scala | 10 +++++----- 3 files changed, 12 insertions(+), 13 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/PrintableSpec.scala b/src/test/scala/chiselTests/PrintableSpec.scala index 0325d3bc..25b54966 100644 --- a/src/test/scala/chiselTests/PrintableSpec.scala +++ b/src/test/scala/chiselTests/PrintableSpec.scala @@ -150,7 +150,6 @@ class PrintableSpec extends AnyFlatSpec with Matchers { printf(p"${FullName(myInst.io.fizz)}") } val firrtl = ChiselStage.emitChirrtl(new MyModule) - println(firrtl) getPrintfs(firrtl) match { case Seq(Printf("foo", Seq()), Printf("myWire.foo", Seq()), @@ -256,8 +255,8 @@ class PrintableSpec extends AnyFlatSpec with Matchers { val firLines = scala.io.Source.fromFile(firFile).getLines.toList // check that verification components have expected names - exactly(1, firLines) should include ("""printf(clock, UInt<1>(1), "hello AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar): howdy""") - exactly(1, firLines) should include ("""printf(clock, UInt<1>(1), "goodbye AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar): SIM""") - exactly(1, firLines) should include ("""printf(clock, UInt<1>(1), "adieu AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar): farewell""") + exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "hello AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : howdy""") + exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "goodbye AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : SIM""") + exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "adieu AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : farewell""") } } diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala index d11289e1..d91cd2f4 100644 --- a/src/test/scala/chiselTests/VecLiteralSpec.scala +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -461,10 +461,10 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { "vec literals can contain bundles" in { val chirrtl = (new chisel3.stage.ChiselStage).emitChirrtl(new VecExample, args = Array("--full-stacktrace")) - chirrtl should include("""out[0].bar <= UInt<5>("h016")""") - chirrtl should include("""out[0].foo <= UInt<6>("h02a")""") - chirrtl should include("""out[1].bar <= UInt<2>("h03")""") - chirrtl should include("""out[1].foo <= UInt<3>("h07")""") + chirrtl should include("""out[0].bar <= UInt<5>("h16")""") + chirrtl should include("""out[0].foo <= UInt<6>("h2a")""") + chirrtl should include("""out[1].bar <= UInt<2>("h3")""") + chirrtl should include("""out[1].foo <= UInt<3>("h7")""") } diff --git a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala index a1fc2a1d..1e080739 100644 --- a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala +++ b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala @@ -104,9 +104,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>(1), \"\") : cov") - exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(1), \"\") : assm") - exactly(1, firLines) should include ("assert(clock, _T_6, UInt<1>(1), \"\") : asst") + 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_6, UInt<1>(\"h1\"), \"\") : asst") } property("annotation of verification constructs with suggested name should work") { @@ -148,7 +148,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>(1), \"\") : hello") - exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(1), \"\") : howdy") + exactly(1, firLines) should include ("assert(clock, _T, UInt<1>(\"h1\"), \"\") : hello") + exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(\"h1\"), \"\") : howdy") } } -- cgit v1.2.3 From 3840fec3d918f23df07a18311136ac6a1bc365e1 Mon Sep 17 00:00:00 2001 From: Kevin Laeufer Date: Wed, 25 Aug 2021 12:38:56 -0700 Subject: replace custom model checker with chiseltest formal verify command (#2075) * replace custom model checker with chiseltest formal verify command * integration-tests can make use of chiseltest This is a compromise solution to avoid issues with binary compatibility breaking changes in chisel3. * ci: move integration tests into separate job * run integration tests only for one scala version * ci: install espresso for integration tests * Update build.sbt Co-authored-by: Jack Koenig Co-authored-by: Jack Koenig --- .../scala/chiselTests/SMTModelCheckingSpec.scala | 104 -------- .../util/experimental/DecoderSpec.scala | 61 ----- .../util/experimental/minimizer/EspressoSpec.scala | 9 - .../experimental/minimizer/MinimizerSpec.scala | 277 --------------------- .../util/experimental/minimizer/QMCSpec.scala | 9 - 5 files changed, 460 deletions(-) delete mode 100644 src/test/scala/chiselTests/SMTModelCheckingSpec.scala delete mode 100644 src/test/scala/chiselTests/util/experimental/DecoderSpec.scala delete mode 100644 src/test/scala/chiselTests/util/experimental/minimizer/EspressoSpec.scala delete mode 100644 src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala delete mode 100644 src/test/scala/chiselTests/util/experimental/minimizer/QMCSpec.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/SMTModelCheckingSpec.scala b/src/test/scala/chiselTests/SMTModelCheckingSpec.scala deleted file mode 100644 index 0a752b10..00000000 --- a/src/test/scala/chiselTests/SMTModelCheckingSpec.scala +++ /dev/null @@ -1,104 +0,0 @@ -package chiselTests - -import chisel3.Module -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} -import firrtl.annotations.Annotation -import firrtl.options.{OutputAnnotationFileAnnotation, TargetDirAnnotation} -import firrtl.stage.OutputFileAnnotation -import firrtl.util.BackendCompilationUtilities.timeStamp -import logger.{LazyLogging, LogLevel, LogLevelAnnotation} -import org.scalatest.flatspec.AnyFlatSpec -import os._ -import scala.util.Properties - -/** [[SMTModelCheckingSpec]] use z3 and [[firrtl.backends.experimental.smt]] library - * to solve `assert/assume` in [[chisel3.experimental.verification]], - * It is a copy&paste version from `firrtl.backends.experimental.smt.end2end.EndToEndSMTBaseSpec` from firrtl - * Useful to check combinational logic and some small test. - */ -abstract class SMTModelCheckingSpec extends AnyFlatSpec { - def success = MCSuccess - - def fail(k: Int) = MCFail(k) - - def test(dut: () => Module, name: String, expected: MCResult, kmax: Int = 0, annos: Seq[Annotation] = Seq()): Unit = { - expected match { - case MCFail(k) => - assert(kmax >= k, s"Please set a kmax that includes the expected failing step! ($kmax < $expected)") - case _ => - } - // @todo rewrite BackendCompilationUtilities - val testBaseDir = os.pwd / "test_run_dir" / name - os.makeDir.all(testBaseDir) - val testDir = os.temp.dir(testBaseDir, timeStamp, deleteOnExit = false) - val res = (new ChiselStage).execute( - Array("-E", "experimental-smt2"), - Seq( - LogLevelAnnotation(LogLevel.Error), // silence warnings for tests - ChiselGeneratorAnnotation(dut), - TargetDirAnnotation(testDir.toString) - ) ++ annos - ) - val top = res.collectFirst{case OutputAnnotationFileAnnotation(top) => top}.get - assert(res.collectFirst { case _: OutputFileAnnotation => true }.isDefined) - val r = Z3ModelChecker.bmc(testDir, top, kmax) - assert(r == expected) - } -} - -private object Z3ModelChecker extends LazyLogging { - def bmc(testDir: Path, main: String, kmax: Int): MCResult = { - assert(kmax >= 0 && kmax < 50, "Trying to keep kmax in a reasonable range.") - Seq.tabulate(kmax + 1) { k => - val stepFile = testDir / s"${main}_step$k.smt2" - os.copy(testDir / s"$main.smt2", stepFile) - os.write.append(stepFile, - s"""${step(main, k)} - |(check-sat) - |""".stripMargin) - val success = executeStep(stepFile) - if (!success) return MCFail(k) - } - MCSuccess - } - - private def executeStep(path: Path): Boolean = { - val (out, ret) = executeCmd(path.toString) - assert(ret == 0, s"expected success (0), not $ret: `$out`\nz3 ${path.toString}") - assert(out == "sat" + Properties.lineSeparator || out == "unsat" + Properties.lineSeparator, s"Unexpected output: $out") - out == "unsat" + Properties.lineSeparator - } - - private def executeCmd(cmd: String): (String, Int) = { - val process = os.proc("z3", cmd).call(stderr = ProcessOutput.Readlines(logger.warn(_))) - (process.out.chunks.mkString, process.exitCode) - } - - private def step(main: String, k: Int): String = { - // define all states - (0 to k).map(ii => s"(declare-fun s$ii () $main$StateTpe)") ++ - // assert that init holds in state 0 - List(s"(assert ($main$Init s0))") ++ - // assert transition relation - (0 until k).map(ii => s"(assert ($main$Transition s$ii s${ii + 1}))") ++ - // assert that assumptions hold in all states - (0 to k).map(ii => s"(assert ($main$Assumes s$ii))") ++ - // assert that assertions hold for all but last state - (0 until k).map(ii => s"(assert ($main$Asserts s$ii))") ++ - // check to see if we can violate the assertions in the last state - List(s"(assert (not ($main$Asserts s$k)))") - }.mkString("\n") - - // the following suffixes have to match the ones in [[SMTTransitionSystemEncoder]] - private val Transition = "_t" - private val Init = "_i" - private val Asserts = "_a" - private val Assumes = "_u" - private val StateTpe = "_s" -} -sealed trait MCResult - -case object MCSuccess extends MCResult - -case class MCFail(k: Int) extends MCResult - diff --git a/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala b/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala deleted file mode 100644 index 3c9d490d..00000000 --- a/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chiselTests.util.experimental - -import chisel3.util.experimental.decode.{DecodeTableAnnotation, Minimizer, QMCMinimizer, TruthTable} -import chiselTests.SMTModelCheckingSpec -import chiselTests.util.experimental.minimizer.DecodeTestModule -import firrtl.annotations.ReferenceTarget - -class DecoderSpec extends SMTModelCheckingSpec { - val xor = TruthTable( - """10->1 - |01->1 - | 0""".stripMargin) - - def minimizer: Minimizer = QMCMinimizer - - "decoder" should "pass without DecodeTableAnnotation" in { - test( - () => new DecodeTestModule(minimizer, table = xor), - s"${minimizer.getClass.getSimpleName}.noAnno", - success - ) - } - - "decoder" should "fail with a incorrect DecodeTableAnnotation" in { - test( - () => new DecodeTestModule(minimizer, table = xor), - s"${minimizer.getClass.getSimpleName}.incorrectAnno", - fail(0), - annos = Seq( - DecodeTableAnnotation(ReferenceTarget("", "", Nil, "", Nil), - """10->1 - |01->1 - | 0""".stripMargin, - """10->1 - | 0""".stripMargin - ) - ) - ) - } - - "decoder" should "success with a correct DecodeTableAnnotation" in { - test( - () => new DecodeTestModule(minimizer, table = xor), - s"${minimizer.getClass.getSimpleName}.correctAnno", - success, - annos = Seq( - DecodeTableAnnotation(ReferenceTarget("", "", Nil, "", Nil), - """10->1 - |01->1 - | 0""".stripMargin, - QMCMinimizer.minimize(TruthTable( - """10->1 - |01->1 - | 0""".stripMargin)).toString - ) - ) - ) - } -} diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/EspressoSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/EspressoSpec.scala deleted file mode 100644 index f3270cae..00000000 --- a/src/test/scala/chiselTests/util/experimental/minimizer/EspressoSpec.scala +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chiselTests.util.experimental.minimizer -import chisel3.util.experimental.decode.EspressoMinimizer -import chisel3.util.experimental.decode.Minimizer - -class EspressoSpec extends MinimizerSpec { - override def minimizer: Minimizer = EspressoMinimizer -} diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala deleted file mode 100644 index 5e3be9a6..00000000 --- a/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala +++ /dev/null @@ -1,277 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chiselTests.util.experimental.minimizer - -import chisel3._ -import chisel3.util._ -import chisel3.util.experimental.decode._ -import chisel3.util.pla -import chiselTests.SMTModelCheckingSpec - -class DecodeTestModule(minimizer: Minimizer, table: TruthTable) extends Module { - val i = IO(Input(UInt(table.table.head._1.getWidth.W))) - val (unminimizedI, unminimizedO) = pla(table.table.toSeq) - unminimizedI := i - val minimizedO: UInt = decoder(minimizer, i, table) - - chisel3.experimental.verification.assert( - // for each instruction, if input matches, output should match, not no matched, fallback to default - (table.table.map { case (key, value) => (i === key) && (minimizedO === value) } ++ - Seq(table.table.keys.map(i =/= _).reduce(_ && _) && minimizedO === table.default)).reduce(_ || _) - ) -} - -trait MinimizerSpec extends SMTModelCheckingSpec { - def minimizer: Minimizer - - def minimizerTest(testcase: TruthTable, caseName: String) = { - test( - () => new DecodeTestModule(minimizer, table = testcase), - s"${minimizer.getClass.getSimpleName}.$caseName", - success - ) - } - - // Term that being commented out is the result of which is same as default, - // making optimization opportunities to decoder algorithms - - "case0" should "pass" in { - minimizerTest(TruthTable( - Map( - // BitPat("b000") -> BitPat("b0"), - BitPat("b001") -> BitPat("b?"), - BitPat("b010") -> BitPat("b?"), - // BitPat("b011") -> BitPat("b0"), - BitPat("b100") -> BitPat("b1"), - BitPat("b101") -> BitPat("b1"), - // BitPat("b110") -> BitPat("b0"), - BitPat("b111") -> BitPat("b1") - ), - BitPat("b0") - ), "case0") - } - - "case1" should "pass" in { - minimizerTest(TruthTable( - Map( - BitPat("b000") -> BitPat("b0"), - BitPat("b001") -> BitPat("b?"), - BitPat("b010") -> BitPat("b?"), - BitPat("b011") -> BitPat("b0"), - // BitPat("b100") -> BitPat("b1"), - // BitPat("b101") -> BitPat("b1"), - BitPat("b110") -> BitPat("b0"), - // BitPat("b111") -> BitPat("b1") - ), - BitPat("b1") - ), "case1") - } - - "caseX" should "pass" in { - minimizerTest(TruthTable( - Map( - BitPat("b000") -> BitPat("b0"), - // BitPat("b001") -> BitPat("b?"), - // BitPat("b010") -> BitPat("b?"), - BitPat("b011") -> BitPat("b0"), - BitPat("b100") -> BitPat("b1"), - BitPat("b101") -> BitPat("b1"), - BitPat("b110") -> BitPat("b0"), - BitPat("b111") -> BitPat("b1") - ), - BitPat("b?") - ), "caseX") - } - - "caseMultiDefault" should "pass" in { - minimizerTest(TruthTable( - Map( - BitPat("b000") -> BitPat("b0100"), - BitPat("b001") -> BitPat("b?111"), - BitPat("b010") -> BitPat("b?000"), - BitPat("b011") -> BitPat("b0101"), - BitPat("b111") -> BitPat("b1101") - ), - BitPat("b?100") - ), "caseMultiDefault") - } - - "case7SegDecoder" should "pass" in { - minimizerTest(TruthTable( - Map( - BitPat("b0000") -> BitPat("b111111001"), - BitPat("b0001") -> BitPat("b011000001"), - BitPat("b0010") -> BitPat("b110110101"), - BitPat("b0011") -> BitPat("b111100101"), - BitPat("b0100") -> BitPat("b011001101"), - BitPat("b0101") -> BitPat("b101101101"), - BitPat("b0110") -> BitPat("b101111101"), - BitPat("b0111") -> BitPat("b111000001"), - BitPat("b1000") -> BitPat("b111111101"), - BitPat("b1001") -> BitPat("b111101101"), - ), - BitPat("b???????10") - ), "case7SegDecoder") - } - - // A simple RV32I decode table example - "caseRV32I" should "pass" in { - val BEQ = "?????????????????000?????1100011" - val BNE = "?????????????????001?????1100011" - val BLT = "?????????????????100?????1100011" - val BGE = "?????????????????101?????1100011" - val BLTU = "?????????????????110?????1100011" - val BGEU = "?????????????????111?????1100011" - val JALR = "?????????????????000?????1100111" - val JAL = "?????????????????????????1101111" - val LUI = "?????????????????????????0110111" - val AUIPC = "?????????????????????????0010111" - val ADDI = "?????????????????000?????0010011" - val SLTI = "?????????????????010?????0010011" - val SLTIU = "?????????????????011?????0010011" - val XORI = "?????????????????100?????0010011" - val ORI = "?????????????????110?????0010011" - val ANDI = "?????????????????111?????0010011" - val ADD = "0000000??????????000?????0110011" - val SUB = "0100000??????????000?????0110011" - val SLL = "0000000??????????001?????0110011" - val SLT = "0000000??????????010?????0110011" - val SLTU = "0000000??????????011?????0110011" - val XOR = "0000000??????????100?????0110011" - val SRL = "0000000??????????101?????0110011" - val SRA = "0100000??????????101?????0110011" - val OR = "0000000??????????110?????0110011" - val AND = "0000000??????????111?????0110011" - val LB = "?????????????????000?????0000011" - val LH = "?????????????????001?????0000011" - val LW = "?????????????????010?????0000011" - val LBU = "?????????????????100?????0000011" - val LHU = "?????????????????101?????0000011" - val SB = "?????????????????000?????0100011" - val SH = "?????????????????001?????0100011" - val SW = "?????????????????010?????0100011" - val FENCE = "?????????????????000?????0001111" - val MRET = "00110000001000000000000001110011" - val WFI = "00010000010100000000000001110011" - val CEASE = "00110000010100000000000001110011" - val CSRRW = "?????????????????001?????1110011" - val CSRRS = "?????????????????010?????1110011" - val CSRRC = "?????????????????011?????1110011" - val CSRRWI = "?????????????????101?????1110011" - val CSRRSI = "?????????????????110?????1110011" - val CSRRCI = "?????????????????111?????1110011" - val SCALL = "00000000000000000000000001110011" - val SBREAK = "00000000000100000000000001110011" - val SLLI_RV32 = "0000000??????????001?????0010011" - val SRLI_RV32 = "0000000??????????101?????0010011" - val SRAI_RV32 = "0100000??????????101?????0010011" - - val A1_X = "??" - val A1_ZERO = "00" - val A1_RS1 = "01" - val A1_PC = "10" - - val IMM_X = "???" - val IMM_S = "000" - val IMM_SB = "001" - val IMM_U = "010" - val IMM_UJ = "011" - val IMM_I = "100" - val IMM_Z = "101" - - val A2_X = "??" - val A2_ZERO = "00" - val A2_SIZE = "01" - val A2_RS2 = "10" - val A2_IMM = "11" - - val X = "?" - val N = "0" - val Y = "1" - - val DW_X = X - val DW_XPR = Y - - val M_X = "?????" - val M_XRD = "00000" - val M_XWR = "00001" - - val CSR_X = "???" - val CSR_N = "000" - val CSR_I = "100" - val CSR_W = "101" - val CSR_S = "110" - val CSR_C = "111" - - val FN_X = "????" - val FN_ADD = "0000" - val FN_SL = "0001" - val FN_SEQ = "0010" - val FN_SNE = "0011" - val FN_XOR = "0100" - val FN_SR = "0101" - val FN_OR = "0110" - val FN_AND = "0111" - val FN_SUB = "1010" - val FN_SRA = "1011" - val FN_SLT = "1100" - val FN_SGE = "1101" - val FN_SLTU = "1110" - val FN_SGEU = "1111" - - minimizerTest(TruthTable( - Map( - BNE -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SNE, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - BEQ -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SEQ, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - BLT -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SLT, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - BLTU -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SLTU, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - BGE -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SGE, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - BGEU -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SGEU, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - JAL -> Seq(Y, N, N, N, Y, N, N, N, N, A2_SIZE, A1_PC, IMM_UJ, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - JALR -> Seq(Y, N, N, N, N, Y, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - AUIPC -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_PC, IMM_U, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - LB -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - LH -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - LW -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - LBU -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - LHU -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, Y, M_XRD, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SB -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_IMM, A1_RS1, IMM_S, DW_XPR, FN_ADD, Y, M_XWR, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - SH -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_IMM, A1_RS1, IMM_S, DW_XPR, FN_ADD, Y, M_XWR, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - SW -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_IMM, A1_RS1, IMM_S, DW_XPR, FN_ADD, Y, M_XWR, N, N, N, N, N, N, N, CSR_N, N, N, N, N), - LUI -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_ZERO, IMM_U, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - ADDI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SLTI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SLT, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SLTIU -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SLTU, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - ANDI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_AND, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - ORI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_OR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - XORI -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_XOR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - ADD -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SUB -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SUB, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SLT -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SLT, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SLTU -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SLTU, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - AND -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_AND, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - OR -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_OR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - XOR -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_XOR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SLL -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SL, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SRL -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SRA -> Seq(Y, N, N, N, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_X, DW_XPR, FN_SRA, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - FENCE -> Seq(Y, N, N, N, N, N, N, N, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_N, N, Y, N, N), - SCALL -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), - SBREAK -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), - MRET -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), - WFI -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), - CEASE -> Seq(Y, N, N, N, N, N, N, X, N, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, N, N, N, N, N, N, N, CSR_I, N, N, N, N), - CSRRW -> Seq(Y, N, N, N, N, N, N, Y, N, A2_ZERO, A1_RS1, IMM_X, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_W, N, N, N, N), - CSRRS -> Seq(Y, N, N, N, N, N, N, Y, N, A2_ZERO, A1_RS1, IMM_X, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_S, N, N, N, N), - CSRRC -> Seq(Y, N, N, N, N, N, N, Y, N, A2_ZERO, A1_RS1, IMM_X, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_C, N, N, N, N), - CSRRWI -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_ZERO, IMM_Z, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_W, N, N, N, N), - CSRRSI -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_ZERO, IMM_Z, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_S, N, N, N, N), - CSRRCI -> Seq(Y, N, N, N, N, N, N, N, N, A2_IMM, A1_ZERO, IMM_Z, DW_XPR, FN_ADD, N, M_X, N, N, N, N, N, N, Y, CSR_C, N, N, N, N), - SLLI_RV32 -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SL, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SRLI_RV32 -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SR, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - SRAI_RV32 -> Seq(Y, N, N, N, N, N, N, Y, N, A2_IMM, A1_RS1, IMM_I, DW_XPR, FN_SRA, N, M_X, N, N, N, N, N, N, Y, CSR_N, N, N, N, N), - ).map { case (k, v) => BitPat(s"b$k") -> BitPat(s"b${v.reduce(_ + _)}") }, - BitPat(s"b${Seq(N, X, X, X, X, X, X, X, X, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, X, X, X, X, X, X, X, CSR_X, X, X, X, X).reduce(_ + _)}") - ), "rv32i") - } -} diff --git a/src/test/scala/chiselTests/util/experimental/minimizer/QMCSpec.scala b/src/test/scala/chiselTests/util/experimental/minimizer/QMCSpec.scala deleted file mode 100644 index fc770202..00000000 --- a/src/test/scala/chiselTests/util/experimental/minimizer/QMCSpec.scala +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chiselTests.util.experimental.minimizer -import chisel3.util.experimental.decode.Minimizer -import chisel3.util.experimental.decode.QMCMinimizer - -class QMCSpec extends MinimizerSpec { - override def minimizer: Minimizer = QMCMinimizer -} -- cgit v1.2.3 From e74b978d5188d9cd28e3520912d858d228136e75 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Fri, 27 Aug 2021 04:37:34 +0800 Subject: add new APIs to BitPat (#2076) * add Y and N to BitPat. * add ## for BitPat. * add rawString API. * use rawString in decoder * add select and slice to BitPat.--- src/test/scala/chiselTests/util/BitPatSpec.scala | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/BitPatSpec.scala b/src/test/scala/chiselTests/util/BitPatSpec.scala index ca8dd85c..0c83493f 100644 --- a/src/test/scala/chiselTests/util/BitPatSpec.scala +++ b/src/test/scala/chiselTests/util/BitPatSpec.scala @@ -15,7 +15,30 @@ class BitPatSpec extends AnyFlatSpec with Matchers { BitPat("b" + testPattern).toString should be (s"BitPat($testPattern)") } + it should "convert a BitPat to raw form" in { + val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32 + BitPat("b" + testPattern).rawString should be(testPattern) + } + it should "not fail if BitPat width is 0" in { intercept[IllegalArgumentException]{BitPat("b")} } + + it should "contact BitPat via ##" in { + (BitPat.Y(4) ## BitPat.dontCare(3) ## BitPat.N(2)).toString should be (s"BitPat(1111???00)") + } + + it should "index and return new BitPat" in { + val b = BitPat("b1001???") + b(0) should be(BitPat.dontCare(1)) + b(6) should be(BitPat.Y()) + b(5) should be(BitPat.N()) + } + + it should "slice and return new BitPat" in { + val b = BitPat("b1001???") + b(2, 0) should be(BitPat("b???")) + b(4, 3) should be(BitPat("b01")) + b(6, 6) should be(BitPat("b1")) + } } -- cgit v1.2.3 From 7fb2c1ebc23ca07e5de6416a284e1be1b62a48ac Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Mon, 30 Aug 2021 18:56:33 -0700 Subject: Fix chisel3 <> for compatibility Bundles (Take 3) (#2093) Previous incomplete fixes in #2023 and #2031. The legality of a FIRRTL connection is determined by type and flow. Chisel does not have access to true flow information. Previous fix attempts tried to use ActualDirection as a stand-in for flow, but it is incorrect in many cases. This new approach checks the flows of the lvalue and rvalues in the connect and flips the connection if either the lvalue cannot be a sink or the rvalue cannot be a source.--- .../CompatibilityInteroperabilitySpec.scala | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index 28b8bc80..1795cc1f 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -332,5 +332,32 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { } } } + + "A unidirectional but flipped Bundle" should "bulk connect in import chisel3._ code correctly" in { + object Compat { + import Chisel._ + 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) + }) + override def cloneType = (new MyBundle(extraFlip)).asInstanceOf[this.type] + } + } + import chisel3._ + import Compat._ + 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)) + } } -- cgit v1.2.3 From 9fa8da227569455a77596355aeb114f9c164510a Mon Sep 17 00:00:00 2001 From: Adam Izraelevitz Date: Sun, 5 Sep 2021 12:11:32 -0700 Subject: Add Definition and Instance API (#2045) This introduces a new experimental API for module instantiation that disentagles elaborating the definition (or implementation) from instantiation of a given module. This solves Chisel's longstanding reliance on "Deduplication" for generating Verilog with multiple instances of the same module. The new API resides in package chisel3.experimental.hierarchy. Please see the hierarchy ScalaDoc, documentation, and tests for examples of use. Co-authored-by: Jack Koenig Co-authored-by: Megan Wachs Co-authored-by: Schuyler Eldridge --- src/test/scala/chiselTests/ChiselSpec.scala | 11 +- src/test/scala/chiselTests/aop/SelectSpec.scala | 31 +- .../scala/chiselTests/experimental/DataView.scala | 40 +- .../experimental/DataViewTargetSpec.scala | 4 +- .../experimental/hierarchy/Annotations.scala | 28 + .../experimental/hierarchy/DefinitionSpec.scala | 493 ++++++++++++++ .../experimental/hierarchy/Examples.scala | 186 ++++++ .../experimental/hierarchy/InstanceSpec.scala | 709 +++++++++++++++++++++ .../chiselTests/experimental/hierarchy/Utils.scala | 21 + 9 files changed, 1500 insertions(+), 23 deletions(-) create mode 100644 src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala create mode 100644 src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala create mode 100644 src/test/scala/chiselTests/experimental/hierarchy/Examples.scala create mode 100644 src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala create mode 100644 src/test/scala/chiselTests/experimental/hierarchy/Utils.scala (limited to 'src/test') diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 8e35273d..8647d903 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -16,6 +16,7 @@ import org.scalacheck._ import org.scalatest._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.funspec.AnyFunSpec import org.scalatest.propspec.AnyPropSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks @@ -104,13 +105,14 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { * @param t the generator for the module * @return The FIRRTL Circuit and Annotations _before_ FIRRTL compilation */ - def getFirrtlAndAnnos(t: => RawModule): (Circuit, Seq[Annotation]) = { + def getFirrtlAndAnnos(t: => RawModule, providedAnnotations: Seq[Annotation] = Nil): (Circuit, Seq[Annotation]) = { val args = Array( "--target-dir", createTestDirectory(this.getClass.getSimpleName).toString, - "--no-run-firrtl" + "--no-run-firrtl", + "--full-stacktrace" ) - val annos = (new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => t))) + val annos = (new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => t)) ++ providedAnnotations) val circuit = annos.collectFirst { case FirrtlCircuitAnnotation(c) => c }.getOrElse(fail("No FIRRTL Circuit found!!")) @@ -124,6 +126,9 @@ abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matche /** Spec base class for BDD-style testers. */ abstract class ChiselFreeSpec extends AnyFreeSpec with ChiselRunners with Matchers +/** Spec base class for BDD-style testers. */ +abstract class ChiselFunSpec extends AnyFunSpec with ChiselRunners with Matchers + /** Spec base class for property-based testers. */ abstract class ChiselPropSpec extends AnyPropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala index 46c62d67..e09e78c8 100644 --- a/src/test/scala/chiselTests/aop/SelectSpec.scala +++ b/src/test/scala/chiselTests/aop/SelectSpec.scala @@ -163,7 +163,7 @@ class SelectSpec extends ChiselFlatSpec { val out = IO(Output(UInt(8.W))) out := in } - class Top extends MultiIOModule { + class Top extends Module { val in = IO(Input(UInt(8.W))) val out = IO(Output(UInt(8.W))) val inst0 = Module(new Child) @@ -182,5 +182,34 @@ class SelectSpec extends ChiselFlatSpec { Select.instances(top) should equal (Seq(top.inst0)) } + "Using Definition/Instance with Injecting Aspects" should "throw an error" in { + import chisel3.experimental.CloneModuleAsRecord + import chisel3.experimental.hierarchy._ + @instantiable + class Child extends RawModule { + @public val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + out := in + } + class Top extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + val definition = Definition(new Child) + val inst0 = Instance(definition) + val inst1 = Instance(definition) + inst0.in := in + inst1.in := inst0.out + out := inst1.out + } + val top = ChiselGeneratorAnnotation(() => { + new Top() + }).elaborate + .collectFirst { case DesignAnnotation(design: Top) => design } + .get + intercept[Exception] { Select.collectDeep(top) { case x => x } } + intercept[Exception] { Select.getDeep(top)(x => Seq(x)) } + intercept[Exception] { Select.instances(top) } + } + } diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index 381cfeb5..d1620e88 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -29,6 +29,27 @@ object VecBundleDataView { implicit val v2 = v1.invert(_ => new MyBundle) } +object FlatDecoupledDataView { + class FizzBuzz extends Bundle { + val fizz = UInt(8.W) + val buzz = UInt(8.W) + } + class FlatDecoupled extends Bundle { + val valid = Output(Bool()) + val ready = Input(Bool()) + val fizz = Output(UInt(8.W)) + val buzz = Output(UInt(8.W)) + } + implicit val view = DataView[FlatDecoupled, DecoupledIO[FizzBuzz]]( + _ => Decoupled(new FizzBuzz), + _.valid -> _.valid, + _.ready -> _.ready, + _.fizz -> _.bits.fizz, + _.buzz -> _.bits.buzz + ) + implicit val view2 = view.invert(_ => new FlatDecoupled) +} + // This should become part of Chisel in a later PR object Tuple2DataProduct { implicit def tuple2DataProduct[A : DataProduct, B : DataProduct] = new DataProduct[(A, B)] { @@ -177,24 +198,7 @@ class DataViewSpec extends ChiselFlatSpec { } it should "work with bidirectional connections for nested types" in { - class FizzBuzz extends Bundle { - val fizz = UInt(8.W) - val buzz = UInt(8.W) - } - class FlatDecoupled extends Bundle { - val valid = Output(Bool()) - val ready = Input(Bool()) - val fizz = Output(UInt(8.W)) - val buzz = Output(UInt(8.W)) - } - implicit val view = DataView[FlatDecoupled, DecoupledIO[FizzBuzz]]( - _ => Decoupled(new FizzBuzz), - _.valid -> _.valid, - _.ready -> _.ready, - _.fizz -> _.bits.fizz, - _.buzz -> _.bits.buzz - ) - implicit val view2 = view.invert(_ => new FlatDecoupled) + import FlatDecoupledDataView._ class MyModule extends Module { val enq = IO(Flipped(Decoupled(new FizzBuzz))) val deq = IO(new FlatDecoupled) diff --git a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala index 41636da7..92091631 100644 --- a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala +++ b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala @@ -1,4 +1,4 @@ -// See LICENSE for license details. +// SPDX-License-Identifier: Apache-2.0 package chiselTests.experimental @@ -166,4 +166,6 @@ class DataViewTargetSpec extends ChiselFlatSpec { ) pairs should equal (expected) } + + // TODO check these properties when using @instance API (especially preservation of totality) } diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala new file mode 100644 index 00000000..43111fdd --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.experimental.hierarchy + +import _root_.firrtl.annotations._ +import chisel3.experimental.{annotate, BaseModule} +import chisel3.Data +import chisel3.experimental.hierarchy.{Instance, Definition} + +object Annotations { + case class MarkAnnotation(target: IsMember, tag: String) extends SingleTargetAnnotation[IsMember] { + def duplicate(n: IsMember): Annotation = this.copy(target = n) + } + case class MarkChiselInstanceAnnotation[B <: BaseModule](d: Instance[B], tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation { + def toFirrtl = MarkAnnotation(d.toTarget, tag) + } + case class MarkChiselDefinitionAnnotation[B <: BaseModule](d: Definition[B], tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation { + def toFirrtl = MarkAnnotation(d.toTarget, tag) + } + case class MarkChiselAnnotation(d: Data, tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation { + def toFirrtl = if(isAbsolute) MarkAnnotation(d.toAbsoluteTarget, tag) else MarkAnnotation(d.toTarget, tag) + } + def mark(d: Data, tag: String): Unit = annotate(MarkChiselAnnotation(d, tag, false)) + def mark[B <: BaseModule](d: Instance[B], tag: String): Unit = annotate(MarkChiselInstanceAnnotation(d, tag, false)) + def mark[B <: BaseModule](d: Definition[B], tag: String): Unit = annotate(MarkChiselDefinitionAnnotation(d, tag, false)) + def amark(d: Data, tag: String): Unit = annotate(MarkChiselAnnotation(d, tag, true)) + def amark[B <: BaseModule](d: Instance[B], tag: String): Unit = annotate(MarkChiselInstanceAnnotation(d, tag, true)) +} diff --git a/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala new file mode 100644 index 00000000..19261c36 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests +package experimental.hierarchy + +import chisel3._ +import chisel3.experimental.BaseModule +import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public} + +// TODO/Notes +// - In backport, clock/reset are not automatically assigned. I think this is fixed in 3.5 +// - CircuitTarget for annotations on the definition are wrong - needs to be fixed. +class DefinitionSpec extends ChiselFunSpec with Utils { + import Annotations._ + import Examples._ + describe("0: Definition instantiation") { + it("0.0: module name of a definition should be correct") { + class Top extends Module { + val definition = Definition(new AddOne) + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("module AddOne :") + } + it("0.2: accessing internal fields through non-generated means is hard to do") { + class Top extends Module { + val definition = Definition(new AddOne) + //definition.lookup(_.in) // Uncommenting this line will give the following error: + //"You are trying to access a macro-only API. Please use the @public annotation instead." + definition.in + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("module AddOne :") + } + it("0.2: reset inference is not defaulted to Bool for definitions") { + class Top extends Module with RequireAsyncReset { + val definition = Definition(new HasUninferredReset) + val i0 = Instance(definition) + i0.in := 0.U + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("inst i0 of HasUninferredReset") + } + } + describe("1: Annotations on definitions in same chisel compilation") { + it("1.0: should work on a single definition, annotating the definition") { + class Top extends Module { + val definition: Definition[AddOne] = Definition(new AddOne) + mark(definition, "mark") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne".mt, "mark")) + } + it("1.1: should work on a single definition, annotating an inner wire") { + class Top extends Module { + val definition: Definition[AddOne] = Definition(new AddOne) + mark(definition.innerWire, "i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "i0.innerWire")) + } + it("1.2: should work on a two nested definitions, annotating the definition") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + mark(definition.definition, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne".mt, "i0.i0")) + } + it("1.2: should work on an instance in a definition, annotating the instance") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + mark(definition.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne".it, "i0.i0")) + } + it("1.2: should work on a definition in an instance, annotating the definition") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + val i0 = Instance(definition) + mark(i0.definition, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne".mt, "i0.i0")) + } + it("1.3: should work on a wire in an instance in a definition") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + mark(definition.i0.innerWire, "i0.i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "i0.i0.innerWire")) + } + it("1.4: should work on a nested module in a definition, annotating the module") { + class Top extends Module { + val definition: Definition[AddTwoMixedModules] = Definition(new AddTwoMixedModules) + mark(definition.i1, "i0.i1") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2".it, "i0.i1")) + } + // Can you define an instantiable container? I think not. + // Instead, we can test the instantiable container in a definition + it("1.5: should work on an instantiable container, annotating a wire in the defintion") { + class Top extends Module { + val definition: Definition[AddOneWithInstantiableWire] = Definition(new AddOneWithInstantiableWire) + mark(definition.wireContainer.innerWire, "i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableWire>innerWire".rt, "i0.innerWire")) + } + it("1.6: should work on an instantiable container, annotating a module") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableModule) + mark(definition.moduleContainer.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableModule/i0:AddOne".it, "i0.i0")) + } + it("1.7: should work on an instantiable container, annotating an instance") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstance) + mark(definition.instanceContainer.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstance/i0:AddOne".it, "i0.i0")) + } + it("1.8: should work on an instantiable container, annotating an instantiable container's module") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstantiable) + mark(definition.containerContainer.container.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0")) + } + it("1.9: should work on public member which references public member of another instance") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstantiable) + mark(definition.containerContainer.container.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0")) + } + it("1.10: should work for targets on definition to have correct circuit name"){ + class Top extends Module { + val definition = Definition(new AddOneWithAnnotation) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire")) + } + } + describe("2: Annotations on designs not in the same chisel compilation") { + it("2.0: should work on an innerWire, marked in a different compilation") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Definition(new ViewerParent(x, false, true)) + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "first")) + } + it("2.1: should work on an innerWire, marked in a different compilation, in instanced instantiable") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Definition(new ViewerParent(x, true, false)) + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "second")) + } + it("2.2: should work on an innerWire, marked in a different compilation, in instanced module") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Definition(new ViewerParent(x, false, false)) + mark(parent.viewer.x.i0.innerWire, "third") + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "third")) + } + } + describe("3: @public") { + it("3.0: should work on multi-vals") { + class Top() extends Module { + val mv = Definition(new MultiVal()) + mark(mv.x, "mv.x") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|MultiVal>x".rt, "mv.x")) + } + it("3.1: should work on lazy vals") { + class Top() extends Module { + val lv = Definition(new LazyVal()) + mark(lv.x, lv.y) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|LazyVal>x".rt, "Hi")) + } + it("3.2: should work on islookupables") { + class Top() extends Module { + val p = Parameters("hi", 0) + val up = Definition(new UsesParameters(p)) + mark(up.x, up.y.string + up.y.int) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|UsesParameters>x".rt, "hi0")) + } + it("3.3: should work on lists") { + class Top() extends Module { + val i = Definition(new HasList()) + mark(i.x(1), i.y(1).toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasList>x_1".rt, "2")) + } + it("3.4: should work on seqs") { + class Top() extends Module { + val i = Definition(new HasSeq()) + mark(i.x(1), i.y(1).toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasSeq>x_1".rt, "2")) + } + it("3.5: should work on options") { + class Top() extends Module { + val i = Definition(new HasOption()) + i.x.map(x => mark(x, "x")) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasOption>x".rt, "x")) + } + it("3.6: should work on vecs") { + class Top() extends Module { + val i = Definition(new HasVec()) + mark(i.x, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasVec>x".rt, "blah")) + } + it("3.7: should work on statically indexed vectors external to module") { + class Top() extends Module { + val i = Definition(new HasVec()) + mark(i.x(1), "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasVec>x[1]".rt, "blah")) + } + it("3.8: should work on statically indexed vectors internal to module") { + class Top() extends Module { + val i = Definition(new HasIndexedVec()) + mark(i.y, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasIndexedVec>x[1]".rt, "blah")) + } + ignore("3.9: should work on vals in constructor arguments") { + class Top() extends Module { + val i = Definition(new HasPublicConstructorArgs(10)) + //mark(i.x, i.int.toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasPublicConstructorArgs>x".rt, "10")) + } + } + describe("4: toDefinition") { + it("4.0: should work on modules") { + class Top() extends Module { + val i = Module(new AddOne()) + f(i.toDefinition) + } + def f(i: Definition[AddOne]): Unit = mark(i.innerWire, "blah") + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "blah")) + } + it("4.2: should work on seqs of modules") { + class Top() extends Module { + val is = Seq(Module(new AddTwo()), Module(new AddTwo())).map(_.toDefinition) + mark(f(is), "blah") + } + def f(i: Seq[Definition[AddTwo]]): Data = i.head.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("4.2: should work on options of modules") { + class Top() extends Module { + val is: Option[Definition[AddTwo]] = Some(Module(new AddTwo())).map(_.toDefinition) + mark(f(is), "blah") + } + def f(i: Option[Definition[AddTwo]]): Data = i.get.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + } + describe("5: Absolute Targets should work as expected") { + it("5.0: toAbsoluteTarget on a port of a definition") { + class Top() extends Module { + val i = Definition(new AddTwo()) + amark(i.in, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo>in".rt, "blah")) + } + it("5.1: toAbsoluteTarget on a subinstance's data within a definition") { + class Top() extends Module { + val i = Definition(new AddTwo()) + amark(i.i0.innerWire, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("5.2: toAbsoluteTarget on a submodule's data within a definition") { + class Top() extends Module { + val i = Definition(new AddTwoMixedModules()) + amark(i.i1.in, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2>in".rt, "blah")) + } + it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within a definition") { + class Top() extends Module { + val i = Definition(new InstantiatesHasVec()) + amark(i.i1.x.head, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah")) + } + } + describe("6: @instantiable traits should work as expected") { + class MyBundle extends Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + } + @instantiable + trait ModuleIntf extends BaseModule { + @public val io = IO(new MyBundle) + } + @instantiable + class ModuleWithCommonIntf(suffix: String = "") extends Module with ModuleIntf { + override def desiredName: String = super.desiredName + suffix + @public val sum = io.in + 1.U + + io.out := sum + } + class BlackBoxWithCommonIntf extends BlackBox with ModuleIntf + + it("6.0: A Module that implements an @instantiable trait should be definable as that trait") { + class Top extends Module { + val i: Definition[ModuleIntf] = Definition(new ModuleWithCommonIntf) + mark(i.io.in, "gotcha") + mark(i, "inst") + } + val expected = List( + "~Top|ModuleWithCommonIntf>io.in".rt -> "gotcha", + "~Top|ModuleWithCommonIntf".mt -> "inst" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.1 An @instantiable Module that implements an @instantiable trait should be able to use extension methods from both") { + class Top extends Module { + val i: Definition[ModuleWithCommonIntf] = Definition(new ModuleWithCommonIntf) + mark(i.io.in, "gotcha") + mark(i.sum, "also this") + mark(i, "inst") + } + val expected = List( + "~Top|ModuleWithCommonIntf>io.in".rt -> "gotcha", + "~Top|ModuleWithCommonIntf>sum".rt -> "also this", + "~Top|ModuleWithCommonIntf".mt -> "inst" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.2 A BlackBox that implements an @instantiable trait should be instantiable as that trait") { + class Top extends Module { + val m: ModuleIntf = Module(new BlackBoxWithCommonIntf) + val d: Definition[ModuleIntf] = m.toDefinition + mark(d.io.in, "gotcha") + mark(d, "module") + } + val expected = List( + "~Top|BlackBoxWithCommonIntf>in".rt -> "gotcha", + "~Top|BlackBoxWithCommonIntf".mt -> "module" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.3 It should be possible to have Vectors of @instantiable traits mixing concrete subclasses") { + class Top extends Module { + val definition = Definition(new ModuleWithCommonIntf("X")) + val insts: Seq[Definition[ModuleIntf]] = Vector( + Module(new ModuleWithCommonIntf("Y")).toDefinition, + Module(new BlackBoxWithCommonIntf).toDefinition, + definition + ) + mark(insts(0).io.in, "foo") + mark(insts(1).io.in, "bar") + mark(insts(2).io.in, "fizz") + } + val expected = List( + "~Top|ModuleWithCommonIntfY>io.in".rt -> "foo", + "~Top|BlackBoxWithCommonIntf>in".rt -> "bar", + "~Top|ModuleWithCommonIntfX>io.in".rt -> "fizz" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } + describe("7: @instantiable and @public should compose with DataView") { + import chisel3.experimental.dataview._ + ignore("7.0: should work on simple Views") { + @instantiable + class MyModule extends RawModule { + val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + val sum = in + 1.U + out := sum + 1.U + @public val foo = in.viewAs[UInt] + @public val bar = sum.viewAs[UInt] + } + class Top extends RawModule { + val foo = IO(Input(UInt(8.W))) + val bar = IO(Output(UInt(8.W))) + val d = Definition(new MyModule) + val i = Instance(d) + i.foo := foo + bar := i.out + mark(d.out, "out") + mark(d.foo, "foo") + mark(d.bar, "bar") + } + val expectedAnnos = List( + "~Top|MyModule>out".rt -> "out", + "~Top|MyModule>in".rt -> "foo", + "~Top|MyModule>sum".rt -> "bar" + ) + val expectedLines = List( + "i.in <= foo", + "bar <= i.out" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- expectedLines) { + text should include (line) + } + for (e <- expectedAnnos.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + ignore("7.1: should work on Aggregate Views that are mapped 1:1") { + import chiselTests.experimental.SimpleBundleDataView._ + @instantiable + class MyModule extends RawModule { + private val a = IO(Input(new BundleA(8))) + private val b = IO(Output(new BundleA(8))) + @public val in = a.viewAs[BundleB] + @public val out = b.viewAs[BundleB] + out := in + } + class Top extends RawModule { + val foo = IO(Input(new BundleB(8))) + val bar = IO(Output(new BundleB(8))) + val d = Definition(new MyModule) + val i = Instance(d) + i.in := foo + bar.bar := i.out.bar + mark(d.in, "in") + mark(d.in.bar, "in_bar") + } + val expectedAnnos = List( + "~Top|MyModule>a".rt -> "in", + "~Top|MyModule>a.foo".rt -> "in_bar", + ) + val expectedLines = List( + "i.a <= foo", + "bar <= i.b.foo" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- expectedLines) { + text should include (line) + } + for (e <- expectedAnnos.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } +} diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala new file mode 100644 index 00000000..23b8c9c0 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.experimental.hierarchy + +import chisel3._ +import chisel3.util.Valid +import chisel3.experimental.hierarchy._ +import chisel3.experimental.BaseModule + +object Examples { + import Annotations._ + @instantiable + class AddOne extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val innerWire = Wire(UInt(32.W)) + innerWire := in + 1.U + out := innerWire + } + @instantiable + class AddOneWithAnnotation extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val innerWire = Wire(UInt(32.W)) + mark(innerWire, "innerWire") + innerWire := in + 1.U + out := innerWire + } + @instantiable + class AddOneWithAbsoluteAnnotation extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val innerWire = Wire(UInt(32.W)) + amark(innerWire, "innerWire") + innerWire := in + 1.U + out := innerWire + } + @instantiable + class AddTwo extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val definition = Definition(new AddOne) + @public val i0: Instance[AddOne] = Instance(definition) + @public val i1: Instance[AddOne] = Instance(definition) + i0.in := in + i1.in := i0.out + out := i1.out + } + @instantiable + class AddTwoMixedModules extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + val definition = Definition(new AddOne) + @public val i0: Instance[AddOne] = Instance(definition) + @public val i1 = Module(new AddOne) + i0.in := in + i1.in := i0.out + out := i1.out + } + @instantiable + class AggregatePortModule extends Module { + @public val io = IO(new Bundle { + val in = Input(UInt(32.W)) + val out = Output(UInt(32.W)) + }) + io.out := io.in + } + @instantiable + class WireContainer { + @public val innerWire = Wire(UInt(32.W)) + } + @instantiable + class AddOneWithInstantiableWire extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val wireContainer = new WireContainer() + wireContainer.innerWire := in + 1.U + out := wireContainer.innerWire + } + @instantiable + class AddOneContainer { + @public val i0 = Module(new AddOne) + } + @instantiable + class AddOneWithInstantiableModule extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val moduleContainer = new AddOneContainer() + moduleContainer.i0.in := in + out := moduleContainer.i0.out + } + @instantiable + class AddOneInstanceContainer { + val definition = Definition(new AddOne) + @public val i0 = Instance(definition) + } + @instantiable + class AddOneWithInstantiableInstance extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val instanceContainer = new AddOneInstanceContainer() + instanceContainer.i0.in := in + out := instanceContainer.i0.out + } + @instantiable + class AddOneContainerContainer { + @public val container = new AddOneContainer + } + @instantiable + class AddOneWithInstantiableInstantiable extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val containerContainer = new AddOneContainerContainer() + containerContainer.container.i0.in := in + out := containerContainer.container.i0.out + } + @instantiable + class Viewer(val y: AddTwo, markPlease: Boolean) { + @public val x = y + if(markPlease) mark(x.i0.innerWire, "first") + } + @instantiable + class ViewerParent(val x: AddTwo, markHere: Boolean, markThere: Boolean) extends Module { + @public val viewer = new Viewer(x, markThere) + if(markHere) mark(viewer.x.i0.innerWire, "second") + } + @instantiable + class MultiVal() extends Module { + @public val (x, y) = (Wire(UInt(3.W)), Wire(UInt(3.W))) + } + @instantiable + class LazyVal() extends Module { + @public val x = Wire(UInt(3.W)) + @public lazy val y = "Hi" + } + case class Parameters(string: String, int: Int) extends IsLookupable + @instantiable + class UsesParameters(p: Parameters) extends Module { + @public val y = p + @public val x = Wire(UInt(3.W)) + } + @instantiable + class HasList() extends Module { + @public val y = List(1, 2, 3) + @public val x = List.fill(3)(Wire(UInt(3.W))) + } + @instantiable + class HasSeq() extends Module { + @public val y = Seq(1, 2, 3) + @public val x = Seq.fill(3)(Wire(UInt(3.W))) + } + @instantiable + class HasOption() extends Module { + @public val x: Option[UInt] = Some(Wire(UInt(3.W))) + } + @instantiable + class HasVec() extends Module { + @public val x = VecInit(1.U, 2.U, 3.U) + } + @instantiable + class HasIndexedVec() extends Module { + val x = VecInit(1.U, 2.U, 3.U) + @public val y = x(1) + } + @instantiable + class HasSubFieldAccess extends Module { + val in = IO(Input(Valid(UInt(8.W)))) + @public val valid = in.valid + @public val bits = in.bits + } + @instantiable + class HasPublicConstructorArgs(@public val int: Int) extends Module { + @public val x = Wire(UInt(3.W)) + } + @instantiable + class InstantiatesHasVec() extends Module { + @public val i0 = Instance(Definition(new HasVec())) + @public val i1 = Module(new HasVec()) + } + @instantiable + class HasUninferredReset() extends Module { + @public val in = IO(Input(UInt(3.W))) + @public val out = IO(Output(UInt(3.W))) + out := RegNext(in) + } +} diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala new file mode 100644 index 00000000..3866bf87 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala @@ -0,0 +1,709 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests +package experimental.hierarchy + +import chisel3._ +import chisel3.experimental.BaseModule +import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public} +import chisel3.util.{DecoupledIO, Valid} + + +// TODO/Notes +// - In backport, clock/reset are not automatically assigned. I think this is fixed in 3.5 +// - CircuitTarget for annotations on the definition are wrong - needs to be fixed. +class InstanceSpec extends ChiselFunSpec with Utils { + import Annotations._ + import Examples._ + describe("0: Instance instantiation") { + it("0.0: name of an instance should be correct") { + class Top extends Module { + val definition = Definition(new AddOne) + val i0 = Instance(definition) + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("inst i0 of AddOne") + } + it("0.1: name of an instanceclone should not error") { + class Top extends Module { + val definition = Definition(new AddTwo) + val i0 = Instance(definition) + val i = i0.i0 // This should not error + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("inst i0 of AddTwo") + } + it("0.2: accessing internal fields through non-generated means is hard to do") { + class Top extends Module { + val definition = Definition(new AddOne) + val i0 = Instance(definition) + //i0.lookup(_.in) // Uncommenting this line will give the following error: + //"You are trying to access a macro-only API. Please use the @public annotation instead." + i0.in + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("inst i0 of AddOne") + } + } + describe("1: Annotations on instances in same chisel compilation") { + it("1.0: should work on a single instance, annotating the instance") { + class Top extends Module { + val definition: Definition[AddOne] = Definition(new AddOne) + val i0: Instance[AddOne] = Instance(definition) + mark(i0, "i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOne".it, "i0")) + } + it("1.1: should work on a single instance, annotating an inner wire") { + class Top extends Module { + val definition: Definition[AddOne] = Definition(new AddOne) + val i0: Instance[AddOne] = Instance(definition) + mark(i0.innerWire, "i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOne>innerWire".rt, "i0.innerWire")) + } + it("1.2: should work on a two nested instances, annotating the instance") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + val i0: Instance[AddTwo] = Instance(definition) + mark(i0.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddTwo/i0:AddOne".it, "i0.i0")) + } + it("1.3: should work on a two nested instances, annotating the inner wire") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + val i0: Instance[AddTwo] = Instance(definition) + mark(i0.i0.innerWire, "i0.i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddTwo/i0:AddOne>innerWire".rt, "i0.i0.innerWire")) + } + it("1.4: should work on a nested module in an instance, annotating the module") { + class Top extends Module { + val definition: Definition[AddTwoMixedModules] = Definition(new AddTwoMixedModules) + val i0: Instance[AddTwoMixedModules] = Instance(definition) + mark(i0.i1, "i0.i1") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddTwoMixedModules/i1:AddOne_2".it, "i0.i1")) + } + it("1.5: should work on an instantiable container, annotating a wire") { + class Top extends Module { + val definition: Definition[AddOneWithInstantiableWire] = Definition(new AddOneWithInstantiableWire) + val i0: Instance[AddOneWithInstantiableWire] = Instance(definition) + mark(i0.wireContainer.innerWire, "i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableWire>innerWire".rt, "i0.innerWire")) + } + it("1.6: should work on an instantiable container, annotating a module") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableModule) + val i0 = Instance(definition) + mark(i0.moduleContainer.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableModule/i0:AddOne".it, "i0.i0")) + } + it("1.7: should work on an instantiable container, annotating an instance") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstance) + val i0 = Instance(definition) + mark(i0.instanceContainer.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstance/i0:AddOne".it, "i0.i0")) + } + it("1.8: should work on an instantiable container, annotating an instantiable container's module") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstantiable) + val i0 = Instance(definition) + mark(i0.containerContainer.container.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0")) + } + it("1.9: should work on public member which references public member of another instance") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstantiable) + val i0 = Instance(definition) + mark(i0.containerContainer.container.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0")) + } + it("1.10: should work for targets on definition to have correct circuit name"){ + class Top extends Module { + val definition = Definition(new AddOneWithAnnotation) + val i0 = Instance(definition) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire")) + } + } + describe("2: Annotations on designs not in the same chisel compilation") { + it("2.0: should work on an innerWire, marked in a different compilation") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Instance(Definition(new ViewerParent(x, false, true))) + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "first")) + } + it("2.1: should work on an innerWire, marked in a different compilation, in instanced instantiable") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Instance(Definition(new ViewerParent(x, true, false))) + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "second")) + } + it("2.2: should work on an innerWire, marked in a different compilation, in instanced module") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Instance(Definition(new ViewerParent(x, false, false))) + mark(parent.viewer.x.i0.innerWire, "third") + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "third")) + } + } + describe("3: @public") { + it("3.0: should work on multi-vals") { + class Top() extends Module { + val mv = Instance(Definition(new MultiVal())) + mark(mv.x, "mv.x") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/mv:MultiVal>x".rt, "mv.x")) + } + it("3.1: should work on lazy vals") { + class Top() extends Module { + val lv = Instance(Definition(new LazyVal())) + mark(lv.x, lv.y) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/lv:LazyVal>x".rt, "Hi")) + } + it("3.2: should work on islookupables") { + class Top() extends Module { + val p = Parameters("hi", 0) + val up = Instance(Definition(new UsesParameters(p))) + mark(up.x, up.y.string + up.y.int) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/up:UsesParameters>x".rt, "hi0")) + } + it("3.3: should work on lists") { + class Top() extends Module { + val i = Instance(Definition(new HasList())) + mark(i.x(1), i.y(1).toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasList>x_1".rt, "2")) + } + it("3.4: should work on seqs") { + class Top() extends Module { + val i = Instance(Definition(new HasSeq())) + mark(i.x(1), i.y(1).toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasSeq>x_1".rt, "2")) + } + it("3.5: should work on options") { + class Top() extends Module { + val i = Instance(Definition(new HasOption())) + i.x.map(x => mark(x, "x")) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasOption>x".rt, "x")) + } + it("3.6: should work on vecs") { + class Top() extends Module { + val i = Instance(Definition(new HasVec())) + mark(i.x, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasVec>x".rt, "blah")) + } + it("3.7: should work on statically indexed vectors external to module") { + class Top() extends Module { + val i = Instance(Definition(new HasVec())) + mark(i.x(1), "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasVec>x[1]".rt, "blah")) + } + it("3.8: should work on statically indexed vectors internal to module") { + class Top() extends Module { + val i = Instance(Definition(new HasIndexedVec())) + mark(i.y, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasIndexedVec>x[1]".rt, "blah")) + } + it("3.9: should work on accessed subfields of aggregate ports") { + class Top extends Module { + val input = IO(Input(Valid(UInt(8.W)))) + val i = Instance(Definition(new HasSubFieldAccess)) + i.valid := input.valid + i.bits := input.bits + mark(i.valid, "valid") + mark(i.bits, "bits") + } + val expected = List( + "~Top|Top/i:HasSubFieldAccess>in.valid".rt -> "valid", + "~Top|Top/i:HasSubFieldAccess>in.bits".rt -> "bits" + ) + val lines = List( + "i.in.valid <= input.valid", + "i.in.bits <= input.bits" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- lines) { + text should include (line) + } + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + ignore("3.10: should work on vals in constructor arguments") { + class Top() extends Module { + val i = Instance(Definition(new HasPublicConstructorArgs(10))) + //mark(i.x, i.int.toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasPublicConstructorArgs>x".rt, "10")) + } + } + describe("4: toInstance") { + it("4.0: should work on modules") { + class Top() extends Module { + val i = Module(new AddOne()) + f(i.toInstance) + } + def f(i: Instance[AddOne]): Unit = mark(i.innerWire, "blah") + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "blah")) + } + it("4.1: should work on isinstantiables") { + class Top() extends Module { + val i = Module(new AddTwo()) + val v = new Viewer(i, false) + mark(f(v.toInstance), "blah") + } + def f(i: Instance[Viewer]): Data = i.x.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("4.2: should work on seqs of modules") { + class Top() extends Module { + val is = Seq(Module(new AddTwo()), Module(new AddTwo())).map(_.toInstance) + mark(f(is), "blah") + } + def f(i: Seq[Instance[AddTwo]]): Data = i.head.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("4.3: should work on seqs of isInstantiables") { + class Top() extends Module { + val i = Module(new AddTwo()) + val vs = Seq(new Viewer(i, false), new Viewer(i, false)).map(_.toInstance) + mark(f(vs), "blah") + } + def f(i: Seq[Instance[Viewer]]): Data = i.head.x.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("4.2: should work on options of modules") { + class Top() extends Module { + val is: Option[Instance[AddTwo]] = Some(Module(new AddTwo())).map(_.toInstance) + mark(f(is), "blah") + } + def f(i: Option[Instance[AddTwo]]): Data = i.get.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + } + describe("5: Absolute Targets should work as expected") { + it("5.0: toAbsoluteTarget on a port of an instance") { + class Top() extends Module { + val i = Instance(Definition(new AddTwo())) + amark(i.in, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwo>in".rt, "blah")) + } + it("5.1: toAbsoluteTarget on a subinstance's data within an instance") { + class Top() extends Module { + val i = Instance(Definition(new AddTwo())) + amark(i.i0.innerWire, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("5.2: toAbsoluteTarget on a submodule's data within an instance") { + class Top() extends Module { + val i = Instance(Definition(new AddTwoMixedModules())) + amark(i.i1.in, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwoMixedModules/i1:AddOne_2>in".rt, "blah")) + } + it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance") { + class Top() extends Module { + val i = Instance(Definition(new InstantiatesHasVec())) + amark(i.i1.x.head, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah")) + } + it("5.4: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance, ILit") { + class MyBundle extends Bundle { val x = UInt(3.W) } + @instantiable + class HasVec() extends Module { + @public val x = Wire(Vec(3, new MyBundle())) + } + @instantiable + class InstantiatesHasVec() extends Module { + @public val i0 = Instance(Definition(new HasVec())) + @public val i1 = Module(new HasVec()) + } + class Top() extends Module { + val i = Instance(Definition(new InstantiatesHasVec())) + amark(i.i1.x.head.x, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_2>x[0].x".rt, "blah")) + } + it("5.5: toAbsoluteTarget on a subinstance") { + class Top() extends Module { + val i = Instance(Definition(new AddTwo())) + amark(i.i1, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwo/i1:AddOne".it, "blah")) + } + it("5.6: should work for absolute targets on definition to have correct circuit name"){ + class Top extends Module { + val definition = Definition(new AddOneWithAbsoluteAnnotation) + val i0 = Instance(definition) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithAbsoluteAnnotation>innerWire".rt, "innerWire")) + } + } + describe("6: @instantiable traits should work as expected") { + class MyBundle extends Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + } + @instantiable + trait ModuleIntf extends BaseModule { + @public val io = IO(new MyBundle) + } + @instantiable + class ModuleWithCommonIntf(suffix: String = "") extends Module with ModuleIntf { + override def desiredName: String = super.desiredName + suffix + @public val sum = io.in + 1.U + + io.out := sum + } + class BlackBoxWithCommonIntf extends BlackBox with ModuleIntf + + it("6.0: A Module that implements an @instantiable trait should be instantiable as that trait") { + class Top extends Module { + val i: Instance[ModuleIntf] = Instance(Definition(new ModuleWithCommonIntf)) + mark(i.io.in, "gotcha") + mark(i, "inst") + } + val expected = List( + "~Top|Top/i:ModuleWithCommonIntf>io.in".rt -> "gotcha", + "~Top|Top/i:ModuleWithCommonIntf".it -> "inst" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.1 An @instantiable Module that implements an @instantiable trait should be able to use extension methods from both") { + class Top extends Module { + val i: Instance[ModuleWithCommonIntf] = Instance(Definition(new ModuleWithCommonIntf)) + mark(i.io.in, "gotcha") + mark(i.sum, "also this") + mark(i, "inst") + } + val expected = List( + "~Top|Top/i:ModuleWithCommonIntf>io.in".rt -> "gotcha", + "~Top|Top/i:ModuleWithCommonIntf>sum".rt -> "also this", + "~Top|Top/i:ModuleWithCommonIntf".it -> "inst" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.2 A BlackBox that implements an @instantiable trait should be instantiable as that trait") { + class Top extends Module { + val i: Instance[ModuleIntf] = Module(new BlackBoxWithCommonIntf).toInstance + mark(i.io.in, "gotcha") + mark(i, "module") + } + val expected = List( + "~Top|BlackBoxWithCommonIntf>in".rt -> "gotcha", + "~Top|BlackBoxWithCommonIntf".mt -> "module" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.3 It should be possible to have Vectors of @instantiable traits mixing concrete subclasses") { + class Top extends Module { + val proto = Definition(new ModuleWithCommonIntf("X")) + val insts: Seq[Instance[ModuleIntf]] = Vector( + Module(new ModuleWithCommonIntf("Y")).toInstance, + Module(new BlackBoxWithCommonIntf).toInstance, + Instance(proto) + ) + mark(insts(0).io.in, "foo") + mark(insts(1).io.in, "bar") + mark(insts(2).io.in, "fizz") + } + val expected = List( + "~Top|ModuleWithCommonIntfY>io.in".rt -> "foo", + "~Top|BlackBoxWithCommonIntf>in".rt -> "bar", + "~Top|Top/insts_2:ModuleWithCommonIntfX>io.in".rt -> "fizz" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } + // TODO don't forget to test this with heterogeneous Views (eg. viewing a tuple of a port and non-port as a single Bundle) + describe("7: @instantiable and @public should compose with DataView") { + import chisel3.experimental.dataview._ + it("7.0: should work on simple Views") { + @instantiable + class MyModule extends RawModule { + val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + val sum = in + 1.U + out := sum + 1.U + @public val foo = in.viewAs[UInt] + @public val bar = sum.viewAs[UInt] + } + class Top extends RawModule { + val foo = IO(Input(UInt(8.W))) + val bar = IO(Output(UInt(8.W))) + val i = Instance(Definition(new MyModule)) + i.foo := foo + bar := i.out + mark(i.out, "out") + mark(i.foo, "foo") + mark(i.bar, "bar") + } + val expectedAnnos = List( + "~Top|Top/i:MyModule>out".rt -> "out", + "~Top|Top/i:MyModule>in".rt -> "foo", + "~Top|Top/i:MyModule>sum".rt -> "bar" + ) + val expectedLines = List( + "i.in <= foo", + "bar <= i.out" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- expectedLines) { + text should include (line) + } + for (e <- expectedAnnos.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + + ignore("7.1: should work on Aggregate Views") { + import chiselTests.experimental.FlatDecoupledDataView._ + type RegDecoupled = DecoupledIO[FizzBuzz] + @instantiable + class MyModule extends RawModule { + private val a = IO(Flipped(new FlatDecoupled)) + private val b = IO(new FlatDecoupled) + @public val enq = a.viewAs[RegDecoupled] + @public val deq = b.viewAs[RegDecoupled] + @public val enq_valid = enq.valid // Also return a subset of the view + deq <> enq + } + class Top extends RawModule { + val foo = IO(Flipped(new RegDecoupled(new FizzBuzz))) + val bar = IO(new RegDecoupled(new FizzBuzz)) + val i = Instance(Definition(new MyModule)) + i.enq <> foo + i.enq_valid := foo.valid // Make sure connections also work for @public on elements of a larger Aggregate + i.deq.ready := bar.ready + bar.valid := i.deq.valid + bar.bits := i.deq.bits + mark(i.enq, "enq") + mark(i.enq.bits, "enq.bits") + mark(i.deq.bits.fizz, "deq.bits.fizz") + mark(i.enq_valid, "enq_valid") + } + val expectedAnnos = List( + "~Top|Top/i:MyModule>a".rt -> "enq", // Not split, checks 1:1 + "~Top|Top/i:MyModule>a.fizz".rt -> "enq.bits", // Split, checks non-1:1 inner Aggregate + "~Top|Top/i:MyModule>a.buzz".rt -> "enq.bits", + "~Top|Top/i:MyModule>b.fizz".rt -> "deq.bits.fizz", // Checks 1 inner Element + "~Top|Top/i:MyModule>a.valid".rt -> "enq_valid" + ) + val expectedLines = List( + "i.a.valid <= foo.valid", + "foo.ready <= i.a.ready", + "i.a.fizz <= foo.bits.fizz", + "i.a.buzz <= foo.bits.buzz", + "bar.valid <= i.b.valid", + "i.b.ready <= bar.ready", + "bar.bits.fizz <= i.b.fizz", + "bar.bits.buzz <= i.b.buzz", + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- expectedLines) { + text should include (line) + } + for (e <- expectedAnnos.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + + it("7.2: should work on views of views") { + import chiselTests.experimental.SimpleBundleDataView._ + @instantiable + class MyModule extends RawModule { + private val a = IO(Input(UInt(8.W))) + private val b = IO(Output(new BundleA(8))) + @public val in = a.viewAs[UInt].viewAs[UInt] + @public val out = b.viewAs[BundleB].viewAs[BundleA].viewAs[BundleB] + out.bar := in + } + class Top extends RawModule { + val foo = IO(Input(UInt(8.W))) + val bar = IO(Output(new BundleB(8))) + val i = Instance(Definition(new MyModule)) + i.in := foo + bar := i.out + bar.bar := i.out.bar + mark(i.in, "in") + mark(i.out.bar, "out_bar") + } + val expected = List( + "~Top|Top/i:MyModule>a".rt -> "in", + "~Top|Top/i:MyModule>b.foo".rt -> "out_bar", + ) + val lines = List( + "i.a <= foo", + "bar.bar <= i.b.foo" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- lines) { + text should include (line) + } + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + + it("7.3: should work with DataView + implicit conversion") { + import chiselTests.experimental.SeqToVec._ + @instantiable + class MyModule extends RawModule { + private val a = IO(Input(UInt(8.W))) + private val b = IO(Output(UInt(8.W))) + @public val ports = Seq(a, b) + b := a + } + class Top extends RawModule { + val foo = IO(Input(UInt(8.W))) + val bar = IO(Output(UInt(8.W))) + val i = Instance(Definition(new MyModule)) + i.ports <> Seq(foo, bar) + mark(i.ports, "i.ports") + } + val expected = List( + // Not 1:1 so will get split out + "~Top|Top/i:MyModule>a".rt -> "i.ports", + "~Top|Top/i:MyModule>b".rt -> "i.ports", + ) + val lines = List( + "i.a <= foo", + "bar <= i.b" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- lines) { + text should include (line) + } + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } + + describe("8: @instantiable and @public should compose with CloneModuleAsRecord") { + it("8.0: it should support @public on a CMAR Record in Definitions") { + @instantiable + class HasCMAR extends Module { + @public val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + @public val m = Module(new AggregatePortModule) + @public val c = experimental.CloneModuleAsRecord(m) + } + class Top extends Module { + val d = Definition(new HasCMAR) + mark(d.c("io"), "c.io") + val bun = d.c("io").asInstanceOf[Record] + mark(bun.elements("out"), "c.io.out") + } + val expected = List( + "~Top|HasCMAR/c:AggregatePortModule>io".rt -> "c.io", + "~Top|HasCMAR/c:AggregatePortModule>io.out".rt -> "c.io.out" + + ) + val (_, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("8.1: it should support @public on a CMAR Record in Instances") { + @instantiable + class HasCMAR extends Module { + @public val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + @public val m = Module(new AggregatePortModule) + @public val c = experimental.CloneModuleAsRecord(m) + } + class Top extends Module { + val i = Instance(Definition(new HasCMAR)) + mark(i.c("io"), "i.c.io") + val bun = i.c("io").asInstanceOf[Record] + mark(bun.elements("out"), "i.c.io.out") + } + val expected = List( + "~Top|Top/i:HasCMAR/c:AggregatePortModule>io".rt -> "i.c.io", + "~Top|Top/i:HasCMAR/c:AggregatePortModule>io.out".rt -> "i.c.io.out" + + ) + val (_, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } +} + diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala b/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala new file mode 100644 index 00000000..a2e51765 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.experimental.hierarchy + +import chisel3._ +import _root_.firrtl.annotations._ +import chisel3.stage.{ChiselCircuitAnnotation, CircuitSerializationAnnotation, DesignAnnotation} +import chiselTests.ChiselRunners +import firrtl.stage.FirrtlCircuitAnnotation +import org.scalatest.matchers.should.Matchers + +trait Utils extends ChiselRunners with chiselTests.Utils with Matchers { + import Annotations._ + // TODO promote to standard API (in FIRRTL) and perhaps even implement with a macro + implicit class Str2RefTarget(str: String) { + def rt: ReferenceTarget = Target.deserialize(str).asInstanceOf[ReferenceTarget] + def it: InstanceTarget = Target.deserialize(str).asInstanceOf[InstanceTarget] + def mt: ModuleTarget = Target.deserialize(str).asInstanceOf[ModuleTarget] + def ct: CircuitTarget = Target.deserialize(str).asInstanceOf[CircuitTarget] + } +} -- cgit v1.2.3 From 89679a3c7b42f34d3b9e93dfb6972bc36b6af297 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Mon, 6 Sep 2021 07:35:46 -0700 Subject: Add a test case to demonstrate the bug found in #2112 --- .../scala/chiselTests/util/experimental/PlaSpec.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/PlaSpec.scala b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala index 45ac012e..ced4f9dd 100644 --- a/src/test/scala/chiselTests/util/experimental/PlaSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala @@ -49,6 +49,22 @@ class PlaSpec extends ChiselFlatSpec { }) } + "#2112" should "be generated correctly" in { + assertTesterPasses(new BasicTester { + val table = Seq( + (BitPat("b0"), BitPat("b?0")), + (BitPat("b1"), BitPat("b?1")), + (BitPat("b?"), BitPat("b1?")), + ) + table.foreach { case (i, o) => + val (plaIn, plaOut) = pla(table) + plaIn := WireDefault(i.value.U(3.W)) + chisel3.assert(plaOut === o.value.U(8.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut) + } + stop() + }) + } + "A simple PLA" should "be generated correctly" in { assertTesterPasses(new BasicTester { val table = Seq( -- cgit v1.2.3 From 0ca4ce23400d3624f8f617aa8ae21880d8430c2e Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Mon, 6 Sep 2021 21:27:51 -0700 Subject: Test case rework --- src/test/scala/chiselTests/util/experimental/PlaSpec.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/util/experimental/PlaSpec.scala b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala index ced4f9dd..8af5c936 100644 --- a/src/test/scala/chiselTests/util/experimental/PlaSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala @@ -52,14 +52,13 @@ class PlaSpec extends ChiselFlatSpec { "#2112" should "be generated correctly" in { assertTesterPasses(new BasicTester { val table = Seq( - (BitPat("b0"), BitPat("b?0")), - (BitPat("b1"), BitPat("b?1")), - (BitPat("b?"), BitPat("b1?")), + (BitPat("b000"), BitPat("b?01")), + (BitPat("b111"), BitPat("b?01")), ) table.foreach { case (i, o) => val (plaIn, plaOut) = pla(table) plaIn := WireDefault(i.value.U(3.W)) - chisel3.assert(plaOut === o.value.U(8.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut) + chisel3.assert(o === plaOut, "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut) } stop() }) -- cgit v1.2.3 From 1ae18008e18dfe20983a6ba3b2b950f66245e69b Mon Sep 17 00:00:00 2001 From: Adam Izraelevitz Date: Wed, 15 Sep 2021 11:49:29 -0700 Subject: Fix higher-kinded types for autoclonetype (#2121) --- src/test/scala/chiselTests/AutoClonetypeSpec.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index 57e00e99..fcbc4785 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -348,5 +348,18 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { elaborate(new MyModule(UInt(8.W))) } + it should "work for higher-kinded types" in { + class DataGen[T <: Data](gen: T) { + def newType: T = gen.cloneType + } + class MyBundle[A <: Data, B <: DataGen[A]](gen: B) extends Bundle { + val foo = gen.newType + } + class MyModule extends MultiIOModule { + val io = IO(Output(new MyBundle[UInt, DataGen[UInt]](new DataGen(UInt(3.W))))) + io.foo := 0.U + } + elaborate(new MyModule) + } } } -- cgit v1.2.3