From c0b954446cdbc85ae8e73c9ddcb5564763036037 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Fri, 4 Mar 2022 16:44:45 -0800 Subject: [PATCH] Support BlackBoxes in D/I Also delete an errant println in InstanceSpec --- core/src/main/scala/chisel3/Module.scala | 34 +++++++-- .../experimental/hierarchy/Lookupable.scala | 7 +- .../experimental/hierarchy/Examples.scala | 7 ++ .../experimental/hierarchy/InstanceSpec.scala | 69 ++++++++++++++++++- 4 files changed, 107 insertions(+), 10 deletions(-) diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index ed3235046bc..84139630da0 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -248,8 +248,14 @@ package internal { } // Maps proto ports to module clone's ports private[chisel3] lazy val ioMap: Map[Data, Data] = { - val name2Port = getPorts.elements - getProto.getChiselPorts.map { case (name, data) => data -> name2Port(name) }.toMap + getProto match { + // BlackBox needs special handling for its pseduo-io Bundle + case protoBB: BlackBox => + Map(protoBB._io.get -> getPorts.elements("io")) + case _ => + val name2Port = getPorts.elements + getProto.getChiselPorts.map { case (name, data) => data -> name2Port(name) }.toMap + } } // This module doesn't actually exist in the FIRRTL so no initialization to do private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = () @@ -267,7 +273,17 @@ package internal { case bad => throwException(s"Internal Error! Cloned-module Record $record has unexpected ref $bad") } // Set both the record and the module to have the same instance name - record.setRef(ModuleCloneIO(getProto, instName), force = true) // force because we did .forceName first + val ref = ModuleCloneIO(getProto, instName) + record.setRef(ref, force = true) // force because we did .forceName first + getProto match { + // BlackBox needs special handling for its pseduo-io Bundle + case _: BlackBox => + // Override the io Bundle's ref so that it thinks it is the top for purposes of + // generating FIRRTL + record.elements("io").setRef(ref, force = true) + case _ => // Do nothing + } + this.setRef(Ref(instName)) } } @@ -329,8 +345,8 @@ package internal { * @note These are not true Data (the Record doesn't correspond to anything in the emitted * FIRRTL yet its elements *do*) so have some very specialized behavior. */ - private[chisel3] class ClonePorts(elts: Data*)(implicit compileOptions: CompileOptions) extends Record { - val elements = ListMap(elts.map(d => d.instanceName -> d.cloneTypeFull): _*) + private[chisel3] class ClonePorts(elts: (String, Data)*)(implicit compileOptions: CompileOptions) extends Record { + val elements = ListMap(elts.map { case (name, d) => name -> d.cloneTypeFull }: _*) def apply(field: String) = elements(field) override def cloneType = (new ClonePorts(elts: _*)).asInstanceOf[this.type] } @@ -351,12 +367,18 @@ package internal { // Fake Module to serve as the _parent of the cloned ports // We don't create this inside the ModuleClone because we need the ref to be set by the // currentModule (and not clonePorts) - val clonePorts = new ClonePorts(proto.getModulePorts: _*) + val clonePorts = proto match { + // BlackBox needs special handling for its pseduo-io Bundle + case b: BlackBox => + new ClonePorts(proto.getChiselPorts :+ ("io" -> b._io.get): _*) + case _ => new ClonePorts(proto.getChiselPorts: _*) + } clonePorts.bind(PortBinding(cloneParent)) clonePorts.setAllParents(Some(cloneParent)) cloneParent._portsRecord = clonePorts // Normally handled during Module construction but ClonePorts really lives in its parent's parent if (!compileOptions.explicitInvalidate) { + // FIXME This almost certainly doesn't work since clonePorts is not a real thing... pushCommand(DefInvalid(sourceInfo, clonePorts.ref)) } if (proto.isInstanceOf[Module]) { diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala index bc94f95dbfe..60290f83120 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala @@ -10,7 +10,7 @@ import scala.annotation.implicitNotFound import scala.collection.mutable.HashMap import chisel3._ import chisel3.experimental.dataview.{isView, reify, reifySingleData} -import chisel3.internal.firrtl.{Arg, ILit, Index, Slot, ULit} +import chisel3.internal.firrtl.{Arg, ILit, Index, ModuleIO, Slot, ULit} import chisel3.internal.{throwException, AggregateViewBinding, Builder, ChildBinding, ViewBinding, ViewParent} /** Represents lookup typeclass to determine how a value accessed from an original IsInstantiable @@ -123,8 +123,8 @@ object Lookupable { def unrollCoordinates(res: List[Arg], d: Data): (List[Arg], Data) = d.binding.get match { case ChildBinding(parent) => d.getRef match { - case arg @ (_: Slot | _: Index) => unrollCoordinates(arg :: res, parent) - case other => err(s"Unroll coordinates failed for '$arg'! Unexpected arg '$other'") + case arg @ (_: Slot | _: Index | _: ModuleIO) => unrollCoordinates(arg :: res, parent) + case other => err(s"unrollCoordinates failed for '$arg'! Unexpected arg '$other'") } case _ => (res, d) } @@ -135,6 +135,7 @@ object Lookupable { val next = (coor.head, d) match { case (Slot(_, name), rec: Record) => rec.elements(name) case (Index(_, ILit(n)), vec: Vec[_]) => vec.apply(n.toInt) + case (ModuleIO(_, name), rec: Record) => rec.elements(name) case (arg, _) => err(s"Unexpected Arg '$arg' applied to '$d'! Root was '$start'.") } applyCoordinates(coor.tail, next) diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala index aff0a7710d9..fa26cbdec6f 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala @@ -47,6 +47,13 @@ object Examples { val addOneDef = Seq.fill(3)(Definition(new AddOne)) out := in + 1.U } + @instantiable + class AddOneBlackBox extends BlackBox { + @public val io = IO(new Bundle { + val in = Input(UInt(32.W)) + val out = Output(UInt(32.W)) + }) + } @instantiable class AddTwo extends Module { diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala index 407a7237311..8d8f7ea506a 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala @@ -43,6 +43,29 @@ class InstanceSpec extends ChiselFunSpec with Utils { val (chirrtl, _) = getFirrtlAndAnnos(new Top) chirrtl.serialize should include("inst i0 of AddOne") } + it("0.3: BlackBoxes should be supported") { + class Top extends Module { + val in = IO(Input(UInt(32.W))) + val out = IO(Output(UInt(32.W))) + val io = IO(new Bundle { + val in = Input(UInt(32.W)) + val out = Output(UInt(32.W)) + }) + val definition = Definition(new AddOneBlackBox) + val i0 = Instance(definition) + val i1 = Instance(definition) + i0.io.in := in + out := i0.io.out + io <> i1.io + } + val chirrtl = getFirrtlAndAnnos(new Top)._1.serialize + chirrtl should include("inst i0 of AddOneBlackBox") + chirrtl should include("inst i1 of AddOneBlackBox") + chirrtl should include("i0.in <= in") + chirrtl should include("out <= i0.out") + chirrtl should include("i1.in <= io.in") + chirrtl should include("io.out <= i1.out") + } } describe("1: Annotations on instances in same chisel compilation") { it("1.0: should work on a single instance, annotating the instance") { @@ -338,7 +361,6 @@ class InstanceSpec extends ChiselFunSpec with Utils { mark(i.syncReadMem, "SyncReadMem") } val (_, annos) = getFirrtlAndAnnos(new Top) - annos.foreach { x => println(x.serialize) } annos should contain(MarkAnnotation("~Top|Top/i:HasMems>mem".rt, "Mem")) annos should contain(MarkAnnotation("~Top|Top/i:HasMems>syncReadMem".rt, "SyncReadMem")) } @@ -717,6 +739,51 @@ class InstanceSpec extends ChiselFunSpec with Utils { annos should contain(e) } } + + it("7.4: should work on Views of BlackBoxes") { + @instantiable + class MyBlackBox extends BlackBox { + @public val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + @public val innerView = io.viewAs + @public val foo = io.in.viewAs[UInt] + @public val bar = io.out.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 MyBlackBox)) + val outerView = i.io.viewAs + i.foo := foo + bar := i.bar + mark(i.foo, "i.foo") + mark(i.bar, "i.bar") + mark(i.innerView.in, "i.innerView.in") + mark(outerView.out, "outerView.out") + } + val inst = "~Top|Top/i:MyBlackBox" + val expectedAnnos = List( + s"$inst>in".rt -> "i.foo", + s"$inst>out".rt -> "i.bar", + s"$inst>in".rt -> "i.innerView.in", + s"$inst>out".rt -> "outerView.out" + ) + 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) + } + } + } describe("8: @instantiable and @public should compose with CloneModuleAsRecord") {