summaryrefslogtreecommitdiff
path: root/core/src/main/scala/chisel3/internal
diff options
context:
space:
mode:
authorAdam Izraelevitz2021-09-05 12:11:32 -0700
committerGitHub2021-09-05 12:11:32 -0700
commit9fa8da227569455a77596355aeb114f9c164510a (patch)
tree3be3dd579fcc7ae83297d3c28020e97417a6b984 /core/src/main/scala/chisel3/internal
parent7fb2c1ebc23ca07e5de6416a284e1be1b62a48ac (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 'core/src/main/scala/chisel3/internal')
-rw-r--r--core/src/main/scala/chisel3/internal/Binding.scala5
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala49
-rw-r--r--core/src/main/scala/chisel3/internal/firrtl/IR.scala33
3 files changed, 68 insertions, 19 deletions
diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala
index 6f4ab4b0..a0dcc20c 100644
--- a/core/src/main/scala/chisel3/internal/Binding.scala
+++ b/core/src/main/scala/chisel3/internal/Binding.scala
@@ -129,6 +129,11 @@ private[chisel3] case class ViewBinding(target: Element) extends UnconstrainedBi
private[chisel3] case class AggregateViewBinding(childMap: Map[Data, Element], target: Option[Data]) extends UnconstrainedBinding
+/** Binding for Data's returned from accessing an Instance/Definition members, if not readable/writable port */
+private[chisel3] case object CrossModuleBinding extends TopBinding {
+ def location = None
+}
+
sealed trait LitBinding extends UnconstrainedBinding with ReadOnlyBinding
// Literal binding attached to a element that is not part of a Bundle.
case class ElementLitBinding(litArg: LitArg) extends LitBinding
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index f7306d5d..1d15247d 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -6,6 +6,7 @@ import scala.util.DynamicVariable
import scala.collection.mutable.ArrayBuffer
import chisel3._
import chisel3.experimental._
+import chisel3.experimental.hierarchy.Instance
import chisel3.internal.firrtl._
import chisel3.internal.naming._
import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget}
@@ -19,6 +20,7 @@ import scala.collection.mutable
private[chisel3] class Namespace(keywords: Set[String]) {
private val names = collection.mutable.HashMap[String, Long]()
+ def copyTo(other: Namespace): Unit = names.foreach { case (s: String, l: Long) => other.names(s) = l }
for (keyword <- keywords)
names(keyword) = 1
@@ -87,6 +89,9 @@ private[chisel3] trait HasId extends InstanceId {
private[chisel3] def _onModuleClose: Unit = {}
private[chisel3] var _parent: Option[BaseModule] = Builder.currentModule
+ // Set if the returned top-level module of a nested call to the Chisel Builder, see Definition.apply
+ private[chisel3] var _circuit: Option[BaseModule] = None
+
private[chisel3] val _id: Long = Builder.idGen.next
// TODO: remove this, but its removal seems to cause a nasty Scala compiler crash.
@@ -216,7 +221,7 @@ private[chisel3] trait HasId extends InstanceId {
private[chisel3] def getRef: Arg = _ref.get
private[chisel3] def getOptionRef: Option[Arg] = _ref
- private def localName(c: Component): String = _ref match {
+ private def refName(c: Component): String = _ref match {
case Some(arg) => arg fullName c
case None => computeName(None, None).get
}
@@ -232,11 +237,13 @@ private[chisel3] trait HasId extends InstanceId {
// Implementation of public methods.
def instanceName: String = _parent match {
- case Some(ViewParent) => reifyTarget.map(_.instanceName).getOrElse(this.localName(ViewParent.fakeComponent))
- case Some(p) => p._component match {
- case Some(c) => localName(c)
- case None => throwException("signalName/pathName should be called after circuit elaboration")
- }
+ case Some(ViewParent) => reifyTarget.map(_.instanceName).getOrElse(this.refName(ViewParent.fakeComponent))
+ case Some(p) =>
+ (p._component, this) match {
+ case (Some(c), _) => refName(c)
+ case (None, d: Data) if d.topBindingOpt == Some(CrossModuleBinding) => _ref.get.localName
+ case (None, _) => throwException(s"signalName/pathName should be called after circuit elaboration: $this, ${_parent}")
+ }
case None => throwException("this cannot happen")
}
def pathName: String = _parent match {
@@ -256,7 +263,10 @@ private[chisel3] trait HasId extends InstanceId {
}
// TODO Should this be public?
protected def circuitName: String = _parent match {
- case None => instanceName
+ case None => _circuit match {
+ case None => instanceName
+ case Some(o) => o.circuitName
+ }
case Some(ViewParent) => reifyParent.circuitName
case Some(p) => p.circuitName
}
@@ -296,8 +306,12 @@ private[chisel3] trait NamedComponent extends HasId {
val name = this.instanceName
if (!validComponentName(name)) throwException(s"Illegal component name: $name (note: literals are illegal)")
import _root_.firrtl.annotations.{Target, TargetToken}
+ val root = _parent.map {
+ case ViewParent => reifyParent
+ case other => other
+ }.get.toTarget // All NamedComponents will have a parent, only the top module can have None here
Target.toTargetTokens(name).toList match {
- case TargetToken.Ref(r) :: components => ReferenceTarget(this.circuitName, this.parentModName, Nil, r, components)
+ case TargetToken.Ref(r) :: components => root.ref(r).copy(component = components)
case other =>
throw _root_.firrtl.annotations.Target.NamedException(s"Cannot convert $name into [[ReferenceTarget]]: $other")
}
@@ -354,6 +368,8 @@ private[chisel3] class DynamicContext(val annotationSeq: AnnotationSeq) {
var currentReset: Option[Reset] = None
val errors = new ErrorLog
val namingStack = new NamingStack
+ // Used to indicate if this is the top-level module of full elaboration, or from a Definition
+ var inDefinition: Boolean = false
}
private[chisel3] object Builder extends LazyLogging {
@@ -368,6 +384,11 @@ private[chisel3] object Builder extends LazyLogging {
dynamicContextVar.value.get
}
+ // Returns the current dynamic context
+ def captureContext(): DynamicContext = dynamicContext
+ // Sets the current dynamic contents
+ def restoreContext(dc: DynamicContext) = dynamicContextVar.value = Some(dc)
+
// Ensure we have a thread-specific ChiselContext
private val chiselContext = new ThreadLocal[ChiselContext]{
override def initialValue: ChiselContext = {
@@ -563,6 +584,12 @@ private[chisel3] object Builder extends LazyLogging {
dynamicContext.currentReset = newReset
}
+ def inDefinition: Boolean = {
+ dynamicContextVar.value
+ .map(_.inDefinition)
+ .getOrElse(false)
+ }
+
// This should only be used for testing, must be true outside of Builder context
def allowReflectiveAutoCloneType: Boolean = {
dynamicContextVar.value
@@ -632,6 +659,10 @@ private[chisel3] object Builder extends LazyLogging {
* (Note: Map is Iterable[Tuple2[_,_]] and thus excluded)
*/
def nameRecursively(prefix: String, nameMe: Any, namer: (HasId, String) => Unit): Unit = nameMe match {
+ case (id: Instance[_]) => id.cloned match {
+ case Right(m: internal.BaseModule.ModuleClone[_]) => namer(m.getPorts, prefix)
+ case _ =>
+ }
case (id: HasId) => namer(id, prefix)
case Some(elt) => nameRecursively(prefix, elt, namer)
case (iter: Iterable[_]) if iter.hasDefiniteSize =>
@@ -696,7 +727,7 @@ private[chisel3] object Builder extends LazyLogging {
renames
}
- private [chisel3] def build[T <: RawModule](f: => T, dynamicContext: DynamicContext): (Circuit, T) = {
+ private [chisel3] def build[T <: BaseModule](f: => T, dynamicContext: DynamicContext): (Circuit, T) = {
dynamicContextVar.withValue(Some(dynamicContext)) {
ViewParent // Must initialize the singleton in a Builder context or weird things can happen
// in tiny designs/testcases that never access anything in chisel3.internal
diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
index f8a3cf7f..0b568548 100644
--- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -65,13 +65,19 @@ object PrimOp {
}
abstract class Arg {
- def fullName(ctx: Component): String = name
+ def localName: String = name
+ def contextualName(ctx: Component): String = name
+ def fullName(ctx: Component): String = contextualName(ctx)
def name: String
}
case class Node(id: HasId) extends Arg {
- override def fullName(ctx: Component): String = id.getOptionRef match {
- case Some(arg) => arg.fullName(ctx)
+ override def contextualName(ctx: Component): String = id.getOptionRef match {
+ case Some(arg) => arg.contextualName(ctx)
+ case None => id.instanceName
+ }
+ override def localName: String = id.getOptionRef match {
+ case Some(arg) => arg.localName
case None => id.instanceName
}
def name: String = id.getOptionRef match {
@@ -83,7 +89,7 @@ case class Node(id: HasId) extends Arg {
abstract class LitArg(val num: BigInt, widthArg: Width) extends Arg {
private[chisel3] def forcedWidth = widthArg.known
private[chisel3] def width: Width = if (forcedWidth) widthArg else Width(minWidth)
- override def fullName(ctx: Component): String = name
+ override def contextualName(ctx: Component): String = name
// Ensure the node representing this LitArg has a ref to it and a literal binding.
def bindLitArg[T <: Element](elem: T): T = {
elem.bind(ElementLitBinding(this))
@@ -167,7 +173,7 @@ case class Ref(name: String) extends Arg
* @param name the name of the port
*/
case class ModuleIO(mod: BaseModule, name: String) extends Arg {
- override def fullName(ctx: Component): String =
+ override def contextualName(ctx: Component): String =
if (mod eq ctx.id) name else s"${mod.getRef.name}.$name"
}
/** Ports of cloned modules (CloneModuleAsRecord)
@@ -175,19 +181,25 @@ case class ModuleIO(mod: BaseModule, name: String) extends Arg {
* @param name the name of the module instance
*/
case class ModuleCloneIO(mod: BaseModule, name: String) extends Arg {
- override def fullName(ctx: Component): String =
+ override def localName = ""
+ override def contextualName(ctx: Component): String =
// NOTE: mod eq ctx.id only occurs in Target and Named-related APIs
- if (mod eq ctx.id) "" else name
+ if (mod eq ctx.id) localName else name
}
case class Slot(imm: Node, name: String) extends Arg {
- override def fullName(ctx: Component): String = {
- val immName = imm.fullName(ctx)
+ override def contextualName(ctx: Component): String = {
+ val immName = imm.contextualName(ctx)
+ if (immName.isEmpty) name else s"$immName.$name"
+ }
+ override def localName: String = {
+ val immName = imm.localName
if (immName.isEmpty) name else s"$immName.$name"
}
}
case class Index(imm: Arg, value: Arg) extends Arg {
def name: String = s"[$value]"
- override def fullName(ctx: Component): String = s"${imm.fullName(ctx)}[${value.fullName(ctx)}]"
+ override def contextualName(ctx: Component): String = s"${imm.contextualName(ctx)}[${value.contextualName(ctx)}]"
+ override def localName: String = s"${imm.localName}[${value.localName}]"
}
object Width {
@@ -792,4 +804,5 @@ case class DefBlackBox(id: BaseBlackBox, name: String, ports: Seq[Port], topDir:
case class Circuit(name: String, components: Seq[Component], annotations: Seq[ChiselAnnotation], renames: RenameMap) {
def firrtlAnnotations: Iterable[Annotation] = annotations.flatMap(_.toFirrtl.update(renames))
+
}