diff options
Diffstat (limited to 'src/test/scala')
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] + } +} |
