Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support BlackBoxes in D/I #2438

Merged
merged 1 commit into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions core/src/main/scala/chisel3/Module.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ()
Expand All @@ -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))
}
}
Expand Down Expand Up @@ -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]
}
Expand All @@ -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]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down Expand Up @@ -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"))
}
Expand Down Expand Up @@ -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") {
Expand Down