summaryrefslogtreecommitdiff
path: root/core/src/main/scala
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/scala')
-rw-r--r--core/src/main/scala/chisel3/BlackBox.scala3
-rw-r--r--core/src/main/scala/chisel3/Module.scala107
-rw-r--r--core/src/main/scala/chisel3/RawModule.scala9
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala99
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala111
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala17
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/IsLookupable.scala25
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala368
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/package.scala48
-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
12 files changed, 846 insertions, 28 deletions
diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala
index 0c42600f..38b08193 100644
--- a/core/src/main/scala/chisel3/BlackBox.scala
+++ b/core/src/main/scala/chisel3/BlackBox.scala
@@ -158,11 +158,12 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param
val namedPorts = _io.elements.toSeq.reverse // ListMaps are stored in reverse order
- // setRef is not called on the actual io.
// There is a risk of user improperly attempting to connect directly with io
// Long term solution will be to define BlackBox IO differently as part of
// it not descending from the (current) Module
for ((name, port) <- namedPorts) {
+ // We are setting a 'fake' ref for io, so that cloneType works but if a user connects to io, it still fails.
+ this.findPort("io").get.setRef(ModuleIO(internal.ViewParent, ""), force = true)
// We have to force override the _ref because it was set during IO binding
port.setRef(ModuleIO(this, _namespace.name(name)), force = true)
}
diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala
index 64065ec9..d0171693 100644
--- a/core/src/main/scala/chisel3/Module.scala
+++ b/core/src/main/scala/chisel3/Module.scala
@@ -121,7 +121,8 @@ abstract class Module(implicit moduleCompileOptions: CompileOptions) extends Raw
private[chisel3] def mkReset: Reset = {
// Top module and compatibility mode use Bool for reset
- val inferReset = _parent.isDefined && moduleCompileOptions.inferModuleReset
+ // Note that a Definition elaboration will lack a parent, but still not be a Top module
+ val inferReset = (_parent.isDefined || Builder.inDefinition) && moduleCompileOptions.inferModuleReset
if (inferReset) Reset() else Bool()
}
@@ -181,13 +182,31 @@ package experimental {
package internal {
import chisel3.experimental.BaseModule
+ import chisel3.experimental.hierarchy.IsInstantiable
object BaseModule {
+ /** Represents a clone of an underlying object. This is used to support CloneModuleAsRecord and Instance/Definition.
+ *
+ * @note We don't actually "clone" anything in the traditional sense but is a placeholder so we lazily clone internal state
+ */
+ private [chisel3] trait IsClone[+T] {
+ // Underlying object of which this is a clone of
+ val _proto: T
+ def getProto: T = _proto
+ def isACloneOf(a: Any): Boolean = this == a || _proto == a
+ }
+
// Private internal class to serve as a _parent for Data in cloned ports
- private[chisel3] class ModuleClone(_proto: BaseModule) extends PseudoModule {
+ private[chisel3] class ModuleClone[T <: BaseModule] (val _proto: T) extends PseudoModule with IsClone[T] {
+ override def toString = s"ModuleClone(${_proto})"
+ def getPorts = _portsRecord
// ClonePorts that hold the bound ports for this module
// Used for setting the refs of both this module and the Record
private[BaseModule] var _portsRecord: Record = _
+ // This is necessary for correctly supporting .toTarget on a Module Clone. If it is made from the
+ // Instance/Definition API, it should return an instanceTarget. If made from CMAR, it should return a
+ // ModuleTarget.
+ private[chisel3] var _madeFromDefinition: Boolean = false
// Don't generate a component, but point to the one for the cloned Module
private[chisel3] def generateComponent(): Option[Component] = {
require(!_closed, "Can't generate module more than once")
@@ -195,9 +214,15 @@ package internal {
_component = _proto._component
None
}
+ // Maps proto ports to module clone's ports
+ private[chisel3] lazy val ioMap: Map[Data, Data] = {
+ val name2Port = getPorts.elements
+ _proto.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 = ()
+ // Name of this instance's module is the same as the proto's name
override def desiredName: String = _proto.name
private[chisel3] def setRefAndPortsRef(namespace: Namespace): Unit = {
@@ -215,6 +240,53 @@ package internal {
}
}
+ /** Represents a module viewed from a different instance context.
+ *
+ * @note Why do we need both ModuleClone and InstanceClone? If we are annotating a reference in a module-clone,
+ * all submodules must be also be 'cloned' so the toTarget can be computed properly. However, we don't need separate
+ * connectable ports for this instance; all that's different from the proto is the parent.
+ *
+ * @note In addition, the instance name of an InstanceClone is going to be the SAME as the proto, but this is not true
+ * for ModuleClone.
+ */
+ private[chisel3] final class InstanceClone[T <: BaseModule] (val _proto: T, val instName: () => String) extends PseudoModule with IsClone[T] {
+ override def toString = s"InstanceClone(${_proto})"
+ // No addition components are generated
+ private[chisel3] def generateComponent(): Option[Component] = None
+ // Necessary for toTarget to work
+ private[chisel3] def setAsInstanceRef(): Unit = { this.setRef(Ref(instName())) }
+ // This module doesn't acutally exist in the FIRRTL so no initialization to do
+ private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = ()
+ // Instance name is the same as proto's instance name
+ override def instanceName = instName()
+ // Module name is the same as proto's module name
+ override def desiredName: String = _proto.name
+ }
+
+ /** Represents a Definition root module, when accessing something from a definition
+ *
+ * @note This is necessary to distinguish between the toTarget behavior for a Module returned from a Definition,
+ * versus a normal Module. A normal Module.toTarget will always return a local target. If calling toTarget
+ * on a Module returned from a Definition (and thus wrapped in an Instance), we need to return the non-local
+ * target whose root is the Definition. This DefinitionClone is used to represent the root parent of the
+ * InstanceClone (which represents the returned module).
+ */
+ private[chisel3] class DefinitionClone[T <: BaseModule] (val _proto: T) extends PseudoModule with IsClone[T] {
+ override def toString = s"DefinitionClone(${_proto})"
+ // No addition components are generated
+ private[chisel3] def generateComponent(): Option[Component] = None
+ // Necessary for toTarget to work
+ private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = ()
+ // Module name is the same as proto's module name
+ override def desiredName: String = _proto.name
+ }
+
+ /** @note If we are cloning a non-module, we need another object which has the proper _parent set!
+ */
+ private[chisel3] final class InstantiableClone[T <: IsInstantiable] (val _proto: T) extends IsClone[T] {
+ private[chisel3] var _parent: Option[BaseModule] = internal.Builder.currentModule
+ }
+
/** Record type returned by CloneModuleAsRecord
*
* @note These are not true Data (the Record doesn't correspond to anything in the emitted
@@ -232,6 +304,9 @@ package internal {
// We make this before clonePorts because we want it to come up first in naming in
// currentModule
val cloneParent = Module(new ModuleClone(proto))
+ require(proto.isClosed, "Can't clone a module before module close")
+ require(cloneParent.getOptionRef.isEmpty, "Can't have ref set already!")
+ // 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: _*)
@@ -253,10 +328,19 @@ package internal {
package experimental {
+ import chisel3.experimental.hierarchy.IsInstantiable
+
+ object BaseModule {
+ implicit class BaseModuleExtensions[T <: BaseModule](b: T) {
+ import chisel3.experimental.hierarchy.{Instance, Definition}
+ def toInstance: Instance[T] = new Instance(Left(b))
+ def toDefinition: Definition[T] = new Definition(Left(b))
+ }
+ }
/** Abstract base class for Modules, an instantiable organizational unit for RTL.
*/
// TODO: seal this?
- abstract class BaseModule extends HasId {
+ abstract class BaseModule extends HasId with IsInstantiable {
_parent.foreach(_.addId(this))
//
@@ -379,13 +463,24 @@ package experimental {
*
* @note Should not be called until circuit elaboration is complete
*/
- final def toNamed: ModuleName = toTarget.toNamed
+ final def toNamed: ModuleName = ModuleTarget(this.circuitName, this.name).toNamed
/** Returns a FIRRTL ModuleTarget that references this object
*
* @note Should not be called until circuit elaboration is complete
*/
- final def toTarget: ModuleTarget = ModuleTarget(this.circuitName, this.name)
+ final def toTarget: IsModule = {
+ this match {
+ case m: internal.BaseModule.InstanceClone[_] if m._parent.nonEmpty => m._parent.get.toTarget.instOf(instanceName, name)
+ case m: internal.BaseModule.InstanceClone[_] => ModuleTarget(this.circuitName, this.name)
+ case m: internal.BaseModule.ModuleClone[_] if m._madeFromDefinition => m._parent.get.toTarget.instOf(instanceName, name)
+ case m: internal.BaseModule.ModuleClone[_] => ModuleTarget(this.circuitName, this.name)
+ // Without this, we get the wrong CircuitName for the Definition
+ case m: internal.BaseModule.DefinitionClone[_] if m._circuit.nonEmpty => ModuleTarget(this._circuit.get.circuitName, this.name)
+ case m: internal.BaseModule.DefinitionClone[_] => ModuleTarget(this.circuitName, this.name)
+ case m => ModuleTarget(this.circuitName, this.name)
+ }
+ }
/** Returns a FIRRTL ModuleTarget that references this object
*
@@ -393,7 +488,7 @@ package experimental {
*/
final def toAbsoluteTarget: IsModule = {
_parent match {
- case Some(parent) => parent.toAbsoluteTarget.instOf(this.instanceName, toTarget.module)
+ case Some(parent) => parent.toAbsoluteTarget.instOf(this.instanceName, name)
case None =>
// FIXME Special handling for Views - evidence of "weirdness" of .toAbsoluteTarget
// In theory, .toAbsoluteTarget should not be necessary, .toTarget combined with the
diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala
index 74e9db6c..27f16ad4 100644
--- a/core/src/main/scala/chisel3/RawModule.scala
+++ b/core/src/main/scala/chisel3/RawModule.scala
@@ -7,7 +7,7 @@ import scala.util.Try
import scala.language.experimental.macros
import chisel3.experimental.{BaseModule, BaseSim}
import chisel3.internal._
-import chisel3.internal.BaseModule.ModuleClone
+import chisel3.internal.BaseModule.{ModuleClone, InstanceClone}
import chisel3.internal.Builder._
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.UnlocatableSourceInfo
@@ -77,7 +77,8 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions)
// All suggestions are in, force names to every node.
for (id <- getIds) {
id match {
- case id: ModuleClone => id.setRefAndPortsRef(_namespace) // special handling
+ case id: ModuleClone[_] => id.setRefAndPortsRef(_namespace) // special handling
+ case id: InstanceClone[_] => id.setAsInstanceRef()
case id: BaseModule => id.forceName(None, default=id.desiredName, _namespace)
case id: MemBase[_] => id.forceName(None, default="MEM", _namespace)
case id: BaseSim => id.forceName(None, default="SIM", _namespace)
@@ -158,6 +159,10 @@ trait RequireSyncReset extends Module {
package object internal {
+ import scala.annotation.implicitNotFound
+ @implicitNotFound("You are trying to access a macro-only API. Please use the @public annotation instead.")
+ trait MacroGenerated
+
/** Marker trait for modules that are not true modules */
private[chisel3] trait PseudoModule extends BaseModule
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
new file mode 100644
index 00000000..2e917dfa
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import scala.language.experimental.macros
+
+import chisel3._
+import scala.collection.mutable.HashMap
+import chisel3.internal.{Builder, DynamicContext}
+import chisel3.internal.sourceinfo.{DefinitionTransform, DefinitionWrapTransform, SourceInfo}
+import chisel3.experimental.BaseModule
+import chisel3.internal.BaseModule.IsClone
+
+/** User-facing Definition type.
+ * Represents a definition of an object of type [[A]] which are marked as @instantiable
+ * Can be created using Definition.apply method.
+ *
+ * These definitions are then used to create multiple [[Instance]]s.
+ *
+ * @param cloned The internal representation of the instance, which may be either be directly the object, or a clone of an object
+ */
+case class Definition[+A] private[chisel3] (private[chisel3] cloned: Either[A, IsClone[A]]) extends IsLookupable {
+ private[chisel3] def proto: A = cloned match {
+ case Left(value: A) => value
+ case Right(i: IsClone[A]) => i._proto
+ }
+ /** Used by Chisel's internal macros. DO NOT USE in your normal Chisel code!!!
+ * Instead, mark the field you are accessing with [[@public]]
+ *
+ * Given a selector function (that) which selects a member from the original, return the
+ * corresponding member from the instance.
+ *
+ * Our @instantiable and @public macros generate the calls to this apply method
+ *
+ * By calling this function, we summon the proper Lookupable typeclass from our implicit scope.
+ *
+ * @param that a user-specified lookup function
+ * @param lookup typeclass which contains the correct lookup function, based on the types of A and B
+ * @param macroGenerated a value created in the macro, to make it harder for users to use this API
+ */
+ def _lookup[B, C](that: A => B)(implicit lookup: Lookupable[B], macroGenerated: chisel3.internal.MacroGenerated): lookup.C = {
+ lookup.definitionLookup(that, this)
+ }
+
+ /** Updated by calls to [[apply]], to avoid recloning returned Data's */
+ private [chisel3] val cache = HashMap[Data, Data]()
+
+
+ /** @return the context of any Data's return from inside the instance */
+ private[chisel3] def getInnerDataContext: Option[BaseModule] = proto match {
+ case value: BaseModule =>
+ val newChild = Module.do_apply(new internal.BaseModule.DefinitionClone(value))(chisel3.internal.sourceinfo.UnlocatableSourceInfo, chisel3.ExplicitCompileOptions.Strict)
+ newChild._circuit = value._circuit.orElse(Some(value))
+ newChild._parent = None
+ Some(newChild)
+ case value: IsInstantiable => None
+ }
+
+}
+
+/** Factory methods for constructing [[Definition]]s */
+object Definition extends SourceInfoDoc {
+ implicit class DefinitionBaseModuleExtensions[T <: BaseModule](d: Definition[T]) {
+ /** If this is an instance of a Module, returns the toTarget of this instance
+ * @return target of this instance
+ */
+ def toTarget = d.proto.toTarget
+
+ /** If this is an instance of a Module, returns the toAbsoluteTarget of this instance
+ * @return absoluteTarget of this instance
+ */
+ def toAbsoluteTarget = d.proto.toAbsoluteTarget
+ }
+ /** A construction method to build a Definition of a Module
+ *
+ * @param proto the Module being defined
+ *
+ * @return the input module as a Definition
+ */
+ def apply[T <: BaseModule with IsInstantiable](proto: => T): Definition[T] = macro DefinitionTransform.apply[T]
+
+ /** A construction method to build a Definition of a Module
+ *
+ * @param bc the Module being defined
+ *
+ * @return the input module as a Definition
+ */
+ def do_apply[T <: BaseModule with IsInstantiable](proto: => T) (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Definition[T] = {
+ val dynamicContext = new DynamicContext(Nil)
+ Builder.globalNamespace.copyTo(dynamicContext.globalNamespace)
+ dynamicContext.inDefinition = true
+ val (ir, module) = Builder.build(Module(proto), dynamicContext)
+ Builder.components ++= ir.components
+ Builder.annotations ++= ir.annotations
+ module._circuit = Builder.currentModule
+ dynamicContext.globalNamespace.copyTo(Builder.globalNamespace)
+ new Definition(Left(module))
+ }
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala
new file mode 100644
index 00000000..aec42da3
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import scala.collection.mutable.{ArrayBuffer, HashMap}
+import scala.language.experimental.macros
+
+import chisel3._
+import chisel3.internal.BaseModule.{ModuleClone, IsClone, InstantiableClone}
+import chisel3.internal.sourceinfo.{InstanceTransform, SourceInfo}
+import chisel3.experimental.BaseModule
+
+/** User-facing Instance type.
+ * Represents a unique instance of type [[A]] which are marked as @instantiable
+ * Can be created using Instance.apply method.
+ *
+ * @param cloned The internal representation of the instance, which may be either be directly the object, or a clone of an object
+ */
+case class Instance[+A] private [chisel3] (private[chisel3] cloned: Either[A, IsClone[A]]) {
+
+ /** Returns the original object which is instantiated here.
+ * If this is an instance of a clone, return that clone's original proto
+ *
+ * @return the original object which was instantiated
+ */
+ private[chisel3] def proto: A = cloned match {
+ case Left(value: A) => value
+ case Right(i: IsClone[A]) => i._proto
+ }
+
+ /** @return the context of any Data's return from inside the instance */
+ private[chisel3] def getInnerDataContext: Option[BaseModule] = cloned match {
+ case Left(value: BaseModule) => Some(value)
+ case Left(value: IsInstantiable) => None
+ case Right(i: BaseModule) => Some(i)
+ case Right(i: InstantiableClone[_]) => i._parent
+ }
+
+ /** @return the context this instance. Note that for non-module clones, getInnerDataContext will be the same as getClonedParent */
+ private[chisel3] def getClonedParent: Option[BaseModule] = cloned match {
+ case Left(value: BaseModule) => value._parent
+ case Right(i: BaseModule) => i._parent
+ case Right(i: InstantiableClone[_]) => i._parent
+ }
+
+ /** Updated by calls to [[apply]], to avoid recloning returned Data's */
+ private [chisel3] val cache = HashMap[Data, Data]()
+
+ /** Used by Chisel's internal macros. DO NOT USE in your normal Chisel code!!!
+ * Instead, mark the field you are accessing with [[@public]]
+ *
+ * Given a selector function (that) which selects a member from the original, return the
+ * corresponding member from the instance.
+ *
+ * Our @instantiable and @public macros generate the calls to this apply method
+ *
+ * By calling this function, we summon the proper Lookupable typeclass from our implicit scope.
+ *
+ * @param that a user-specified lookup function
+ * @param lookup typeclass which contains the correct lookup function, based on the types of A and B
+ * @param macroGenerated a value created in the macro, to make it harder for users to use this API
+ */
+ def _lookup[B, C](that: A => B)(implicit lookup: Lookupable[B], macroGenerated: chisel3.internal.MacroGenerated): lookup.C = {
+ lookup.instanceLookup(that, this)
+ }
+
+ /** Returns the definition of this Instance */
+ def toDefinition: Definition[A] = new Definition(Left(proto))
+
+}
+
+/** Factory methods for constructing [[Instance]]s */
+object Instance extends SourceInfoDoc {
+ implicit class InstanceBaseModuleExtensions[T <: BaseModule](i: Instance[T]) {
+ /** If this is an instance of a Module, returns the toTarget of this instance
+ * @return target of this instance
+ */
+ def toTarget = i.cloned match {
+ case Left(x: BaseModule) => x.toTarget
+ case Right(x: IsClone[_] with BaseModule) => x.toTarget
+ }
+
+ /** If this is an instance of a Module, returns the toAbsoluteTarget of this instance
+ * @return absoluteTarget of this instance
+ */
+ def toAbsoluteTarget = i.cloned match {
+ case Left(x) => x.toAbsoluteTarget
+ case Right(x: IsClone[_] with BaseModule) => x.toAbsoluteTarget
+ }
+
+ }
+ /** A constructs an [[Instance]] from a [[Definition]]
+ *
+ * @param definition the Module being created
+ * @return an instance of the module definition
+ */
+ def apply[T <: BaseModule with IsInstantiable](definition: Definition[T]): Instance[T] = macro InstanceTransform.apply[T]
+
+ /** A constructs an [[Instance]] from a [[Definition]]
+ *
+ * @param definition the Module being created
+ * @return an instance of the module definition
+ */
+ def do_apply[T <: BaseModule with IsInstantiable](definition: Definition[T])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Instance[T] = {
+ val ports = experimental.CloneModuleAsRecord(definition.proto)
+ val clone = ports._parent.get.asInstanceOf[ModuleClone[T]]
+ clone._madeFromDefinition = true
+ new Instance(Right(clone))
+ }
+
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala
new file mode 100644
index 00000000..26ba0286
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+/** While this is public, it is not recommended for users to extend directly.
+ * Instead, use the [[@instantiable]] annotation on your trait or class.
+ *
+ * This trait indicates whether a class can be returned from an Instance.
+ *
+ */
+trait IsInstantiable
+
+object IsInstantiable {
+ implicit class IsInstantiableExtensions[T <: IsInstantiable](i: T) {
+ def toInstance: Instance[T] = new Instance(Left(i))
+ }
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/IsLookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/IsLookupable.scala
new file mode 100644
index 00000000..37d29a43
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/IsLookupable.scala
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+/** A User-extendable trait to mark metadata-containers, e.g. parameter case classes, as valid to return unchanged
+ * from an instance.
+ *
+ * This should only be true of the metadata returned is identical for ALL instances!
+ *
+ * @example For instances of the same proto, metadata or other construction parameters
+ * may be useful to access outside of the instance construction. For parameters that are
+ * the same for all instances, we should mark it as IsLookupable
+ * {{{
+ * case class Params(debugMessage: String) extends IsLookupable
+ * class MyModule(p: Params) extends MultiIOModule {
+ * printf(p.debugMessage)
+ * }
+ * val myParams = Params("Hello World")
+ * val definition = Definition(new MyModule(myParams))
+ * val i0 = Instance(definition)
+ * val i1 = Instance(definition)
+ * require(i0.p == i1.p) // p is only accessable because it extends IsLookupable
+ * }}}
+ */
+trait IsLookupable
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
new file mode 100644
index 00000000..b9617723
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import chisel3.experimental.BaseModule
+import chisel3.internal.sourceinfo.SourceInfo
+import chisel3.internal.BaseModule.{InstanceClone, InstantiableClone, IsClone, ModuleClone}
+
+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.{AggregateViewBinding, Builder, ChildBinding, ViewBinding, ViewParent, throwException}
+
+/** Represents lookup typeclass to determine how a value accessed from an original IsInstantiable
+ * should be tweaked to return the Instance's version
+ * Sealed.
+ */
+@implicitNotFound("@public is only legal within a class marked @instantiable and only on vals of type" +
+ " Data, BaseModule, IsInstantiable, IsLookupable, or Instance[_], or in an Iterable or Option")
+sealed trait Lookupable[-B] {
+ type C // Return type of the lookup
+ /** Function called to modify the returned value of type B from A, into C
+ *
+ * @param that function that selects B from A
+ * @param instance Instance of A, used to determine C's context
+ * @return
+ */
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C
+
+ /** Function called to modify the returned value of type B from A, into C
+ *
+ * @param that function that selects B from A
+ * @param definition Definition of A, used to determine C's context
+ * @return
+ */
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C
+}
+
+private[chisel3] object Lookupable {
+
+ /** Clones a data and sets its internal references to its parent module to be in a new context.
+ *
+ * @param data data to be cloned
+ * @param context new context
+ * @return
+ */
+ private[chisel3] def cloneDataToContext[T <: Data](data: T, context: BaseModule)
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
+ internal.requireIsHardware(data, "cross module reference type")
+ data._parent match {
+ case None => data
+ case Some(parent) =>
+ val newParent = cloneModuleToContext(Left(parent), context)
+ newParent match {
+ case Left(p) if p == parent => data
+ case Right(m: BaseModule) =>
+ val newChild = data.cloneTypeFull
+ newChild.setRef(data.getRef, true)
+ newChild.bind(internal.CrossModuleBinding)
+ newChild.setAllParents(Some(m))
+ newChild
+ }
+ }
+ }
+ // The business logic of lookupData
+ // Also called by cloneViewToContext which potentially needs to lookup stuff from ioMap or the cache
+ private[chisel3] def doLookupData[A, B <: Data](data: B, cache: HashMap[Data, Data], ioMap: Option[Map[Data, Data]], context: Option[BaseModule])
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): B = {
+ def impl[C <: Data](d: C): C = d match {
+ case x: Data if ioMap.nonEmpty && ioMap.get.contains(x) => ioMap.get(x).asInstanceOf[C]
+ case x: Data if cache.contains(x) => cache(x).asInstanceOf[C]
+ case _ =>
+ assert(context.nonEmpty) // TODO is this even possible? Better error message here
+ val ret = cloneDataToContext(d, context.get)
+ cache(d) = ret
+ ret
+ }
+ data.binding match {
+ case Some(_: ChildBinding) => mapRootAndExtractSubField(data, impl)
+ case _ => impl(data)
+ }
+ }
+
+ // Helper for co-iterating on Elements of aggregates, they must be the same type but that is unchecked
+ private def coiterate(a: Data, b: Data): Iterable[(Element, Element)] = {
+ val as = getRecursiveFields.lazily(a, "_")
+ val bs = getRecursiveFields.lazily(b, "_")
+ as.zip(bs).collect { case ((ae: Element, _), (be: Element, _)) => (ae, be) }
+ }
+
+ /** Given a Data, find the root of its binding, apply a function to the root to get a "new root",
+ * and find the equivalent child Data in the "new root"
+ *
+ * @example {{{
+ * Given `arg = a.b[2].c` and some `f`:
+ * 1. a = root(arg) = root(a.b[2].c)
+ * 2. newRoot = f(root(arg)) = f(a)
+ * 3. return newRoot.b[2].c
+ * }}}
+ *
+ * Invariants that elt is a Child of something of the type of data is dynamically checked as we traverse
+ */
+ private def mapRootAndExtractSubField[A <: Data](arg: A, f: Data => Data): A = {
+ def err(msg: String) = throwException(s"Internal Error! $msg")
+ 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 _ => (res, d)
+ }
+ def applyCoordinates(fullCoor: List[Arg], start: Data): Data = {
+ def rec(coor: List[Arg], d: Data): Data = {
+ if (coor.isEmpty) d
+ else {
+ 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 (arg, _) => err(s"Unexpected Arg '$arg' applied to '$d'! Root was '$start'.")
+ }
+ applyCoordinates(coor.tail, next)
+ }
+ }
+ rec(fullCoor, start)
+ }
+ val (coor, root) = unrollCoordinates(Nil, arg)
+ val newRoot = f(root)
+ val result = applyCoordinates(coor, newRoot)
+ try {
+ result.asInstanceOf[A]
+ } catch {
+ case _: ClassCastException => err(s"Applying '$coor' to '$newRoot' somehow resulted in '$result'")
+ }
+ }
+
+ // TODO this logic is complicated, can any of it be unified with viewAs?
+ // If `.viewAs` would capture its arguments, we could potentially use it
+ // TODO Describe what this is doing at a high level
+ private[chisel3] def cloneViewToContext[A, B <: Data](data: B, cache: HashMap[Data, Data], ioMap: Option[Map[Data, Data]], context: Option[BaseModule])
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): B = {
+ // alias to shorten lookups
+ def lookupData[C <: Data](d: C) = doLookupData(d, cache, ioMap, context)
+
+ val result = data.cloneTypeFull
+
+ // We have to lookup the target(s) of the view since they may need to be cloned into the current context
+ val newBinding = data.topBinding match {
+ case ViewBinding(target) => ViewBinding(lookupData(reify(target)))
+ case avb @ AggregateViewBinding(map, targetOpt) => data match {
+ case _: Element => ViewBinding(lookupData(reify(map(data))))
+ case _: Aggregate =>
+ // Provide a 1:1 mapping if possible
+ val singleTargetOpt = targetOpt.filter(_ => avb == data.binding.get).flatMap(reifySingleData)
+ singleTargetOpt match {
+ case Some(singleTarget) => // It is 1:1!
+ // This is a little tricky because the values in newMap need to point to Elements of newTarget
+ val newTarget = lookupData(singleTarget)
+ val newMap = coiterate(result, data).map { case (res, from) =>
+ (res: Data) -> mapRootAndExtractSubField(map(from), _ => newTarget)
+ }.toMap
+ AggregateViewBinding(newMap, Some(newTarget))
+
+ case None => // No 1:1 mapping so we have to do a flat binding
+ // Just remap each Element of this aggregate
+ val newMap = coiterate(result, data).map {
+ // Upcast res to Data since Maps are invariant in the Key type parameter
+ case (res, from) => (res: Data) -> lookupData(reify(map(from)))
+ }.toMap
+ AggregateViewBinding(newMap, None)
+ }
+ }
+ }
+
+ // TODO Unify the following with `.viewAs`
+ // We must also mark non-1:1 and child Aggregates in the view for renaming
+ newBinding match {
+ case _: ViewBinding => // Do nothing
+ case AggregateViewBinding(_, target) =>
+ if (target.isEmpty) {
+ Builder.unnamedViews += result
+ }
+ // Binding does not capture 1:1 for child aggregates views
+ getRecursiveFields.lazily(result, "_").foreach {
+ case (agg: Aggregate, _) if agg != result =>
+ Builder.unnamedViews += agg
+ case _ => // Do nothing
+ }
+ }
+
+ result.bind(newBinding)
+ result.setAllParents(Some(ViewParent))
+ result.forceName(None, "view", Builder.viewNamespace)
+ result
+ }
+ /** Given a module (either original or a clone), clone it to a new context
+ *
+ * This function effectively recurses up the parents of module to find whether:
+ * (1) A parent is already in the context; then we do nothing and return module
+ * (2) A parent is in a different clone of the context; then we clone all the parents up
+ * to that parent and set their parents to be in this cloned context
+ * (3) A parent has no root; in that case, we do nothing and return the module.
+ *
+ * @param module original or clone to be cloned into a new context
+ * @param context new context
+ * @return original or clone in the new context
+ */
+ private[chisel3] def cloneModuleToContext[T <: BaseModule](module: Either[T, IsClone[T]], context: BaseModule)
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Either[T, IsClone[T]] = {
+ // Recursive call
+ def rec[A <: BaseModule](m: A): Either[A, IsClone[A]] = {
+ def clone(x: A, p: Option[BaseModule], name: () => String): Either[A, IsClone[A]] = {
+ val newChild = Module.do_apply(new internal.BaseModule.InstanceClone(x, name))
+ newChild._parent = p
+ Right(newChild)
+ }
+ (m, context) match {
+ case (c, ctx) if ctx == c => Left(c)
+ case (c, ctx: IsClone[_]) if ctx.isACloneOf(c) => Right(ctx.asInstanceOf[IsClone[A]])
+ case (c, ctx) if c._parent.isEmpty => Left(c)
+ case (_, _) =>
+ cloneModuleToContext(Left(m._parent.get), context) match {
+ case Left(p) => Left(m)
+ case Right(p: BaseModule) =>
+ clone(m, Some(p), () => m.instanceName)
+ }
+ }
+ }
+ module match {
+ case Left(m) => rec(m)
+ case Right(m: ModuleClone[_]) =>
+ rec(m) match {
+ case Left(mx) => Right(mx)
+ case Right(i: InstanceClone[_]) =>
+ val newChild = Module.do_apply(new InstanceClone(m._proto, () => m.instanceName))
+ newChild._parent = i._parent
+ Right(newChild)
+ }
+ case Right(m: InstanceClone[_]) =>
+ rec(m) match {
+ case Left(mx) => Right(mx)
+ case Right(i: InstanceClone[_]) =>
+ val newChild = Module.do_apply(new InstanceClone(m._proto, () => m.instanceName))
+ newChild._parent = i._parent
+ Right(newChild)
+ }
+ }
+ }
+
+ class SimpleLookupable[X] extends Lookupable[X] {
+ type B = X
+ type C = X
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C = that(definition.proto)
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C = that(instance.proto)
+ }
+
+ implicit def lookupInstance[B <: BaseModule](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new Lookupable[Instance[B]] {
+ type C = Instance[B]
+ def definitionLookup[A](that: A => Instance[B], definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ new Instance(cloneModuleToContext(ret.cloned, definition.getInnerDataContext.get))
+ }
+ def instanceLookup[A](that: A => Instance[B], instance: Instance[A]): C = {
+ val ret = that(instance.proto)
+ instance.cloned match {
+ // If instance is just a normal module, no changing of context is necessary
+ case Left(_) => new Instance(ret.cloned)
+ case Right(_) => new Instance(cloneModuleToContext(ret.cloned, instance.getInnerDataContext.get))
+ }
+ }
+ }
+
+ implicit def lookupModule[B <: BaseModule](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new Lookupable[B] {
+ type C = Instance[B]
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ new Instance(cloneModuleToContext(Left(ret), definition.getInnerDataContext.get))
+ }
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C = {
+ val ret = that(instance.proto)
+ instance.cloned match {
+ // If instance is just a normal module, no changing of context is necessary
+ case Left(_) => new Instance(Left(ret))
+ case Right(_) => new Instance(cloneModuleToContext(Left(ret), instance.getInnerDataContext.get))
+ }
+ }
+ }
+
+ implicit def lookupData[B <: Data](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new Lookupable[B] {
+ type C = B
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ if (isView(ret)) {
+ ??? // TODO!!!!!! cloneViewToContext(ret, instance, ioMap, instance.getInnerDataContext)
+ } else {
+ doLookupData(ret, definition.cache, None, definition.getInnerDataContext)
+ }
+ }
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C = {
+ val ret = that(instance.proto)
+ val ioMap: Option[Map[Data, Data]] = instance.cloned match {
+ case Right(x: ModuleClone[_]) => Some(x.ioMap)
+ case Left(x: BaseModule) => Some(x.getChiselPorts.map { case (_, data) => data -> data }.toMap)
+ case _ => None
+ }
+ if (isView(ret)) {
+ cloneViewToContext(ret, instance.cache, ioMap, instance.getInnerDataContext)
+ } else {
+ doLookupData(ret, instance.cache, ioMap, instance.getInnerDataContext)
+ }
+
+ }
+ }
+
+ import scala.language.higherKinds // Required to avoid warning for lookupIterable type parameter
+ implicit def lookupIterable[B, F[_] <: Iterable[_]](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions, lookupable: Lookupable[B]) = new Lookupable[F[B]] {
+ type C = F[lookupable.C]
+ def definitionLookup[A](that: A => F[B], definition: Definition[A]): C = {
+ val ret = that(definition.proto).asInstanceOf[Iterable[B]]
+ ret.map{ x: B => lookupable.definitionLookup[A](_ => x, definition) }.asInstanceOf[C]
+ }
+ def instanceLookup[A](that: A => F[B], instance: Instance[A]): C = {
+ import instance._
+ val ret = that(proto).asInstanceOf[Iterable[B]]
+ ret.map{ x: B => lookupable.instanceLookup[A](_ => x, instance) }.asInstanceOf[C]
+ }
+ }
+ implicit def lookupOption[B](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions, lookupable: Lookupable[B]) = new Lookupable[Option[B]] {
+ type C = Option[lookupable.C]
+ def definitionLookup[A](that: A => Option[B], definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ ret.map{ x: B => lookupable.definitionLookup[A](_ => x, definition) }
+ }
+ def instanceLookup[A](that: A => Option[B], instance: Instance[A]): C = {
+ import instance._
+ val ret = that(proto)
+ ret.map{ x: B => lookupable.instanceLookup[A](_ => x, instance) }
+ }
+ }
+ implicit def lookupIsInstantiable[B <: IsInstantiable](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new Lookupable[B] {
+ type C = Instance[B]
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ val cloned = new InstantiableClone(ret)
+ cloned._parent = definition.getInnerDataContext
+ new Instance(Right(cloned))
+ }
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C = {
+ val ret = that(instance.proto)
+ val cloned = new InstantiableClone(ret)
+ cloned._parent = instance.getInnerDataContext
+ new Instance(Right(cloned))
+ }
+ }
+
+ implicit def lookupIsLookupable[B <: IsLookupable](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new SimpleLookupable[B]()
+
+ implicit val lookupInt = new SimpleLookupable[Int]()
+ implicit val lookupByte = new SimpleLookupable[Byte]()
+ implicit val lookupShort = new SimpleLookupable[Short]()
+ implicit val lookupLong = new SimpleLookupable[Long]()
+ implicit val lookupFloat = new SimpleLookupable[Float]()
+ implicit val lookupChar = new SimpleLookupable[Char]()
+ implicit val lookupString = new SimpleLookupable[String]()
+ implicit val lookupBoolean = new SimpleLookupable[Boolean]()
+ implicit val lookupBigInt = new SimpleLookupable[BigInt]()
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/package.scala b/core/src/main/scala/chisel3/experimental/hierarchy/package.scala
new file mode 100644
index 00000000..c309ab52
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/package.scala
@@ -0,0 +1,48 @@
+package chisel3.experimental
+
+package object hierarchy {
+
+ /** Classes or traits which will be used with the [[Definition]] + [[Instance]] api should be marked
+ * with the [[@instantiable]] annotation at the class/trait definition.
+ *
+ * @example {{{
+ * @instantiable
+ * class MyModule extends Module {
+ * ...
+ * }
+ *
+ * val d = Definition(new MyModule)
+ * val i0 = Instance(d)
+ * val i1 = Instance(d)
+ * }}}
+ */
+ class instantiable extends chisel3.internal.instantiable
+
+ /** Classes marked with [[@instantiable]] can have their vals marked with the [[@public]] annotation to
+ * enable accessing these values from a [[Definition]] or [[Instance]] of the class.
+ *
+ * Only vals of the the following types can be marked [[@public]]:
+ * 1. IsInstantiable
+ * 2. IsLookupable
+ * 3. Data
+ * 4. BaseModule
+ * 5. Iterable/Option containing a type that meets these requirements
+ * 6. Basic type like String, Int, BigInt etc.
+ *
+ * @example {{{
+ * @instantiable
+ * class MyModule extends Module {
+ * @public val in = IO(Input(UInt(3.W)))
+ * @public val out = IO(Output(UInt(3.W)))
+ * ..
+ * }
+ *
+ * val d = Definition(new MyModule)
+ * val i0 = Instance(d)
+ * val i1 = Instance(d)
+ *
+ * i1.in := i0.out
+ * }}}
+ */
+ class public extends chisel3.internal.public
+}
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))
+
}