summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/chisel3/stage/phases/Convert.scala3
-rw-r--r--src/test/scala/chiselTests/ChiselSpec.scala29
-rw-r--r--src/test/scala/chiselTests/experimental/DataView.scala542
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala57
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala169
-rw-r--r--src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala91
-rw-r--r--src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala3
7 files changed, 891 insertions, 3 deletions
diff --git a/src/main/scala/chisel3/stage/phases/Convert.scala b/src/main/scala/chisel3/stage/phases/Convert.scala
index bf42b58a..b5b01b8d 100644
--- a/src/main/scala/chisel3/stage/phases/Convert.scala
+++ b/src/main/scala/chisel3/stage/phases/Convert.scala
@@ -29,8 +29,7 @@ class Convert extends Phase {
/* Convert all Chisel Annotations to FIRRTL Annotations */
a
.circuit
- .annotations
- .map(_.toFirrtl) ++
+ .firrtlAnnotations ++
a
.circuit
.annotations
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 <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..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,