summaryrefslogtreecommitdiff
path: root/src/test/scala/chiselTests/experimental
diff options
context:
space:
mode:
authorJack Koenig2021-09-17 21:01:26 -0700
committerJack Koenig2021-09-17 21:01:26 -0700
commit5c8c19345e6711279594cf1f9ddab33623c8eba7 (patch)
treed9d6ced3934aa4a8be3dec19ddcefe50a7a93d5a /src/test/scala/chiselTests/experimental
parente63b9667d89768e0ec6dc8a9153335cb48a213a7 (diff)
parent958904cb2f2f65d02b2ab3ec6d9ec2e06d04e482 (diff)
Merge branch 'master' into 3.5-release
Diffstat (limited to 'src/test/scala/chiselTests/experimental')
-rw-r--r--src/test/scala/chiselTests/experimental/DataView.scala546
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala57
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala171
-rw-r--r--src/test/scala/chiselTests/experimental/ForceNames.scala2
-rw-r--r--src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala91
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala28
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala493
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Examples.scala186
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala709
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Utils.scala21
-rw-r--r--src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala135
11 files changed, 2429 insertions, 10 deletions
diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala
new file mode 100644
index 00000000..d1620e88
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/DataView.scala
@@ -0,0 +1,546 @@
+// 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)
+}
+
+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)] {
+ 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 {
+ import FlatDecoupledDataView._
+ 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 <unknown>""".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..92091631
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: Apache-2.0
+
+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)
+ }
+
+ // TODO check these properties when using @instance API (especially preservation of totality)
+}
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}
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/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]
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala
index 52293abb..1e080739 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,18 +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)
+}
- def assertContains[T](s: Seq[T], x: T): Unit = {
- val containsLine = s.map(_ == x).reduce(_ || _)
+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(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.emitFirrtl(new VerificationModule)
+ val fir = ChiselStage.emitChirrtl(new SimpleTest)
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]")
- assertContains(lines, "assert(clock, _T_3, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 19:18]")
+
+ // reset guard around the verification statement
+ 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>(\"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") {
+ /** 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")
+
+ // read in FIRRTL file
+ val firFile = new File(testDir, "AnnotationRenameTest.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 ("assert(clock, _T, UInt<1>(\"h1\"), \"\") : hello")
+ exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(\"h1\"), \"\") : howdy")
}
}