diff options
| author | Adam Izraelevitz | 2021-09-05 12:11:32 -0700 |
|---|---|---|
| committer | GitHub | 2021-09-05 12:11:32 -0700 |
| commit | 9fa8da227569455a77596355aeb114f9c164510a (patch) | |
| tree | 3be3dd579fcc7ae83297d3c28020e97417a6b984 /src/test/scala/chiselTests | |
| parent | 7fb2c1ebc23ca07e5de6416a284e1be1b62a48ac (diff) | |
Add Definition and Instance API (#2045)
This introduces a new experimental API for module instantiation that disentagles
elaborating the definition (or implementation) from instantiation of
a given module. This solves Chisel's longstanding reliance on
"Deduplication" for generating Verilog with multiple instances of the
same module.
The new API resides in package chisel3.experimental.hierarchy. Please
see the hierarchy ScalaDoc, documentation, and tests for examples of
use.
Co-authored-by: Jack Koenig <koenig@sifive.com>
Co-authored-by: Megan Wachs <megan@sifive.com>
Co-authored-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
Diffstat (limited to 'src/test/scala/chiselTests')
9 files changed, 1500 insertions, 23 deletions
diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 8e35273d..8647d903 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -16,6 +16,7 @@ import org.scalacheck._ import org.scalatest._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.funspec.AnyFunSpec import org.scalatest.propspec.AnyPropSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks @@ -104,13 +105,14 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { * @param t the generator for the module * @return The FIRRTL Circuit and Annotations _before_ FIRRTL compilation */ - def getFirrtlAndAnnos(t: => RawModule): (Circuit, Seq[Annotation]) = { + def getFirrtlAndAnnos(t: => RawModule, providedAnnotations: Seq[Annotation] = Nil): (Circuit, Seq[Annotation]) = { val args = Array( "--target-dir", createTestDirectory(this.getClass.getSimpleName).toString, - "--no-run-firrtl" + "--no-run-firrtl", + "--full-stacktrace" ) - val annos = (new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => t))) + val annos = (new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => t)) ++ providedAnnotations) val circuit = annos.collectFirst { case FirrtlCircuitAnnotation(c) => c }.getOrElse(fail("No FIRRTL Circuit found!!")) @@ -124,6 +126,9 @@ abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matche /** Spec base class for BDD-style testers. */ abstract class ChiselFreeSpec extends AnyFreeSpec with ChiselRunners with Matchers +/** Spec base class for BDD-style testers. */ +abstract class ChiselFunSpec extends AnyFunSpec with ChiselRunners with Matchers + /** Spec base class for property-based testers. */ abstract class ChiselPropSpec extends AnyPropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala index 46c62d67..e09e78c8 100644 --- a/src/test/scala/chiselTests/aop/SelectSpec.scala +++ b/src/test/scala/chiselTests/aop/SelectSpec.scala @@ -163,7 +163,7 @@ class SelectSpec extends ChiselFlatSpec { val out = IO(Output(UInt(8.W))) out := in } - class Top extends MultiIOModule { + class Top extends Module { val in = IO(Input(UInt(8.W))) val out = IO(Output(UInt(8.W))) val inst0 = Module(new Child) @@ -182,5 +182,34 @@ class SelectSpec extends ChiselFlatSpec { Select.instances(top) should equal (Seq(top.inst0)) } + "Using Definition/Instance with Injecting Aspects" should "throw an error" in { + import chisel3.experimental.CloneModuleAsRecord + import chisel3.experimental.hierarchy._ + @instantiable + class Child extends RawModule { + @public val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + out := in + } + class Top extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + val definition = Definition(new Child) + val inst0 = Instance(definition) + val inst1 = Instance(definition) + inst0.in := in + inst1.in := inst0.out + out := inst1.out + } + val top = ChiselGeneratorAnnotation(() => { + new Top() + }).elaborate + .collectFirst { case DesignAnnotation(design: Top) => design } + .get + intercept[Exception] { Select.collectDeep(top) { case x => x } } + intercept[Exception] { Select.getDeep(top)(x => Seq(x)) } + intercept[Exception] { Select.instances(top) } + } + } diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index 381cfeb5..d1620e88 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -29,6 +29,27 @@ object VecBundleDataView { implicit val v2 = v1.invert(_ => new MyBundle) } +object FlatDecoupledDataView { + class FizzBuzz extends Bundle { + val fizz = UInt(8.W) + val buzz = UInt(8.W) + } + class FlatDecoupled extends Bundle { + val valid = Output(Bool()) + val ready = Input(Bool()) + val fizz = Output(UInt(8.W)) + val buzz = Output(UInt(8.W)) + } + implicit val view = DataView[FlatDecoupled, DecoupledIO[FizzBuzz]]( + _ => Decoupled(new FizzBuzz), + _.valid -> _.valid, + _.ready -> _.ready, + _.fizz -> _.bits.fizz, + _.buzz -> _.bits.buzz + ) + implicit val view2 = view.invert(_ => new FlatDecoupled) +} + // This should become part of Chisel in a later PR object Tuple2DataProduct { implicit def tuple2DataProduct[A : DataProduct, B : DataProduct] = new DataProduct[(A, B)] { @@ -177,24 +198,7 @@ class DataViewSpec extends ChiselFlatSpec { } it should "work with bidirectional connections for nested types" in { - class FizzBuzz extends Bundle { - val fizz = UInt(8.W) - val buzz = UInt(8.W) - } - class FlatDecoupled extends Bundle { - val valid = Output(Bool()) - val ready = Input(Bool()) - val fizz = Output(UInt(8.W)) - val buzz = Output(UInt(8.W)) - } - implicit val view = DataView[FlatDecoupled, DecoupledIO[FizzBuzz]]( - _ => Decoupled(new FizzBuzz), - _.valid -> _.valid, - _.ready -> _.ready, - _.fizz -> _.bits.fizz, - _.buzz -> _.bits.buzz - ) - implicit val view2 = view.invert(_ => new FlatDecoupled) + import FlatDecoupledDataView._ class MyModule extends Module { val enq = IO(Flipped(Decoupled(new FizzBuzz))) val deq = IO(new FlatDecoupled) diff --git a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala index 41636da7..92091631 100644 --- a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala +++ b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala @@ -1,4 +1,4 @@ -// See LICENSE for license details. +// SPDX-License-Identifier: Apache-2.0 package chiselTests.experimental @@ -166,4 +166,6 @@ class DataViewTargetSpec extends ChiselFlatSpec { ) pairs should equal (expected) } + + // TODO check these properties when using @instance API (especially preservation of totality) } diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala new file mode 100644 index 00000000..43111fdd --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.experimental.hierarchy + +import _root_.firrtl.annotations._ +import chisel3.experimental.{annotate, BaseModule} +import chisel3.Data +import chisel3.experimental.hierarchy.{Instance, Definition} + +object Annotations { + case class MarkAnnotation(target: IsMember, tag: String) extends SingleTargetAnnotation[IsMember] { + def duplicate(n: IsMember): Annotation = this.copy(target = n) + } + case class MarkChiselInstanceAnnotation[B <: BaseModule](d: Instance[B], tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation { + def toFirrtl = MarkAnnotation(d.toTarget, tag) + } + case class MarkChiselDefinitionAnnotation[B <: BaseModule](d: Definition[B], tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation { + def toFirrtl = MarkAnnotation(d.toTarget, tag) + } + case class MarkChiselAnnotation(d: Data, tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation { + def toFirrtl = if(isAbsolute) MarkAnnotation(d.toAbsoluteTarget, tag) else MarkAnnotation(d.toTarget, tag) + } + def mark(d: Data, tag: String): Unit = annotate(MarkChiselAnnotation(d, tag, false)) + def mark[B <: BaseModule](d: Instance[B], tag: String): Unit = annotate(MarkChiselInstanceAnnotation(d, tag, false)) + def mark[B <: BaseModule](d: Definition[B], tag: String): Unit = annotate(MarkChiselDefinitionAnnotation(d, tag, false)) + def amark(d: Data, tag: String): Unit = annotate(MarkChiselAnnotation(d, tag, true)) + def amark[B <: BaseModule](d: Instance[B], tag: String): Unit = annotate(MarkChiselInstanceAnnotation(d, tag, true)) +} diff --git a/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala new file mode 100644 index 00000000..19261c36 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests +package experimental.hierarchy + +import chisel3._ +import chisel3.experimental.BaseModule +import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public} + +// TODO/Notes +// - In backport, clock/reset are not automatically assigned. I think this is fixed in 3.5 +// - CircuitTarget for annotations on the definition are wrong - needs to be fixed. +class DefinitionSpec extends ChiselFunSpec with Utils { + import Annotations._ + import Examples._ + describe("0: Definition instantiation") { + it("0.0: module name of a definition should be correct") { + class Top extends Module { + val definition = Definition(new AddOne) + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("module AddOne :") + } + it("0.2: accessing internal fields through non-generated means is hard to do") { + class Top extends Module { + val definition = Definition(new AddOne) + //definition.lookup(_.in) // Uncommenting this line will give the following error: + //"You are trying to access a macro-only API. Please use the @public annotation instead." + definition.in + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("module AddOne :") + } + it("0.2: reset inference is not defaulted to Bool for definitions") { + class Top extends Module with RequireAsyncReset { + val definition = Definition(new HasUninferredReset) + val i0 = Instance(definition) + i0.in := 0.U + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("inst i0 of HasUninferredReset") + } + } + describe("1: Annotations on definitions in same chisel compilation") { + it("1.0: should work on a single definition, annotating the definition") { + class Top extends Module { + val definition: Definition[AddOne] = Definition(new AddOne) + mark(definition, "mark") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne".mt, "mark")) + } + it("1.1: should work on a single definition, annotating an inner wire") { + class Top extends Module { + val definition: Definition[AddOne] = Definition(new AddOne) + mark(definition.innerWire, "i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "i0.innerWire")) + } + it("1.2: should work on a two nested definitions, annotating the definition") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + mark(definition.definition, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne".mt, "i0.i0")) + } + it("1.2: should work on an instance in a definition, annotating the instance") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + mark(definition.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne".it, "i0.i0")) + } + it("1.2: should work on a definition in an instance, annotating the definition") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + val i0 = Instance(definition) + mark(i0.definition, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne".mt, "i0.i0")) + } + it("1.3: should work on a wire in an instance in a definition") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + mark(definition.i0.innerWire, "i0.i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "i0.i0.innerWire")) + } + it("1.4: should work on a nested module in a definition, annotating the module") { + class Top extends Module { + val definition: Definition[AddTwoMixedModules] = Definition(new AddTwoMixedModules) + mark(definition.i1, "i0.i1") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2".it, "i0.i1")) + } + // Can you define an instantiable container? I think not. + // Instead, we can test the instantiable container in a definition + it("1.5: should work on an instantiable container, annotating a wire in the defintion") { + class Top extends Module { + val definition: Definition[AddOneWithInstantiableWire] = Definition(new AddOneWithInstantiableWire) + mark(definition.wireContainer.innerWire, "i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableWire>innerWire".rt, "i0.innerWire")) + } + it("1.6: should work on an instantiable container, annotating a module") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableModule) + mark(definition.moduleContainer.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableModule/i0:AddOne".it, "i0.i0")) + } + it("1.7: should work on an instantiable container, annotating an instance") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstance) + mark(definition.instanceContainer.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstance/i0:AddOne".it, "i0.i0")) + } + it("1.8: should work on an instantiable container, annotating an instantiable container's module") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstantiable) + mark(definition.containerContainer.container.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0")) + } + it("1.9: should work on public member which references public member of another instance") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstantiable) + mark(definition.containerContainer.container.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0")) + } + it("1.10: should work for targets on definition to have correct circuit name"){ + class Top extends Module { + val definition = Definition(new AddOneWithAnnotation) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire")) + } + } + describe("2: Annotations on designs not in the same chisel compilation") { + it("2.0: should work on an innerWire, marked in a different compilation") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Definition(new ViewerParent(x, false, true)) + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "first")) + } + it("2.1: should work on an innerWire, marked in a different compilation, in instanced instantiable") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Definition(new ViewerParent(x, true, false)) + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "second")) + } + it("2.2: should work on an innerWire, marked in a different compilation, in instanced module") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Definition(new ViewerParent(x, false, false)) + mark(parent.viewer.x.i0.innerWire, "third") + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "third")) + } + } + describe("3: @public") { + it("3.0: should work on multi-vals") { + class Top() extends Module { + val mv = Definition(new MultiVal()) + mark(mv.x, "mv.x") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|MultiVal>x".rt, "mv.x")) + } + it("3.1: should work on lazy vals") { + class Top() extends Module { + val lv = Definition(new LazyVal()) + mark(lv.x, lv.y) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|LazyVal>x".rt, "Hi")) + } + it("3.2: should work on islookupables") { + class Top() extends Module { + val p = Parameters("hi", 0) + val up = Definition(new UsesParameters(p)) + mark(up.x, up.y.string + up.y.int) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|UsesParameters>x".rt, "hi0")) + } + it("3.3: should work on lists") { + class Top() extends Module { + val i = Definition(new HasList()) + mark(i.x(1), i.y(1).toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasList>x_1".rt, "2")) + } + it("3.4: should work on seqs") { + class Top() extends Module { + val i = Definition(new HasSeq()) + mark(i.x(1), i.y(1).toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasSeq>x_1".rt, "2")) + } + it("3.5: should work on options") { + class Top() extends Module { + val i = Definition(new HasOption()) + i.x.map(x => mark(x, "x")) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasOption>x".rt, "x")) + } + it("3.6: should work on vecs") { + class Top() extends Module { + val i = Definition(new HasVec()) + mark(i.x, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasVec>x".rt, "blah")) + } + it("3.7: should work on statically indexed vectors external to module") { + class Top() extends Module { + val i = Definition(new HasVec()) + mark(i.x(1), "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasVec>x[1]".rt, "blah")) + } + it("3.8: should work on statically indexed vectors internal to module") { + class Top() extends Module { + val i = Definition(new HasIndexedVec()) + mark(i.y, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasIndexedVec>x[1]".rt, "blah")) + } + ignore("3.9: should work on vals in constructor arguments") { + class Top() extends Module { + val i = Definition(new HasPublicConstructorArgs(10)) + //mark(i.x, i.int.toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|HasPublicConstructorArgs>x".rt, "10")) + } + } + describe("4: toDefinition") { + it("4.0: should work on modules") { + class Top() extends Module { + val i = Module(new AddOne()) + f(i.toDefinition) + } + def f(i: Definition[AddOne]): Unit = mark(i.innerWire, "blah") + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "blah")) + } + it("4.2: should work on seqs of modules") { + class Top() extends Module { + val is = Seq(Module(new AddTwo()), Module(new AddTwo())).map(_.toDefinition) + mark(f(is), "blah") + } + def f(i: Seq[Definition[AddTwo]]): Data = i.head.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("4.2: should work on options of modules") { + class Top() extends Module { + val is: Option[Definition[AddTwo]] = Some(Module(new AddTwo())).map(_.toDefinition) + mark(f(is), "blah") + } + def f(i: Option[Definition[AddTwo]]): Data = i.get.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + } + describe("5: Absolute Targets should work as expected") { + it("5.0: toAbsoluteTarget on a port of a definition") { + class Top() extends Module { + val i = Definition(new AddTwo()) + amark(i.in, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo>in".rt, "blah")) + } + it("5.1: toAbsoluteTarget on a subinstance's data within a definition") { + class Top() extends Module { + val i = Definition(new AddTwo()) + amark(i.i0.innerWire, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("5.2: toAbsoluteTarget on a submodule's data within a definition") { + class Top() extends Module { + val i = Definition(new AddTwoMixedModules()) + amark(i.i1.in, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2>in".rt, "blah")) + } + it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within a definition") { + class Top() extends Module { + val i = Definition(new InstantiatesHasVec()) + amark(i.i1.x.head, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah")) + } + } + describe("6: @instantiable traits should work as expected") { + class MyBundle extends Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + } + @instantiable + trait ModuleIntf extends BaseModule { + @public val io = IO(new MyBundle) + } + @instantiable + class ModuleWithCommonIntf(suffix: String = "") extends Module with ModuleIntf { + override def desiredName: String = super.desiredName + suffix + @public val sum = io.in + 1.U + + io.out := sum + } + class BlackBoxWithCommonIntf extends BlackBox with ModuleIntf + + it("6.0: A Module that implements an @instantiable trait should be definable as that trait") { + class Top extends Module { + val i: Definition[ModuleIntf] = Definition(new ModuleWithCommonIntf) + mark(i.io.in, "gotcha") + mark(i, "inst") + } + val expected = List( + "~Top|ModuleWithCommonIntf>io.in".rt -> "gotcha", + "~Top|ModuleWithCommonIntf".mt -> "inst" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.1 An @instantiable Module that implements an @instantiable trait should be able to use extension methods from both") { + class Top extends Module { + val i: Definition[ModuleWithCommonIntf] = Definition(new ModuleWithCommonIntf) + mark(i.io.in, "gotcha") + mark(i.sum, "also this") + mark(i, "inst") + } + val expected = List( + "~Top|ModuleWithCommonIntf>io.in".rt -> "gotcha", + "~Top|ModuleWithCommonIntf>sum".rt -> "also this", + "~Top|ModuleWithCommonIntf".mt -> "inst" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.2 A BlackBox that implements an @instantiable trait should be instantiable as that trait") { + class Top extends Module { + val m: ModuleIntf = Module(new BlackBoxWithCommonIntf) + val d: Definition[ModuleIntf] = m.toDefinition + mark(d.io.in, "gotcha") + mark(d, "module") + } + val expected = List( + "~Top|BlackBoxWithCommonIntf>in".rt -> "gotcha", + "~Top|BlackBoxWithCommonIntf".mt -> "module" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.3 It should be possible to have Vectors of @instantiable traits mixing concrete subclasses") { + class Top extends Module { + val definition = Definition(new ModuleWithCommonIntf("X")) + val insts: Seq[Definition[ModuleIntf]] = Vector( + Module(new ModuleWithCommonIntf("Y")).toDefinition, + Module(new BlackBoxWithCommonIntf).toDefinition, + definition + ) + mark(insts(0).io.in, "foo") + mark(insts(1).io.in, "bar") + mark(insts(2).io.in, "fizz") + } + val expected = List( + "~Top|ModuleWithCommonIntfY>io.in".rt -> "foo", + "~Top|BlackBoxWithCommonIntf>in".rt -> "bar", + "~Top|ModuleWithCommonIntfX>io.in".rt -> "fizz" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } + describe("7: @instantiable and @public should compose with DataView") { + import chisel3.experimental.dataview._ + ignore("7.0: should work on simple Views") { + @instantiable + class MyModule extends RawModule { + val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + val sum = in + 1.U + out := sum + 1.U + @public val foo = in.viewAs[UInt] + @public val bar = sum.viewAs[UInt] + } + class Top extends RawModule { + val foo = IO(Input(UInt(8.W))) + val bar = IO(Output(UInt(8.W))) + val d = Definition(new MyModule) + val i = Instance(d) + i.foo := foo + bar := i.out + mark(d.out, "out") + mark(d.foo, "foo") + mark(d.bar, "bar") + } + val expectedAnnos = List( + "~Top|MyModule>out".rt -> "out", + "~Top|MyModule>in".rt -> "foo", + "~Top|MyModule>sum".rt -> "bar" + ) + val expectedLines = List( + "i.in <= foo", + "bar <= i.out" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- expectedLines) { + text should include (line) + } + for (e <- expectedAnnos.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + ignore("7.1: should work on Aggregate Views that are mapped 1:1") { + import chiselTests.experimental.SimpleBundleDataView._ + @instantiable + class MyModule extends RawModule { + private val a = IO(Input(new BundleA(8))) + private val b = IO(Output(new BundleA(8))) + @public val in = a.viewAs[BundleB] + @public val out = b.viewAs[BundleB] + out := in + } + class Top extends RawModule { + val foo = IO(Input(new BundleB(8))) + val bar = IO(Output(new BundleB(8))) + val d = Definition(new MyModule) + val i = Instance(d) + i.in := foo + bar.bar := i.out.bar + mark(d.in, "in") + mark(d.in.bar, "in_bar") + } + val expectedAnnos = List( + "~Top|MyModule>a".rt -> "in", + "~Top|MyModule>a.foo".rt -> "in_bar", + ) + val expectedLines = List( + "i.a <= foo", + "bar <= i.b.foo" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- expectedLines) { + text should include (line) + } + for (e <- expectedAnnos.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } +} diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala new file mode 100644 index 00000000..23b8c9c0 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.experimental.hierarchy + +import chisel3._ +import chisel3.util.Valid +import chisel3.experimental.hierarchy._ +import chisel3.experimental.BaseModule + +object Examples { + import Annotations._ + @instantiable + class AddOne extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val innerWire = Wire(UInt(32.W)) + innerWire := in + 1.U + out := innerWire + } + @instantiable + class AddOneWithAnnotation extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val innerWire = Wire(UInt(32.W)) + mark(innerWire, "innerWire") + innerWire := in + 1.U + out := innerWire + } + @instantiable + class AddOneWithAbsoluteAnnotation extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val innerWire = Wire(UInt(32.W)) + amark(innerWire, "innerWire") + innerWire := in + 1.U + out := innerWire + } + @instantiable + class AddTwo extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val definition = Definition(new AddOne) + @public val i0: Instance[AddOne] = Instance(definition) + @public val i1: Instance[AddOne] = Instance(definition) + i0.in := in + i1.in := i0.out + out := i1.out + } + @instantiable + class AddTwoMixedModules extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + val definition = Definition(new AddOne) + @public val i0: Instance[AddOne] = Instance(definition) + @public val i1 = Module(new AddOne) + i0.in := in + i1.in := i0.out + out := i1.out + } + @instantiable + class AggregatePortModule extends Module { + @public val io = IO(new Bundle { + val in = Input(UInt(32.W)) + val out = Output(UInt(32.W)) + }) + io.out := io.in + } + @instantiable + class WireContainer { + @public val innerWire = Wire(UInt(32.W)) + } + @instantiable + class AddOneWithInstantiableWire extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val wireContainer = new WireContainer() + wireContainer.innerWire := in + 1.U + out := wireContainer.innerWire + } + @instantiable + class AddOneContainer { + @public val i0 = Module(new AddOne) + } + @instantiable + class AddOneWithInstantiableModule extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val moduleContainer = new AddOneContainer() + moduleContainer.i0.in := in + out := moduleContainer.i0.out + } + @instantiable + class AddOneInstanceContainer { + val definition = Definition(new AddOne) + @public val i0 = Instance(definition) + } + @instantiable + class AddOneWithInstantiableInstance extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val instanceContainer = new AddOneInstanceContainer() + instanceContainer.i0.in := in + out := instanceContainer.i0.out + } + @instantiable + class AddOneContainerContainer { + @public val container = new AddOneContainer + } + @instantiable + class AddOneWithInstantiableInstantiable extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val containerContainer = new AddOneContainerContainer() + containerContainer.container.i0.in := in + out := containerContainer.container.i0.out + } + @instantiable + class Viewer(val y: AddTwo, markPlease: Boolean) { + @public val x = y + if(markPlease) mark(x.i0.innerWire, "first") + } + @instantiable + class ViewerParent(val x: AddTwo, markHere: Boolean, markThere: Boolean) extends Module { + @public val viewer = new Viewer(x, markThere) + if(markHere) mark(viewer.x.i0.innerWire, "second") + } + @instantiable + class MultiVal() extends Module { + @public val (x, y) = (Wire(UInt(3.W)), Wire(UInt(3.W))) + } + @instantiable + class LazyVal() extends Module { + @public val x = Wire(UInt(3.W)) + @public lazy val y = "Hi" + } + case class Parameters(string: String, int: Int) extends IsLookupable + @instantiable + class UsesParameters(p: Parameters) extends Module { + @public val y = p + @public val x = Wire(UInt(3.W)) + } + @instantiable + class HasList() extends Module { + @public val y = List(1, 2, 3) + @public val x = List.fill(3)(Wire(UInt(3.W))) + } + @instantiable + class HasSeq() extends Module { + @public val y = Seq(1, 2, 3) + @public val x = Seq.fill(3)(Wire(UInt(3.W))) + } + @instantiable + class HasOption() extends Module { + @public val x: Option[UInt] = Some(Wire(UInt(3.W))) + } + @instantiable + class HasVec() extends Module { + @public val x = VecInit(1.U, 2.U, 3.U) + } + @instantiable + class HasIndexedVec() extends Module { + val x = VecInit(1.U, 2.U, 3.U) + @public val y = x(1) + } + @instantiable + class HasSubFieldAccess extends Module { + val in = IO(Input(Valid(UInt(8.W)))) + @public val valid = in.valid + @public val bits = in.bits + } + @instantiable + class HasPublicConstructorArgs(@public val int: Int) extends Module { + @public val x = Wire(UInt(3.W)) + } + @instantiable + class InstantiatesHasVec() extends Module { + @public val i0 = Instance(Definition(new HasVec())) + @public val i1 = Module(new HasVec()) + } + @instantiable + class HasUninferredReset() extends Module { + @public val in = IO(Input(UInt(3.W))) + @public val out = IO(Output(UInt(3.W))) + out := RegNext(in) + } +} diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala new file mode 100644 index 00000000..3866bf87 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala @@ -0,0 +1,709 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests +package experimental.hierarchy + +import chisel3._ +import chisel3.experimental.BaseModule +import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public} +import chisel3.util.{DecoupledIO, Valid} + + +// TODO/Notes +// - In backport, clock/reset are not automatically assigned. I think this is fixed in 3.5 +// - CircuitTarget for annotations on the definition are wrong - needs to be fixed. +class InstanceSpec extends ChiselFunSpec with Utils { + import Annotations._ + import Examples._ + describe("0: Instance instantiation") { + it("0.0: name of an instance should be correct") { + class Top extends Module { + val definition = Definition(new AddOne) + val i0 = Instance(definition) + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("inst i0 of AddOne") + } + it("0.1: name of an instanceclone should not error") { + class Top extends Module { + val definition = Definition(new AddTwo) + val i0 = Instance(definition) + val i = i0.i0 // This should not error + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("inst i0 of AddTwo") + } + it("0.2: accessing internal fields through non-generated means is hard to do") { + class Top extends Module { + val definition = Definition(new AddOne) + val i0 = Instance(definition) + //i0.lookup(_.in) // Uncommenting this line will give the following error: + //"You are trying to access a macro-only API. Please use the @public annotation instead." + i0.in + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("inst i0 of AddOne") + } + } + describe("1: Annotations on instances in same chisel compilation") { + it("1.0: should work on a single instance, annotating the instance") { + class Top extends Module { + val definition: Definition[AddOne] = Definition(new AddOne) + val i0: Instance[AddOne] = Instance(definition) + mark(i0, "i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOne".it, "i0")) + } + it("1.1: should work on a single instance, annotating an inner wire") { + class Top extends Module { + val definition: Definition[AddOne] = Definition(new AddOne) + val i0: Instance[AddOne] = Instance(definition) + mark(i0.innerWire, "i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOne>innerWire".rt, "i0.innerWire")) + } + it("1.2: should work on a two nested instances, annotating the instance") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + val i0: Instance[AddTwo] = Instance(definition) + mark(i0.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddTwo/i0:AddOne".it, "i0.i0")) + } + it("1.3: should work on a two nested instances, annotating the inner wire") { + class Top extends Module { + val definition: Definition[AddTwo] = Definition(new AddTwo) + val i0: Instance[AddTwo] = Instance(definition) + mark(i0.i0.innerWire, "i0.i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddTwo/i0:AddOne>innerWire".rt, "i0.i0.innerWire")) + } + it("1.4: should work on a nested module in an instance, annotating the module") { + class Top extends Module { + val definition: Definition[AddTwoMixedModules] = Definition(new AddTwoMixedModules) + val i0: Instance[AddTwoMixedModules] = Instance(definition) + mark(i0.i1, "i0.i1") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddTwoMixedModules/i1:AddOne_2".it, "i0.i1")) + } + it("1.5: should work on an instantiable container, annotating a wire") { + class Top extends Module { + val definition: Definition[AddOneWithInstantiableWire] = Definition(new AddOneWithInstantiableWire) + val i0: Instance[AddOneWithInstantiableWire] = Instance(definition) + mark(i0.wireContainer.innerWire, "i0.innerWire") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableWire>innerWire".rt, "i0.innerWire")) + } + it("1.6: should work on an instantiable container, annotating a module") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableModule) + val i0 = Instance(definition) + mark(i0.moduleContainer.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableModule/i0:AddOne".it, "i0.i0")) + } + it("1.7: should work on an instantiable container, annotating an instance") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstance) + val i0 = Instance(definition) + mark(i0.instanceContainer.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstance/i0:AddOne".it, "i0.i0")) + } + it("1.8: should work on an instantiable container, annotating an instantiable container's module") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstantiable) + val i0 = Instance(definition) + mark(i0.containerContainer.container.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0")) + } + it("1.9: should work on public member which references public member of another instance") { + class Top extends Module { + val definition = Definition(new AddOneWithInstantiableInstantiable) + val i0 = Instance(definition) + mark(i0.containerContainer.container.i0, "i0.i0") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0")) + } + it("1.10: should work for targets on definition to have correct circuit name"){ + class Top extends Module { + val definition = Definition(new AddOneWithAnnotation) + val i0 = Instance(definition) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire")) + } + } + describe("2: Annotations on designs not in the same chisel compilation") { + it("2.0: should work on an innerWire, marked in a different compilation") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Instance(Definition(new ViewerParent(x, false, true))) + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "first")) + } + it("2.1: should work on an innerWire, marked in a different compilation, in instanced instantiable") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Instance(Definition(new ViewerParent(x, true, false))) + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "second")) + } + it("2.2: should work on an innerWire, marked in a different compilation, in instanced module") { + val first = elaborateAndGetModule(new AddTwo) + class Top(x: AddTwo) extends Module { + val parent = Instance(Definition(new ViewerParent(x, false, false))) + mark(parent.viewer.x.i0.innerWire, "third") + } + val (_, annos) = getFirrtlAndAnnos(new Top(first)) + annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "third")) + } + } + describe("3: @public") { + it("3.0: should work on multi-vals") { + class Top() extends Module { + val mv = Instance(Definition(new MultiVal())) + mark(mv.x, "mv.x") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/mv:MultiVal>x".rt, "mv.x")) + } + it("3.1: should work on lazy vals") { + class Top() extends Module { + val lv = Instance(Definition(new LazyVal())) + mark(lv.x, lv.y) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/lv:LazyVal>x".rt, "Hi")) + } + it("3.2: should work on islookupables") { + class Top() extends Module { + val p = Parameters("hi", 0) + val up = Instance(Definition(new UsesParameters(p))) + mark(up.x, up.y.string + up.y.int) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/up:UsesParameters>x".rt, "hi0")) + } + it("3.3: should work on lists") { + class Top() extends Module { + val i = Instance(Definition(new HasList())) + mark(i.x(1), i.y(1).toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasList>x_1".rt, "2")) + } + it("3.4: should work on seqs") { + class Top() extends Module { + val i = Instance(Definition(new HasSeq())) + mark(i.x(1), i.y(1).toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasSeq>x_1".rt, "2")) + } + it("3.5: should work on options") { + class Top() extends Module { + val i = Instance(Definition(new HasOption())) + i.x.map(x => mark(x, "x")) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasOption>x".rt, "x")) + } + it("3.6: should work on vecs") { + class Top() extends Module { + val i = Instance(Definition(new HasVec())) + mark(i.x, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasVec>x".rt, "blah")) + } + it("3.7: should work on statically indexed vectors external to module") { + class Top() extends Module { + val i = Instance(Definition(new HasVec())) + mark(i.x(1), "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasVec>x[1]".rt, "blah")) + } + it("3.8: should work on statically indexed vectors internal to module") { + class Top() extends Module { + val i = Instance(Definition(new HasIndexedVec())) + mark(i.y, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasIndexedVec>x[1]".rt, "blah")) + } + it("3.9: should work on accessed subfields of aggregate ports") { + class Top extends Module { + val input = IO(Input(Valid(UInt(8.W)))) + val i = Instance(Definition(new HasSubFieldAccess)) + i.valid := input.valid + i.bits := input.bits + mark(i.valid, "valid") + mark(i.bits, "bits") + } + val expected = List( + "~Top|Top/i:HasSubFieldAccess>in.valid".rt -> "valid", + "~Top|Top/i:HasSubFieldAccess>in.bits".rt -> "bits" + ) + val lines = List( + "i.in.valid <= input.valid", + "i.in.bits <= input.bits" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- lines) { + text should include (line) + } + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + ignore("3.10: should work on vals in constructor arguments") { + class Top() extends Module { + val i = Instance(Definition(new HasPublicConstructorArgs(10))) + //mark(i.x, i.int.toString) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:HasPublicConstructorArgs>x".rt, "10")) + } + } + describe("4: toInstance") { + it("4.0: should work on modules") { + class Top() extends Module { + val i = Module(new AddOne()) + f(i.toInstance) + } + def f(i: Instance[AddOne]): Unit = mark(i.innerWire, "blah") + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "blah")) + } + it("4.1: should work on isinstantiables") { + class Top() extends Module { + val i = Module(new AddTwo()) + val v = new Viewer(i, false) + mark(f(v.toInstance), "blah") + } + def f(i: Instance[Viewer]): Data = i.x.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("4.2: should work on seqs of modules") { + class Top() extends Module { + val is = Seq(Module(new AddTwo()), Module(new AddTwo())).map(_.toInstance) + mark(f(is), "blah") + } + def f(i: Seq[Instance[AddTwo]]): Data = i.head.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("4.3: should work on seqs of isInstantiables") { + class Top() extends Module { + val i = Module(new AddTwo()) + val vs = Seq(new Viewer(i, false), new Viewer(i, false)).map(_.toInstance) + mark(f(vs), "blah") + } + def f(i: Seq[Instance[Viewer]]): Data = i.head.x.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("4.2: should work on options of modules") { + class Top() extends Module { + val is: Option[Instance[AddTwo]] = Some(Module(new AddTwo())).map(_.toInstance) + mark(f(is), "blah") + } + def f(i: Option[Instance[AddTwo]]): Data = i.get.i0.innerWire + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + } + describe("5: Absolute Targets should work as expected") { + it("5.0: toAbsoluteTarget on a port of an instance") { + class Top() extends Module { + val i = Instance(Definition(new AddTwo())) + amark(i.in, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwo>in".rt, "blah")) + } + it("5.1: toAbsoluteTarget on a subinstance's data within an instance") { + class Top() extends Module { + val i = Instance(Definition(new AddTwo())) + amark(i.i0.innerWire, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwo/i0:AddOne>innerWire".rt, "blah")) + } + it("5.2: toAbsoluteTarget on a submodule's data within an instance") { + class Top() extends Module { + val i = Instance(Definition(new AddTwoMixedModules())) + amark(i.i1.in, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwoMixedModules/i1:AddOne_2>in".rt, "blah")) + } + it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance") { + class Top() extends Module { + val i = Instance(Definition(new InstantiatesHasVec())) + amark(i.i1.x.head, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah")) + } + it("5.4: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance, ILit") { + class MyBundle extends Bundle { val x = UInt(3.W) } + @instantiable + class HasVec() extends Module { + @public val x = Wire(Vec(3, new MyBundle())) + } + @instantiable + class InstantiatesHasVec() extends Module { + @public val i0 = Instance(Definition(new HasVec())) + @public val i1 = Module(new HasVec()) + } + class Top() extends Module { + val i = Instance(Definition(new InstantiatesHasVec())) + amark(i.i1.x.head.x, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_2>x[0].x".rt, "blah")) + } + it("5.5: toAbsoluteTarget on a subinstance") { + class Top() extends Module { + val i = Instance(Definition(new AddTwo())) + amark(i.i1, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwo/i1:AddOne".it, "blah")) + } + it("5.6: should work for absolute targets on definition to have correct circuit name"){ + class Top extends Module { + val definition = Definition(new AddOneWithAbsoluteAnnotation) + val i0 = Instance(definition) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|AddOneWithAbsoluteAnnotation>innerWire".rt, "innerWire")) + } + } + describe("6: @instantiable traits should work as expected") { + class MyBundle extends Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + } + @instantiable + trait ModuleIntf extends BaseModule { + @public val io = IO(new MyBundle) + } + @instantiable + class ModuleWithCommonIntf(suffix: String = "") extends Module with ModuleIntf { + override def desiredName: String = super.desiredName + suffix + @public val sum = io.in + 1.U + + io.out := sum + } + class BlackBoxWithCommonIntf extends BlackBox with ModuleIntf + + it("6.0: A Module that implements an @instantiable trait should be instantiable as that trait") { + class Top extends Module { + val i: Instance[ModuleIntf] = Instance(Definition(new ModuleWithCommonIntf)) + mark(i.io.in, "gotcha") + mark(i, "inst") + } + val expected = List( + "~Top|Top/i:ModuleWithCommonIntf>io.in".rt -> "gotcha", + "~Top|Top/i:ModuleWithCommonIntf".it -> "inst" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.1 An @instantiable Module that implements an @instantiable trait should be able to use extension methods from both") { + class Top extends Module { + val i: Instance[ModuleWithCommonIntf] = Instance(Definition(new ModuleWithCommonIntf)) + mark(i.io.in, "gotcha") + mark(i.sum, "also this") + mark(i, "inst") + } + val expected = List( + "~Top|Top/i:ModuleWithCommonIntf>io.in".rt -> "gotcha", + "~Top|Top/i:ModuleWithCommonIntf>sum".rt -> "also this", + "~Top|Top/i:ModuleWithCommonIntf".it -> "inst" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.2 A BlackBox that implements an @instantiable trait should be instantiable as that trait") { + class Top extends Module { + val i: Instance[ModuleIntf] = Module(new BlackBoxWithCommonIntf).toInstance + mark(i.io.in, "gotcha") + mark(i, "module") + } + val expected = List( + "~Top|BlackBoxWithCommonIntf>in".rt -> "gotcha", + "~Top|BlackBoxWithCommonIntf".mt -> "module" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("6.3 It should be possible to have Vectors of @instantiable traits mixing concrete subclasses") { + class Top extends Module { + val proto = Definition(new ModuleWithCommonIntf("X")) + val insts: Seq[Instance[ModuleIntf]] = Vector( + Module(new ModuleWithCommonIntf("Y")).toInstance, + Module(new BlackBoxWithCommonIntf).toInstance, + Instance(proto) + ) + mark(insts(0).io.in, "foo") + mark(insts(1).io.in, "bar") + mark(insts(2).io.in, "fizz") + } + val expected = List( + "~Top|ModuleWithCommonIntfY>io.in".rt -> "foo", + "~Top|BlackBoxWithCommonIntf>in".rt -> "bar", + "~Top|Top/insts_2:ModuleWithCommonIntfX>io.in".rt -> "fizz" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } + // TODO don't forget to test this with heterogeneous Views (eg. viewing a tuple of a port and non-port as a single Bundle) + describe("7: @instantiable and @public should compose with DataView") { + import chisel3.experimental.dataview._ + it("7.0: should work on simple Views") { + @instantiable + class MyModule extends RawModule { + val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + val sum = in + 1.U + out := sum + 1.U + @public val foo = in.viewAs[UInt] + @public val bar = sum.viewAs[UInt] + } + class Top extends RawModule { + val foo = IO(Input(UInt(8.W))) + val bar = IO(Output(UInt(8.W))) + val i = Instance(Definition(new MyModule)) + i.foo := foo + bar := i.out + mark(i.out, "out") + mark(i.foo, "foo") + mark(i.bar, "bar") + } + val expectedAnnos = List( + "~Top|Top/i:MyModule>out".rt -> "out", + "~Top|Top/i:MyModule>in".rt -> "foo", + "~Top|Top/i:MyModule>sum".rt -> "bar" + ) + val expectedLines = List( + "i.in <= foo", + "bar <= i.out" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- expectedLines) { + text should include (line) + } + for (e <- expectedAnnos.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + + ignore("7.1: should work on Aggregate Views") { + import chiselTests.experimental.FlatDecoupledDataView._ + type RegDecoupled = DecoupledIO[FizzBuzz] + @instantiable + class MyModule extends RawModule { + private val a = IO(Flipped(new FlatDecoupled)) + private val b = IO(new FlatDecoupled) + @public val enq = a.viewAs[RegDecoupled] + @public val deq = b.viewAs[RegDecoupled] + @public val enq_valid = enq.valid // Also return a subset of the view + deq <> enq + } + class Top extends RawModule { + val foo = IO(Flipped(new RegDecoupled(new FizzBuzz))) + val bar = IO(new RegDecoupled(new FizzBuzz)) + val i = Instance(Definition(new MyModule)) + i.enq <> foo + i.enq_valid := foo.valid // Make sure connections also work for @public on elements of a larger Aggregate + i.deq.ready := bar.ready + bar.valid := i.deq.valid + bar.bits := i.deq.bits + mark(i.enq, "enq") + mark(i.enq.bits, "enq.bits") + mark(i.deq.bits.fizz, "deq.bits.fizz") + mark(i.enq_valid, "enq_valid") + } + val expectedAnnos = List( + "~Top|Top/i:MyModule>a".rt -> "enq", // Not split, checks 1:1 + "~Top|Top/i:MyModule>a.fizz".rt -> "enq.bits", // Split, checks non-1:1 inner Aggregate + "~Top|Top/i:MyModule>a.buzz".rt -> "enq.bits", + "~Top|Top/i:MyModule>b.fizz".rt -> "deq.bits.fizz", // Checks 1 inner Element + "~Top|Top/i:MyModule>a.valid".rt -> "enq_valid" + ) + val expectedLines = List( + "i.a.valid <= foo.valid", + "foo.ready <= i.a.ready", + "i.a.fizz <= foo.bits.fizz", + "i.a.buzz <= foo.bits.buzz", + "bar.valid <= i.b.valid", + "i.b.ready <= bar.ready", + "bar.bits.fizz <= i.b.fizz", + "bar.bits.buzz <= i.b.buzz", + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- expectedLines) { + text should include (line) + } + for (e <- expectedAnnos.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + + it("7.2: should work on views of views") { + import chiselTests.experimental.SimpleBundleDataView._ + @instantiable + class MyModule extends RawModule { + private val a = IO(Input(UInt(8.W))) + private val b = IO(Output(new BundleA(8))) + @public val in = a.viewAs[UInt].viewAs[UInt] + @public val out = b.viewAs[BundleB].viewAs[BundleA].viewAs[BundleB] + out.bar := in + } + class Top extends RawModule { + val foo = IO(Input(UInt(8.W))) + val bar = IO(Output(new BundleB(8))) + val i = Instance(Definition(new MyModule)) + i.in := foo + bar := i.out + bar.bar := i.out.bar + mark(i.in, "in") + mark(i.out.bar, "out_bar") + } + val expected = List( + "~Top|Top/i:MyModule>a".rt -> "in", + "~Top|Top/i:MyModule>b.foo".rt -> "out_bar", + ) + val lines = List( + "i.a <= foo", + "bar.bar <= i.b.foo" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- lines) { + text should include (line) + } + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + + it("7.3: should work with DataView + implicit conversion") { + import chiselTests.experimental.SeqToVec._ + @instantiable + class MyModule extends RawModule { + private val a = IO(Input(UInt(8.W))) + private val b = IO(Output(UInt(8.W))) + @public val ports = Seq(a, b) + b := a + } + class Top extends RawModule { + val foo = IO(Input(UInt(8.W))) + val bar = IO(Output(UInt(8.W))) + val i = Instance(Definition(new MyModule)) + i.ports <> Seq(foo, bar) + mark(i.ports, "i.ports") + } + val expected = List( + // Not 1:1 so will get split out + "~Top|Top/i:MyModule>a".rt -> "i.ports", + "~Top|Top/i:MyModule>b".rt -> "i.ports", + ) + val lines = List( + "i.a <= foo", + "bar <= i.b" + ) + val (chirrtl, annos) = getFirrtlAndAnnos(new Top) + val text = chirrtl.serialize + for (line <- lines) { + text should include (line) + } + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } + + describe("8: @instantiable and @public should compose with CloneModuleAsRecord") { + it("8.0: it should support @public on a CMAR Record in Definitions") { + @instantiable + class HasCMAR extends Module { + @public val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + @public val m = Module(new AggregatePortModule) + @public val c = experimental.CloneModuleAsRecord(m) + } + class Top extends Module { + val d = Definition(new HasCMAR) + mark(d.c("io"), "c.io") + val bun = d.c("io").asInstanceOf[Record] + mark(bun.elements("out"), "c.io.out") + } + val expected = List( + "~Top|HasCMAR/c:AggregatePortModule>io".rt -> "c.io", + "~Top|HasCMAR/c:AggregatePortModule>io.out".rt -> "c.io.out" + + ) + val (_, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + it("8.1: it should support @public on a CMAR Record in Instances") { + @instantiable + class HasCMAR extends Module { + @public val in = IO(Input(UInt(8.W))) + @public val out = IO(Output(UInt(8.W))) + @public val m = Module(new AggregatePortModule) + @public val c = experimental.CloneModuleAsRecord(m) + } + class Top extends Module { + val i = Instance(Definition(new HasCMAR)) + mark(i.c("io"), "i.c.io") + val bun = i.c("io").asInstanceOf[Record] + mark(bun.elements("out"), "i.c.io.out") + } + val expected = List( + "~Top|Top/i:HasCMAR/c:AggregatePortModule>io".rt -> "i.c.io", + "~Top|Top/i:HasCMAR/c:AggregatePortModule>io.out".rt -> "i.c.io.out" + + ) + val (_, annos) = getFirrtlAndAnnos(new Top) + for (e <- expected.map(MarkAnnotation.tupled)) { + annos should contain (e) + } + } + } +} + diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala b/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala new file mode 100644 index 00000000..a2e51765 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.experimental.hierarchy + +import chisel3._ +import _root_.firrtl.annotations._ +import chisel3.stage.{ChiselCircuitAnnotation, CircuitSerializationAnnotation, DesignAnnotation} +import chiselTests.ChiselRunners +import firrtl.stage.FirrtlCircuitAnnotation +import org.scalatest.matchers.should.Matchers + +trait Utils extends ChiselRunners with chiselTests.Utils with Matchers { + import Annotations._ + // TODO promote to standard API (in FIRRTL) and perhaps even implement with a macro + implicit class Str2RefTarget(str: String) { + def rt: ReferenceTarget = Target.deserialize(str).asInstanceOf[ReferenceTarget] + def it: InstanceTarget = Target.deserialize(str).asInstanceOf[InstanceTarget] + def mt: ModuleTarget = Target.deserialize(str).asInstanceOf[ModuleTarget] + def ct: CircuitTarget = Target.deserialize(str).asInstanceOf[CircuitTarget] + } +} |
