summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiuyang Liu2021-11-14 12:43:01 +0800
committerGitHub2021-11-14 12:43:01 +0800
commit6c5ea6781b0850888e7d0d44439df07146665534 (patch)
treed7a7a028cecbbe3bbcb1c91984b7db250cc4b606
parent74ebcbca5a7394b67cd8c0d9fd35e32426759500 (diff)
parent64ad1c06b588ba75839e2629fbf889d7b8604953 (diff)
Merge branch 'master' into update/sbt-unidoc-0.5.0
-rw-r--r--.github/workflows/test.yml18
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--build.sbt3
-rw-r--r--core/src/main/scala/chisel3/Module.scala81
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala21
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Hierarchy.scala112
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala54
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala2
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/LibraryHooks.scala31
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala102
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Underlying.scala14
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala8
-rw-r--r--docs/src/cookbooks/cookbook.md7
-rw-r--r--docs/src/cookbooks/hierarchy.md57
-rw-r--r--macros/src/main/scala/chisel3/internal/InstantiableMacro.scala21
-rw-r--r--project/plugins.sbt2
-rw-r--r--src/main/scala/chisel3/aop/Select.scala178
-rw-r--r--src/main/scala/chisel3/aop/injecting/InjectingAspect.scala2
-rw-r--r--src/main/scala/chisel3/util/Arbiter.scala13
-rw-r--r--src/main/scala/chisel3/util/Decoupled.scala30
-rw-r--r--src/main/scala/chisel3/util/Valid.scala18
-rw-r--r--src/main/scala/chisel3/util/random/PRNG.scala13
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala12
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Examples.scala16
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala146
25 files changed, 772 insertions, 191 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ee4d68d5..5f3252cc 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -25,12 +25,9 @@ jobs:
uses: actions/checkout@v2
- name: Install Tabby OSS Cad Suite (from YosysHQ)
- run: |
- cd /home/runner/work/
- wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2021-09-23/oss-cad-suite-linux-x64-20210923.tgz
- tar -xf oss-cad-suite-linux-x64-20210923.tgz
- ls /home/runner/work/oss-cad-suite/bin
- echo "/home/runner/work/oss-cad-suite/bin" >> $GITHUB_PATH
+ uses: YosysHQ/setup-oss-cad-suite@v1
+ with:
+ osscadsuite-version: '2021-11-09'
- name: Install Espresso
run: |
@@ -61,12 +58,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Install Tabby OSS Cad Suite (from YosysHQ)
- run: |
- cd /home/runner/work/
- wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2021-09-23/oss-cad-suite-linux-x64-20210923.tgz
- tar -xf oss-cad-suite-linux-x64-20210923.tgz
- ls /home/runner/work/oss-cad-suite/bin
- echo "/home/runner/work/oss-cad-suite/bin" >> $GITHUB_PATH
+ uses: YosysHQ/setup-oss-cad-suite@v1
+ with:
+ osscadsuite-version: '2021-11-09'
- name: Install Espresso
run: |
cd /tmp
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5f55ac52..06b5200f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -12,3 +12,5 @@
5. You can peruse the [good-first-issues](https://github.com/chipsalliance/chisel3/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) for easy tasks to start with. Another easy thing to start with is doing your own pass of the [website](https://www.chisel-lang.org/chisel3/docs/introduction.html) looking for typos, pages missing their titles, etc. The sources for the website are [here](https://github.com/chipsalliance/chisel3/tree/master/docs).
+6. Please make your PRs against the `master` branch. The project admins, when reviewing your PR, will decide which stable version (if any) your change should be backported to. The backports will be opened automatically on your behalf and you may need to do some cleanup, but focus first on your `master` PR.
+
diff --git a/build.sbt b/build.sbt
index 9794cc22..49c44c09 100644
--- a/build.sbt
+++ b/build.sbt
@@ -113,7 +113,8 @@ lazy val pluginScalaVersions = Seq(
"2.13.3",
"2.13.4",
"2.13.5",
- "2.13.6"
+ "2.13.6",
+ "2.13.7"
)
lazy val plugin = (project in file("plugin")).
diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala
index 56dce4d5..7ba24585 100644
--- a/core/src/main/scala/chisel3/Module.scala
+++ b/core/src/main/scala/chisel3/Module.scala
@@ -88,6 +88,16 @@ object Module extends SourceInfoDoc {
def reset: Reset = Builder.forcedReset
/** Returns the current Module */
def currentModule: Option[BaseModule] = Builder.currentModule
+
+ private[chisel3] def do_pseudo_apply[T <: BaseModule](bc: => T)
+ (implicit sourceInfo: SourceInfo,
+ compileOptions: CompileOptions): T = {
+ val parent = Builder.currentModule
+
+ val module: T = bc // bc is actually evaluated here
+
+ module
+ }
}
/** Abstract base class for Modules, which behave much like Verilog modules.
@@ -182,23 +192,33 @@ package experimental {
package internal {
import chisel3.experimental.BaseModule
- import chisel3.experimental.hierarchy.IsInstantiable
+ import chisel3.experimental.hierarchy.{IsInstantiable, Proto, Clone}
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] {
+ 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[chisel3] def getProto: T
+
+ /** Determines whether another object is a clone of the same underlying proto
+ *
+ * @param a
+ */
+ def hasSameProto(a: Any): Boolean = {
+ val aProto = a match {
+ case x: IsClone[BaseModule] => x.getProto
+ case o => o
+ }
+ this == aProto || getProto == aProto
+ }
}
// Private internal class to serve as a _parent for Data in cloned ports
- private[chisel3] class ModuleClone[T <: BaseModule] (val _proto: T) extends PseudoModule with IsClone[T] {
- override def toString = s"ModuleClone(${_proto})"
+ private[chisel3] class ModuleClone[T <: BaseModule] (val getProto: T) extends PseudoModule with IsClone[T] {
+ override def toString = s"ModuleClone(${getProto})"
def getPorts = _portsRecord
// ClonePorts that hold the bound ports for this module
// Used for setting the refs of both this module and the Record
@@ -211,19 +231,19 @@ package internal {
private[chisel3] def generateComponent(): Option[Component] = {
require(!_closed, "Can't generate module more than once")
_closed = true
- _component = _proto._component
+ _component = getProto._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
+ 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 = ()
// Name of this instance's module is the same as the proto's name
- override def desiredName: String = _proto.name
+ override def desiredName: String = getProto.name
private[chisel3] def setRefAndPortsRef(namespace: Namespace): Unit = {
val record = _portsRecord
@@ -235,7 +255,7 @@ 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(_proto, instName), force=true) // force because we did .forceName first
+ record.setRef(ModuleCloneIO(getProto, instName), force=true) // force because we did .forceName first
this.setRef(Ref(instName))
}
}
@@ -249,8 +269,8 @@ package internal {
* @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})"
+ private[chisel3] final class InstanceClone[T <: BaseModule] (val getProto: T, val instName: () => String) extends PseudoModule with IsClone[T] {
+ override def toString = s"InstanceClone(${getProto})"
// No addition components are generated
private[chisel3] def generateComponent(): Option[Component] = None
// Necessary for toTarget to work
@@ -260,7 +280,7 @@ package internal {
// 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
+ override def desiredName: String = getProto.name
}
/** Represents a Definition root module, when accessing something from a definition
@@ -271,20 +291,21 @@ package internal {
* 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})"
+ private[chisel3] class DefinitionClone[T <: BaseModule] (val getProto: T) extends PseudoModule with IsClone[T] {
+ override def toString = s"DefinitionClone(${getProto})"
// 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
+ override def desiredName: String = getProto.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
+ trait InstantiableClone[T <: IsInstantiable] extends IsClone[T] {
+ private[chisel3] def _innerContext: experimental.hierarchy.Hierarchy[_]
+ private[chisel3] def getInnerContext: Option[BaseModule] = _innerContext.getInnerDataContext
}
/** Record type returned by CloneModuleAsRecord
@@ -328,13 +349,13 @@ package internal {
package experimental {
- import chisel3.experimental.hierarchy.IsInstantiable
+ import chisel3.experimental.hierarchy.{IsInstantiable, Proto}
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))
+ def toInstance: Instance[T] = new Instance(Proto(b))
+ def toDefinition: Definition[T] = new Definition(Proto(b))
}
}
/** Abstract base class for Modules, an instantiable organizational unit for RTL.
@@ -346,13 +367,19 @@ package experimental {
//
// Builder Internals - this tracks which Module RTL construction belongs to.
//
- if (!Builder.readyForModuleConstr) {
- throwException("Error: attempted to instantiate a Module without wrapping it in Module().")
+ this match {
+ case _: PseudoModule =>
+ case other =>
+ if (!Builder.readyForModuleConstr) {
+ throwException("Error: attempted to instantiate a Module without wrapping it in Module().")
+ }
}
- readyForModuleConstr = false
+ if (Builder.hasDynamicContext) {
+ readyForModuleConstr = false
- Builder.currentModule = Some(this)
- Builder.whenStack = Nil
+ Builder.currentModule = Some(this)
+ Builder.whenStack = Nil
+ }
//
// Module Construction Internals
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
index 0cc3d131..2ac61807 100644
--- a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
@@ -18,13 +18,9 @@ import firrtl.annotations.{IsModule, ModuleTarget}
*
* 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
+ * @param underlying The internal representation of the definition, 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
- }
+final case class Definition[+A] private[chisel3] (private[chisel3] underlying: Underlying[A]) extends IsLookupable with SealedHierarchy[A] {
/** Used by Chisel's internal macros. DO NOT USE in your normal Chisel code!!!
* Instead, mark the field you are accessing with [[@public]]
*
@@ -43,20 +39,20 @@ case class Definition[+A] private[chisel3] (private[chisel3] cloned: Either[A, I
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)
+ val newChild = Module.do_pseudo_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
}
+ override def toDefinition: Definition[A] = this
+ override def toInstance: Instance[A] = new Instance(underlying)
+
+
}
/** Factory methods for constructing [[Definition]]s */
@@ -95,6 +91,7 @@ object Definition extends SourceInfoDoc {
Builder.annotations ++= ir.annotations
module._circuit = Builder.currentModule
dynamicContext.globalNamespace.copyTo(Builder.globalNamespace)
- new Definition(Left(module))
+ new Definition(Proto(module))
}
+
}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Hierarchy.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Hierarchy.scala
new file mode 100644
index 00000000..503e437b
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Hierarchy.scala
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import chisel3._
+import scala.collection.mutable.{HashMap, HashSet}
+import scala.reflect.runtime.universe.TypeTag
+import chisel3.internal.BaseModule.IsClone
+import chisel3.experimental.BaseModule
+import _root_.firrtl.annotations.IsModule
+import scala.annotation.implicitNotFound
+
+/** Super-trait for Instance and Definition
+ *
+ * Enables writing functions which are Instance/Definition agnostic
+ */
+sealed trait Hierarchy[+A] {
+ private[chisel3] def underlying: Underlying[A]
+ private[chisel3] def proto: A = underlying match {
+ case Proto(value: A) => value
+ case Clone(i: IsClone[A]) => i.getProto
+ }
+
+ /** Updated by calls to [[_lookup]], to avoid recloning returned Data's */
+ private[chisel3] val cache = HashMap[Data, Data]()
+ private[chisel3] def getInnerDataContext: Option[BaseModule]
+
+ /** Determine whether underlying proto is of type provided.
+ *
+ * @note IMPORTANT: this function requires summoning a TypeTag[B], which will fail if B is an inner class.
+ * @note IMPORTANT: this function IGNORES type parameters, akin to normal type erasure.
+ * @note IMPORTANT: this function relies on Java reflection for underlying proto, but Scala reflection for provided type
+ *
+ * E.g. isA[List[Int]] will return true, even if underlying proto is of type List[String]
+ * @return Whether underlying proto is of provided type (with caveats outlined above)
+ */
+ def isA[B : TypeTag]: Boolean = {
+ val tptag = implicitly[TypeTag[B]]
+ val name = tptag.tpe.toString
+ inBaseClasses(name)
+ }
+
+
+ // This code handles a special-case where, within an mdoc context, the type returned from
+ // scala reflection (typetag) looks different than when returned from java reflection.
+ // This function detects this case and reshapes the string to match.
+ private def modifyReplString(clz: String): String = {
+ if(clz != null) {
+ clz.split('.').toList match {
+ case "repl" :: "MdocSession" :: app :: rest => s"$app.this." + rest.mkString(".")
+ case other => clz
+ }
+ } else clz
+ }
+ private lazy val superClasses = calculateSuperClasses(proto.getClass())
+ private def calculateSuperClasses(clz: Class[_]): Set[String] = {
+ if(clz != null) {
+ Set(modifyReplString(clz.getCanonicalName())) ++
+ clz.getInterfaces().flatMap(i => calculateSuperClasses(i)) ++
+ calculateSuperClasses(clz.getSuperclass())
+ } else {
+ Set.empty[String]
+ }
+ }
+ private def inBaseClasses(clz: String): Boolean = superClasses.contains(clz)
+
+
+ /** 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 hierarchy.
+ *
+ * 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
+
+ /** @return Return the underlying Definition[A] of this Hierarchy[A] */
+ def toDefinition: Definition[A]
+
+ /** @return Convert this Hierarchy[A] as a top-level Instance[A] */
+ def toInstance: Instance[A]
+}
+
+// Used to effectively seal Hierarchy, without requiring Definition and Instance to be in this file.
+private[chisel3] trait SealedHierarchy[+A] extends Hierarchy[A]
+
+object Hierarchy {
+ implicit class HierarchyBaseModuleExtensions[T <: BaseModule](i: Hierarchy[T]) {
+ /** Returns the toTarget of this hierarchy
+ * @return target of this hierarchy
+ */
+ def toTarget: IsModule = i match {
+ case d: Definition[T] => new Definition.DefinitionBaseModuleExtensions(d).toTarget
+ case i: Instance[T] => new Instance.InstanceBaseModuleExtensions(i).toTarget
+ }
+
+ /** Returns the toAbsoluteTarget of this hierarchy
+ * @return absoluteTarget of this Hierarchy
+ */
+ def toAbsoluteTarget: IsModule = i match {
+ case d: Definition[T] => new Definition.DefinitionBaseModuleExtensions(d).toAbsoluteTarget
+ case i: Instance[T] => new Instance.InstanceBaseModuleExtensions(i).toAbsoluteTarget
+ }
+ }
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala
index 9b17bfce..97b62c23 100644
--- a/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala
@@ -14,38 +14,29 @@ import firrtl.annotations.IsModule
* 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
+ * @param underlying 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
+final case class Instance[+A] private [chisel3] (private[chisel3] underlying: Underlying[A]) extends SealedHierarchy[A] {
+ underlying match {
+ case Proto(p: IsClone[_]) => chisel3.internal.throwException("Cannot have a Proto with a clone!")
+ case other => //Ok
}
/** @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
+ private[chisel3] def getInnerDataContext: Option[BaseModule] = underlying match {
+ case Proto(value: BaseModule) => Some(value)
+ case Proto(value: IsInstantiable) => None
+ case Clone(i: BaseModule) => Some(i)
+ case Clone(i: InstantiableClone[_]) => i.getInnerContext
}
/** @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
+ private[chisel3] def getClonedParent: Option[BaseModule] = underlying match {
+ case Proto(value: BaseModule) => value._parent
+ case Clone(i: BaseModule) => i._parent
+ case Clone(i: InstantiableClone[_]) => i.getInnerContext
}
- /** 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]]
*
@@ -65,7 +56,8 @@ case class Instance[+A] private [chisel3] (private[chisel3] cloned: Either[A, Is
}
/** Returns the definition of this Instance */
- def toDefinition: Definition[A] = new Definition(Left(proto))
+ override def toDefinition: Definition[A] = new Definition(Proto(proto))
+ override def toInstance: Instance[A] = this
}
@@ -75,17 +67,17 @@ object Instance extends SourceInfoDoc {
/** If this is an instance of a Module, returns the toTarget of this instance
* @return target of this instance
*/
- def toTarget: IsModule = i.cloned match {
- case Left(x: BaseModule) => x.getTarget
- case Right(x: IsClone[_] with BaseModule) => x.getTarget
+ def toTarget: IsModule = i.underlying match {
+ case Proto(x: BaseModule) => x.getTarget
+ case Clone(x: IsClone[_] with BaseModule) => x.getTarget
}
/** If this is an instance of a Module, returns the toAbsoluteTarget of this instance
* @return absoluteTarget of this instance
*/
- def toAbsoluteTarget: IsModule = i.cloned match {
- case Left(x) => x.toAbsoluteTarget
- case Right(x: IsClone[_] with BaseModule) => x.toAbsoluteTarget
+ def toAbsoluteTarget: IsModule = i.underlying match {
+ case Proto(x) => x.toAbsoluteTarget
+ case Clone(x: IsClone[_] with BaseModule) => x.toAbsoluteTarget
}
}
@@ -105,7 +97,7 @@ object Instance extends SourceInfoDoc {
val ports = experimental.CloneModuleAsRecord(definition.proto)
val clone = ports._parent.get.asInstanceOf[ModuleClone[T]]
clone._madeFromDefinition = true
- new Instance(Right(clone))
+ new Instance(Clone(clone))
}
}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala
index 26ba0286..4f3c2d42 100644
--- a/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala
@@ -12,6 +12,6 @@ trait IsInstantiable
object IsInstantiable {
implicit class IsInstantiableExtensions[T <: IsInstantiable](i: T) {
- def toInstance: Instance[T] = new Instance(Left(i))
+ def toInstance: Instance[T] = new Instance(Proto(i))
}
}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/LibraryHooks.scala b/core/src/main/scala/chisel3/experimental/hierarchy/LibraryHooks.scala
new file mode 100644
index 00000000..c16cc633
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/LibraryHooks.scala
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import scala.annotation.implicitNotFound
+
+@implicitNotFound("These functions are only for building hierarchy-compatible Chisel libraries! Users beware!")
+// DO NOT extend unless you know what you are doing!!!!!! Not for the casual user!
+trait InsideHierarchyLibraryExtension
+
+// Collection of public functions to give non-core-Chisel libraries the ability to build integrations with
+// the experimental hierarchy package
+object LibraryHooks {
+
+ /** Builds a new instance given a definition and function to create a new instance-specific Underlying, from the
+ * definition's Underlying
+ * @note Implicitly requires being inside a Hierarchy Library Extension
+ */
+ def buildInstance[A](definition: Definition[A],
+ createUnderlying: Underlying[A] => Underlying[A]
+ )(implicit inside: InsideHierarchyLibraryExtension): Instance[A] = {
+ new Instance(createUnderlying(definition.underlying))
+ }
+
+ /** Builds a new definition given an Underlying implementation
+ * @note Implicitly requires being inside a Hierarchy Library Extension
+ */
+ def buildDefinition[A](underlying: Underlying[A])(implicit inside: InsideHierarchyLibraryExtension): Definition[A] = {
+ new Definition(underlying)
+ }
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
index b9617723..ff4d676c 100644
--- a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
@@ -19,7 +19,7 @@ import chisel3.internal.{AggregateViewBinding, Builder, ChildBinding, ViewBindin
*/
@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] {
+trait Lookupable[-B] {
type C // Return type of the lookup
/** Function called to modify the returned value of type B from A, into C
*
@@ -36,9 +36,11 @@ sealed trait Lookupable[-B] {
* @return
*/
def definitionLookup[A](that: A => B, definition: Definition[A]): C
+ protected def getProto[A](h: Hierarchy[A]): A = h.proto
+ protected def getUnderlying[A](h: Hierarchy[A]): Underlying[A] = h.underlying
}
-private[chisel3] object Lookupable {
+object Lookupable {
/** Clones a data and sets its internal references to its parent module to be in a new context.
*
@@ -52,10 +54,10 @@ private[chisel3] object Lookupable {
data._parent match {
case None => data
case Some(parent) =>
- val newParent = cloneModuleToContext(Left(parent), context)
+ val newParent = cloneModuleToContext(Proto(parent), context)
newParent match {
- case Left(p) if p == parent => data
- case Right(m: BaseModule) =>
+ case Proto(p) if p == parent => data
+ case Clone(m: BaseModule) =>
val newChild = data.cloneTypeFull
newChild.setRef(data.getRef, true)
newChild.bind(internal.CrossModuleBinding)
@@ -145,7 +147,7 @@ private[chisel3] object Lookupable {
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
+ // We have to lookup the target(s) of the view since they may need to be underlying into the current context
val newBinding = data.topBinding match {
case ViewBinding(target) => ViewBinding(lookupData(reify(target)))
case avb @ AggregateViewBinding(map, targetOpt) => data match {
@@ -199,51 +201,51 @@ private[chisel3] object Lookupable {
* 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
+ * to that parent and set their parents to be in this underlying 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 module original or clone to be underlying 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]] = {
+ private[chisel3] def cloneModuleToContext[T <: BaseModule](module: Underlying[T], context: BaseModule)
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Underlying[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))
+ def rec[A <: BaseModule](m: A): Underlying[A] = {
+ def clone(x: A, p: Option[BaseModule], name: () => String): Underlying[A] = {
+ val newChild = Module.do_pseudo_apply(new internal.BaseModule.InstanceClone(x, name))
newChild._parent = p
- Right(newChild)
+ Clone(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 (c, ctx) if ctx == c => Proto(c)
+ case (c, ctx: IsClone[_]) if ctx.hasSameProto(c) => Clone(ctx.asInstanceOf[IsClone[A]])
+ case (c, ctx) if c._parent.isEmpty => Proto(c)
case (_, _) =>
- cloneModuleToContext(Left(m._parent.get), context) match {
- case Left(p) => Left(m)
- case Right(p: BaseModule) =>
+ cloneModuleToContext(Proto(m._parent.get), context) match {
+ case Proto(p) => Proto(m)
+ case Clone(p: BaseModule) =>
clone(m, Some(p), () => m.instanceName)
}
}
}
module match {
- case Left(m) => rec(m)
- case Right(m: ModuleClone[_]) =>
+ case Proto(m) => rec(m)
+ case Clone(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))
+ case Proto(mx) => Clone(mx)
+ case Clone(i: InstanceClone[_]) =>
+ val newChild = Module.do_pseudo_apply(new InstanceClone(m.getProto, () => m.instanceName))
newChild._parent = i._parent
- Right(newChild)
+ Clone(newChild)
}
- case Right(m: InstanceClone[_]) =>
+ case Clone(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))
+ case Proto(mx) => Clone(mx)
+ case Clone(i: InstanceClone[_]) =>
+ val newChild = Module.do_pseudo_apply(new InstanceClone(m.getProto, () => m.instanceName))
newChild._parent = i._parent
- Right(newChild)
+ Clone(newChild)
}
}
}
@@ -259,14 +261,14 @@ private[chisel3] object Lookupable {
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))
+ new Instance(cloneModuleToContext(ret.underlying, definition.getInnerDataContext.get))
}
def instanceLookup[A](that: A => Instance[B], instance: Instance[A]): C = {
val ret = that(instance.proto)
- instance.cloned match {
+ instance.underlying 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))
+ case Proto(_) => new Instance(ret.underlying)
+ case Clone(_) => new Instance(cloneModuleToContext(ret.underlying, instance.getInnerDataContext.get))
}
}
}
@@ -275,14 +277,14 @@ private[chisel3] object Lookupable {
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))
+ new Instance(cloneModuleToContext(Proto(ret), definition.getInnerDataContext.get))
}
def instanceLookup[A](that: A => B, instance: Instance[A]): C = {
val ret = that(instance.proto)
- instance.cloned match {
+ instance.underlying 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))
+ case Proto(_) => new Instance(Proto(ret))
+ case Clone(_) => new Instance(cloneModuleToContext(Proto(ret), instance.getInnerDataContext.get))
}
}
}
@@ -299,9 +301,9 @@ private[chisel3] object Lookupable {
}
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)
+ val ioMap: Option[Map[Data, Data]] = instance.underlying match {
+ case Clone(x: ModuleClone[_]) => Some(x.ioMap)
+ case Proto(x: BaseModule) => Some(x.getChiselPorts.map { case (_, data) => data -> data }.toMap)
case _ => None
}
if (isView(ret)) {
@@ -342,15 +344,19 @@ private[chisel3] object Lookupable {
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))
+ val underlying = new InstantiableClone[B] {
+ val getProto = ret
+ lazy val _innerContext = definition
+ }
+ new Instance(Clone(underlying))
}
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))
+ val underlying = new InstantiableClone[B] {
+ val getProto = ret
+ lazy val _innerContext = instance
+ }
+ new Instance(Clone(underlying))
}
}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Underlying.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Underlying.scala
new file mode 100644
index 00000000..864cc8af
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Underlying.scala
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import chisel3.internal.BaseModule.IsClone
+
+/** Represents the underlying implementation of a Definition or Instance */
+sealed trait Underlying[+T]
+
+/** A clone of a real implementation */
+final case class Clone[+T](isClone: IsClone[T]) extends Underlying[T]
+
+/** An actual implementation */
+final case class Proto[+T](proto: T) extends Underlying[T]
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index 57e7578a..55f89ae7 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -6,7 +6,7 @@ import scala.util.DynamicVariable
import scala.collection.mutable.ArrayBuffer
import chisel3._
import chisel3.experimental._
-import chisel3.experimental.hierarchy.Instance
+import chisel3.experimental.hierarchy.{Instance, Clone}
import chisel3.internal.firrtl._
import chisel3.internal.naming._
import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget}
@@ -561,6 +561,8 @@ private[chisel3] object Builder extends LazyLogging {
// A bare api call is, e.g. calling Wire() from the scala console).
)
}
+ def hasDynamicContext: Boolean = dynamicContextVar.value.isDefined
+
def readyForModuleConstr: Boolean = dynamicContext.readyForModuleConstr
def readyForModuleConstr_=(target: Boolean): Unit = {
dynamicContext.readyForModuleConstr = target
@@ -670,8 +672,8 @@ 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 (id: Instance[_]) => id.underlying match {
+ case Clone(m: internal.BaseModule.ModuleClone[_]) => namer(m.getPorts, prefix)
case _ =>
}
case (id: HasId) => namer(id, prefix)
diff --git a/docs/src/cookbooks/cookbook.md b/docs/src/cookbooks/cookbook.md
index ce49b668..4b2b088e 100644
--- a/docs/src/cookbooks/cookbook.md
+++ b/docs/src/cookbooks/cookbook.md
@@ -88,13 +88,14 @@ you are tying off, you can use `chiselTypeOf`:
```scala mdoc:silent:reset
import chisel3._
+import chisel3.stage.ChiselStage
class MyBundle extends Bundle {
val foo = UInt(4.W)
val bar = Vec(4, UInt(1.W))
}
-class Foo(typ: Data) extends RawModule {
+class Foo(typ: MyBundle) extends RawModule {
val bundleA = IO(Output(typ))
val bundleB = IO(Output(typ))
@@ -107,9 +108,7 @@ class Foo(typ: Data) extends RawModule {
bundleB := 0.U.asTypeOf(chiselTypeOf(bundleB))
}
-class Bar extends RawModule {
- val foo = Module(new Foo(new MyBundle()))
-}
+ChiselStage.emitVerilog(new Foo(new MyBundle))
```
### How do I create a Vec of Bools from a UInt?
diff --git a/docs/src/cookbooks/hierarchy.md b/docs/src/cookbooks/hierarchy.md
index 91d99aa6..350d20eb 100644
--- a/docs/src/cookbooks/hierarchy.md
+++ b/docs/src/cookbooks/hierarchy.md
@@ -10,6 +10,8 @@ section: "chisel3"
* [How do I access internal fields of an instance?](#how-do-i-access-internal-fields-of-an-instance)
* [How do I make my parameters accessable from an instance?](#how-do-i-make-my-parameters-accessable-from-an-instance)
* [How do I reuse a previously elaborated module, if my new module has the same parameterization?](#how-do-i-reuse-a-previously-elaborated-module-if-my-new-module-has-the-same-parameterization)
+* [How do I parameterize a module by its children instances?](#how-do-I-parameterize-a-module-by-its-children-instances)
+* [How do I use the new hierarchy-specific Select functions?](#how-do-I-use-the-new-hierarchy-specific-Select-functions)
## How do I instantiate multiple instances with the same module parameterization?
@@ -202,3 +204,58 @@ class AddTwo(addOneDef: Definition[AddOne]) extends Module {
```scala mdoc:verilog
chisel3.stage.ChiselStage.emitVerilog(new AddTwo(Definition(new AddOne(10))))
```
+
+## How do I use the new hierarchy-specific Select functions?
+
+Select functions can be applied after a module has been elaborated, either in a Chisel Aspect or in a parent module applied to a child module.
+
+There are six hierarchy-specific functions, which either return `Instance`'s or `Definition`'s:
+ - `instancesIn(parent)`: Return all instances directly instantiated locally within `parent`
+ - `instancesOf[type](parent)`: Return all instances of provided `type` directly instantiated locally within `parent`
+ - `allInstancesOf[type](root)`: Return all instances of provided `type` directly and indirectly instantiated, locally and deeply, starting from `root`
+ - `definitionsIn`: Return definitions of all instances directly instantiated locally within `parent`
+ - `definitionsOf[type]`: Return definitions of all instances of provided `type` directly instantiated locally within `parent`
+ - `allDefinitionsOf[type]`: Return all definitions of instances of provided `type` directly and indirectly instantiated, locally and deeply, starting from `root`
+
+To demonstrate this, consider the following. We mock up an example where we are using the `Select.allInstancesOf` and `Select.allDefinitionsOf` to annotate instances and the definition of `EmptyModule`. When converting the `ChiselAnnotation` to firrtl's `Annotation`, we print out the resulting `Target`. As shown, despite `EmptyModule` actually only being elaborated once, we still provide different targets depending on how the instance or definition is selected.
+
+```scala mdoc:reset
+import chisel3._
+import chisel3.experimental.hierarchy.{Definition, Instance, Hierarchy, instantiable, public}
+import firrtl.annotations.{IsModule, NoTargetAnnotation}
+case object EmptyAnnotation extends NoTargetAnnotation
+case class MyChiselAnnotation(m: Hierarchy[RawModule], tag: String) extends experimental.ChiselAnnotation {
+ def toFirrtl = {
+ println(tag + ": " + m.toTarget)
+ EmptyAnnotation
+ }
+}
+
+@instantiable
+class EmptyModule extends Module {
+ println("Elaborating EmptyModule!")
+}
+
+@instantiable
+class TwoEmptyModules extends Module {
+ val definition = Definition(new EmptyModule)
+ val i0 = Instance(definition)
+ val i1 = Instance(definition)
+}
+
+class Top extends Module {
+ val definition = Definition(new TwoEmptyModules)
+ val instance = Instance(definition)
+ aop.Select.allInstancesOf[EmptyModule](instance).foreach { i =>
+ experimental.annotate(MyChiselAnnotation(i, "instance"))
+ }
+ aop.Select.allDefinitionsOf[EmptyModule](instance).foreach { d =>
+ experimental.annotate(MyChiselAnnotation(d, "definition"))
+ }
+}
+```
+```scala mdoc:passthrough
+println("```")
+val x = chisel3.stage.ChiselStage.emitFirrtl(new Top)
+println("```")
+```
diff --git a/macros/src/main/scala/chisel3/internal/InstantiableMacro.scala b/macros/src/main/scala/chisel3/internal/InstantiableMacro.scala
index 15f69848..18c6c7aa 100644
--- a/macros/src/main/scala/chisel3/internal/InstantiableMacro.scala
+++ b/macros/src/main/scala/chisel3/internal/InstantiableMacro.scala
@@ -14,18 +14,21 @@ private[chisel3] object instantiableMacro {
def processBody(stats: Seq[Tree]): (Seq[Tree], Iterable[Tree]) = {
val extensions = scala.collection.mutable.ArrayBuffer.empty[Tree]
extensions += q"implicit val mg = new chisel3.internal.MacroGenerated{}"
+ // Note the triple `_` prefixing `module` is to avoid conflicts if a user marks a 'val module'
+ // with @public; in this case, the lookup code is ambiguous between the generated `def module`
+ // function and the argument to the generated implicit class.
val resultStats = stats.flatMap {
case x @ q"@public val $tpname: $tpe = $name" if tpname.toString() == name.toString() =>
- extensions += atPos(x.pos)(q"def $tpname = module._lookup(_.$tpname)")
+ extensions += atPos(x.pos)(q"def $tpname = ___module._lookup(_.$tpname)")
Nil
case x @ q"@public val $tpname: $tpe = $_" =>
- extensions += atPos(x.pos)(q"def $tpname = module._lookup(_.$tpname)")
+ extensions += atPos(x.pos)(q"def $tpname = ___module._lookup(_.$tpname)")
Seq(x)
case x @ q"@public val $tpname: $tpe" =>
- extensions += atPos(x.pos)(q"def $tpname = module._lookup(_.$tpname)")
+ extensions += atPos(x.pos)(q"def $tpname = ___module._lookup(_.$tpname)")
Seq(x)
case x @ q"@public lazy val $tpname: $tpe = $_" =>
- extensions += atPos(x.pos)(q"def $tpname = module._lookup(_.$tpname)")
+ extensions += atPos(x.pos)(q"def $tpname = ___module._lookup(_.$tpname)")
Seq(x)
case other =>
Seq(other)
@@ -42,17 +45,19 @@ private[chisel3] object instantiableMacro {
val defname = TypeName(tpname + c.freshName())
val instname = TypeName(tpname + c.freshName())
val (newStats, extensions) = processBody(stats)
+ val argTParams = tparams.map(_.name)
(q""" $mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents with chisel3.experimental.hierarchy.IsInstantiable { $self => ..$newStats } """,
- Seq(q"""implicit class $defname(module: chisel3.experimental.hierarchy.Definition[$tpname]) { ..$extensions }""",
- q"""implicit class $instname(module: chisel3.experimental.hierarchy.Instance[$tpname]) { ..$extensions } """),
+ Seq(q"""implicit class $defname[..$tparams](___module: chisel3.experimental.hierarchy.Definition[$tpname[..$argTParams]]) { ..$extensions }""",
+ q"""implicit class $instname[..$tparams](___module: chisel3.experimental.hierarchy.Instance[$tpname[..$argTParams]]) { ..$extensions } """),
tpname)
case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
val defname = TypeName(tpname + c.freshName())
val instname = TypeName(tpname + c.freshName())
val (newStats, extensions) = processBody(stats)
+ val argTParams = tparams.map(_.name)
(q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents with chisel3.experimental.hierarchy.IsInstantiable { $self => ..$newStats }",
- Seq(q"""implicit class $defname(module: chisel3.experimental.hierarchy.Definition[$tpname]) { ..$extensions }""",
- q"""implicit class $instname(module: chisel3.experimental.hierarchy.Instance[$tpname]) { ..$extensions } """),
+ Seq(q"""implicit class $defname[..$tparams](___module: chisel3.experimental.hierarchy.Definition[$tpname[..$argTParams]]) { ..$extensions }""",
+ q"""implicit class $instname[..$tparams](___module: chisel3.experimental.hierarchy.Instance[$tpname[..$argTParams]]) { ..$extensions } """),
tpname)
}
val newObj = objOpt match {
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 18436fb4..2690aa31 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -25,4 +25,4 @@ addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.0.1")
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.10")
// From FIRRTL for building from source
-addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.31")
+addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.32")
diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala
index 9c7320ce..8f5a2577 100644
--- a/src/main/scala/chisel3/aop/Select.scala
+++ b/src/main/scala/chisel3/aop/Select.scala
@@ -6,10 +6,12 @@ import chisel3._
import chisel3.internal.{HasId}
import chisel3.experimental.BaseModule
import chisel3.experimental.FixedPoint
-import chisel3.internal.firrtl._
+import chisel3.internal.firrtl.{Definition => DefinitionIR, _}
+import chisel3.experimental.hierarchy._
import chisel3.internal.PseudoModule
import chisel3.internal.BaseModule.ModuleClone
import firrtl.annotations.ReferenceTarget
+import scala.reflect.runtime.universe.TypeTag
import scala.collection.mutable
import chisel3.internal.naming.chiselName
@@ -22,7 +24,6 @@ object Select {
/** Return just leaf components of expanded node
*
* @param d Component to find leafs if aggregate typed. Intermediate fields/indicies are not included
- * @return
*/
def getLeafs(d: Data): Seq[Data] = d match {
case r: Record => r.getElements.flatMap(getLeafs)
@@ -33,7 +34,6 @@ object Select {
/** Return all expanded components, including intermediate aggregate nodes
*
* @param d Component to find leafs if aggregate typed. Intermediate fields/indicies ARE included
- * @return
*/
def getIntermediateAndLeafs(d: Data): Seq[Data] = d match {
case r: Record => r +: r.getElements.flatMap(getIntermediateAndLeafs)
@@ -41,15 +41,156 @@ object Select {
case other => Seq(other)
}
+ /** Selects all instances/modules directly instantiated within given definition
+ *
+ * @param parent
+ */
+ def instancesIn(parent: Hierarchy[BaseModule]): Seq[Instance[BaseModule]] = {
+ check(parent)
+ implicit val mg = new chisel3.internal.MacroGenerated{}
+ parent.proto._component.get match {
+ case d: DefModule => d.commands.collect {
+ case d: DefInstance =>
+ d.id match {
+ case p: chisel3.internal.BaseModule.IsClone[_] =>
+ parent._lookup { x => new Instance(Clone(p)).asInstanceOf[Instance[BaseModule]] }
+ case other: BaseModule =>
+ parent._lookup { x => other }
+ }
+ }
+ case other => Nil
+ }
+ }
+
+ /** Selects all Instances of instances/modules directly instantiated within given module, of provided type
+ *
+ * @note IMPORTANT: this function requires summoning a TypeTag[T], which will fail if T is an inner class.
+ * @param parent hierarchy which instantiates the returned Definitions
+ */
+ def instancesOf[T <: BaseModule : TypeTag](parent: Hierarchy[BaseModule]): Seq[Instance[T]] = {
+ check(parent)
+ implicit val mg = new chisel3.internal.MacroGenerated{}
+ parent.proto._component.get match {
+ case d: DefModule => d.commands.flatMap {
+ case d: DefInstance =>
+ d.id match {
+ case p: chisel3.internal.BaseModule.IsClone[_] =>
+ val i = parent._lookup { x => new Instance(Clone(p)).asInstanceOf[Instance[BaseModule]] }
+ if(i.isA[T]) Some(i.asInstanceOf[Instance[T]]) else None
+ case other: BaseModule =>
+ val i = parent._lookup { x => other }
+ if(i.isA[T]) Some(i.asInstanceOf[Instance[T]]) else None
+ }
+ case other => None
+ }
+ case other => Nil
+ }
+ }
+
+ /** Selects all Instances directly and indirectly instantiated within given root hierarchy, of provided type
+ *
+ * @note IMPORTANT: this function requires summoning a TypeTag[T], which will fail if T is an inner class.
+ * @param root top of the hierarchy to search for instances/modules of given type
+ */
+ def allInstancesOf[T <: BaseModule : TypeTag](root: Hierarchy[BaseModule]): Seq[Instance[T]] = {
+ val soFar = if(root.isA[T]) Seq(root.toInstance.asInstanceOf[Instance[T]]) else Nil
+ val allLocalInstances = instancesIn(root)
+ soFar ++ (allLocalInstances.flatMap(allInstancesOf[T]))
+ }
+
+ /** Selects the Definitions of all instances/modules directly instantiated within given module
+ *
+ * @param parent
+ */
+ def definitionsIn(parent: Hierarchy[BaseModule]): Seq[Definition[BaseModule]] = {
+ type DefType = Definition[BaseModule]
+ implicit val mg = new chisel3.internal.MacroGenerated{}
+ check(parent)
+ val defs = parent.proto._component.get match {
+ case d: DefModule => d.commands.collect {
+ case i: DefInstance =>
+ i.id match {
+ case p: chisel3.internal.BaseModule.IsClone[_] =>
+ parent._lookup { x => new Definition(Proto(p.getProto)).asInstanceOf[Definition[BaseModule]] }
+ case other: BaseModule =>
+ parent._lookup { x => other.toDefinition }
+ }
+ }
+ case other => Nil
+ }
+ val (_, defList) = defs.foldLeft((Set.empty[DefType], List.empty[DefType])) { case ((set, list), definition: Definition[BaseModule]) =>
+ if(set.contains(definition)) (set, list) else (set + definition, definition +: list)
+ }
+ defList.reverse
+ }
+
+
+ /** Selects all Definitions of instances/modules directly instantiated within given module, of provided type
+ *
+ * @note IMPORTANT: this function requires summoning a TypeTag[T], which will fail if T is an inner class.
+ * @param parent hierarchy which instantiates the returned Definitions
+ */
+ def definitionsOf[T <: BaseModule : TypeTag](parent: Hierarchy[BaseModule]): Seq[Definition[T]] = {
+ check(parent)
+ implicit val mg = new chisel3.internal.MacroGenerated{}
+ type DefType = Definition[T]
+ val defs = parent.proto._component.get match {
+ case d: DefModule => d.commands.flatMap {
+ case d: DefInstance =>
+ d.id match {
+ case p: chisel3.internal.BaseModule.IsClone[_] =>
+ val d = parent._lookup { x => new Definition(Clone(p)).asInstanceOf[Definition[BaseModule]] }
+ if(d.isA[T]) Some(d.asInstanceOf[Definition[T]]) else None
+ case other: BaseModule =>
+ val d = parent._lookup { x => other.toDefinition }
+ if(d.isA[T]) Some(d.asInstanceOf[Definition[T]]) else None
+ }
+ case other => None
+ }
+ }
+ val (_, defList) = defs.foldLeft((Set.empty[DefType], List.empty[DefType])) { case ((set, list), definition: Definition[T]) =>
+ if(set.contains(definition)) (set, list) else (set + definition, definition +: list)
+ }
+ defList.reverse
+ }
+
+ /** Selects all Definition's directly and indirectly instantiated within given root hierarchy, of provided type
+ *
+ * @note IMPORTANT: this function requires summoning a TypeTag[T], which will fail if T is an inner class, i.e.
+ * a class defined within another class.
+ * @param root top of the hierarchy to search for definitions of given type
+ */
+ def allDefinitionsOf[T <: BaseModule : TypeTag](root: Hierarchy[BaseModule]): Seq[Definition[T]] = {
+ type DefType = Definition[T]
+ val allDefSet = mutable.HashSet[Definition[BaseModule]]()
+ val defSet = mutable.HashSet[DefType]()
+ val defList = mutable.ArrayBuffer[DefType]()
+ def rec(hier: Definition[BaseModule]): Unit = {
+ if(hier.isA[T] && !defSet.contains(hier.asInstanceOf[DefType])) {
+ defSet += hier.asInstanceOf[DefType]
+ defList += hier.asInstanceOf[DefType]
+ }
+ allDefSet += hier
+ val allDefs = definitionsIn(hier)
+ allDefs.collect {
+ case d if !allDefSet.contains(d) => rec(d)
+ }
+ }
+ rec(root.toDefinition)
+ defList.toList
+ }
+
/** Collects all components selected by collector within module and all children modules it instantiates
* directly or indirectly
* Accepts a collector function, rather than a collector partial function (see [[collectDeep]])
+ *
+ * @note This API will not work with the new experimental hierarchy package. Instead, use allInstancesOf or allDefinitionsOf.
+ *
* @param module Module to collect components, as well as all children module it directly and indirectly instantiates
* @param collector Collector function to pick, given a module, which components to collect
* @param tag Required for generics to work, should ignore this
* @tparam T Type of the component that will be collected
- * @return
*/
def getDeep[T](module: BaseModule)(collector: BaseModule => Seq[T]): Seq[T] = {
check(module)
@@ -63,11 +204,13 @@ object Select {
/** Collects all components selected by collector within module and all children modules it instantiates
* directly or indirectly
* Accepts a collector partial function, rather than a collector function (see [[getDeep]])
+ *
+ * @note This API will not work with the new experimental hierarchy package. Instead, use allInstancesOf or allDefinitionsOf.
+ *
* @param module Module to collect components, as well as all children module it directly and indirectly instantiates
* @param collector Collector partial function to pick, given a module, which components to collect
* @param tag Required for generics to work, should ignore this
* @tparam T Type of the component that will be collected
- * @return
*/
def collectDeep[T](module: BaseModule)(collector: PartialFunction[BaseModule, T]): Iterable[T] = {
check(module)
@@ -78,9 +221,11 @@ object Select {
myItems ++ deepChildrenItems
}
- /** Selects all instances directly instantiated within given module
+ /** Selects all modules directly instantiated within given module
+ *
+ * @note This API will not work with the new experimental hierarchy package. Instead, use instancesIn or definitionsIn.
+ *
* @param module
- * @return
*/
def instances(module: BaseModule): Seq[BaseModule] = {
check(module)
@@ -88,7 +233,7 @@ object Select {
case d: DefModule => d.commands.flatMap {
case i: DefInstance => i.id match {
case m: ModuleClone[_] if !m._madeFromDefinition => None
- case _: PseudoModule => throw new Exception("Aspect APIs are currently incompatible with Definition/Instance")
+ case _: PseudoModule => throw new Exception("instances, collectDeep, and getDeep are currently incompatible with Definition/Instance!")
case other => Some(other)
}
case _ => None
@@ -99,7 +244,6 @@ object Select {
/** Selects all registers directly instantiated within given module
* @param module
- * @return
*/
def registers(module: BaseModule): Seq[Data] = {
check(module)
@@ -111,7 +255,6 @@ object Select {
/** Selects all ios directly contained within given module
* @param module
- * @return
*/
def ios(module: BaseModule): Seq[Data] = {
check(module)
@@ -120,7 +263,6 @@ object Select {
/** Selects all SyncReadMems directly contained within given module
* @param module
- * @return
*/
def syncReadMems(module: BaseModule): Seq[SyncReadMem[_]] = {
check(module)
@@ -131,7 +273,6 @@ object Select {
/** Selects all Mems directly contained within given module
* @param module
- * @return
*/
def mems(module: BaseModule): Seq[Mem[_]] = {
check(module)
@@ -142,7 +283,6 @@ object Select {
/** Selects all arithmetic or logical operators directly instantiated within given module
* @param module
- * @return
*/
def ops(module: BaseModule): Seq[(String, Data)] = {
check(module)
@@ -155,7 +295,6 @@ object Select {
* The kind of operators are contained in [[chisel3.internal.firrtl.PrimOp]]
* @param opKind the kind of operator, e.g. "mux", "add", or "bits"
* @param module
- * @return
*/
def ops(opKind: String)(module: BaseModule): Seq[Data] = {
check(module)
@@ -166,7 +305,6 @@ object Select {
/** Selects all wires in a module
* @param module
- * @return
*/
def wires(module: BaseModule): Seq[Data] = {
check(module)
@@ -177,7 +315,6 @@ object Select {
/** Selects all memory ports, including their direction and memory
* @param module
- * @return
*/
def memPorts(module: BaseModule): Seq[(Data, MemPortDirection, MemBase[_])] = {
check(module)
@@ -189,7 +326,6 @@ object Select {
/** Selects all memory ports of a given direction, including their memory
* @param dir The direction of memory ports to select
* @param module
- * @return
*/
def memPorts(dir: MemPortDirection)(module: BaseModule): Seq[(Data, MemBase[_])] = {
check(module)
@@ -200,7 +336,6 @@ object Select {
/** Selects all components who have been set to be invalid, even if they are later connected to
* @param module
- * @return
*/
def invalids(module: BaseModule): Seq[Data] = {
check(module)
@@ -211,7 +346,6 @@ object Select {
/** Selects all components who are attached to a given signal, within a module
* @param module
- * @return
*/
def attachedTo(module: BaseModule)(signal: Data): Set[Data] = {
check(module)
@@ -226,7 +360,6 @@ object Select {
* E.g. if signal = io.foo.bar, connectionsTo will return all connections to io, io.foo, and io.bar
* @param module
* @param signal
- * @return
*/
def connectionsTo(module: BaseModule)(signal: Data): Seq[PredicatedConnect] = {
check(module)
@@ -237,7 +370,7 @@ object Select {
var seenDef = isPort
searchWhens(module, (cmd: Command, preds) => {
cmd match {
- case cmd: Definition if cmd.id.isInstanceOf[Data] =>
+ case cmd: DefinitionIR if cmd.id.isInstanceOf[Data] =>
val x = getIntermediateAndLeafs(cmd.id.asInstanceOf[Data])
if(x.contains(signal)) prePredicates = preds
case Connect(_, loc@Node(d: Data), exp) =>
@@ -263,7 +396,6 @@ object Select {
/** Selects all stop statements, and includes the predicates surrounding the stop statement
*
* @param module
- * @return
*/
def stops(module: BaseModule): Seq[Stop] = {
val stops = mutable.ArrayBuffer[Stop]()
@@ -279,7 +411,6 @@ object Select {
/** Selects all printf statements, and includes the predicates surrounding the printf statement
*
* @param module
- * @return
*/
def printfs(module: BaseModule): Seq[Printf] = {
val printfs = mutable.ArrayBuffer[Printf]()
@@ -297,6 +428,7 @@ object Select {
require(module.isClosed, "Can't use Selector on modules that have not finished construction!")
require(module._component.isDefined, "Can't use Selector on modules that don't have components!")
}
+ private def check(hierarchy: Hierarchy[BaseModule]): Unit = check(hierarchy.proto)
// Given a loc, return all subcomponents of id that could be assigned to in connect
private def getEffected(a: Arg): Seq[Data] = a match {
diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
index 1a476f61..dc7e6487 100644
--- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
+++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
@@ -43,7 +43,7 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule](
injection: M => Unit
) extends Aspect[T] {
final def toAnnotation(top: T): AnnotationSeq = {
- val moduleNames = Select.collectDeep(top) { case i => i.name }.toSeq
+ val moduleNames = Select.allDefinitionsOf[chisel3.experimental.BaseModule](top.toDefinition).map{i => i.toTarget.module }.toSeq
toAnnotation(selectRoots(top), top.name, moduleNames)
}
diff --git a/src/main/scala/chisel3/util/Arbiter.scala b/src/main/scala/chisel3/util/Arbiter.scala
index 135700fa..b68acae1 100644
--- a/src/main/scala/chisel3/util/Arbiter.scala
+++ b/src/main/scala/chisel3/util/Arbiter.scala
@@ -10,6 +10,7 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because
/** IO bundle definition for an Arbiter, which takes some number of ready-valid inputs and outputs
* (selects) at most one.
+ * @groupdesc Signals The actual hardware fields of the Bundle
*
* @param gen data type
* @param n number of inputs
@@ -17,8 +18,20 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because
class ArbiterIO[T <: Data](private val gen: T, val n: Int) extends Bundle {
// See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs.
+/** Input data, one per potential sender
+ *
+ * @group Signals
+ */
val in = Flipped(Vec(n, Decoupled(gen)))
+/** Output data after arbitration
+ *
+ * @group Signals
+ */
val out = Decoupled(gen)
+/** One-Hot vector indicating which output was chosen
+ *
+ * @group Signals
+ */
val chosen = Output(UInt(log2Ceil(n).W))
}
diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala
index 060a684c..2a098f4d 100644
--- a/src/main/scala/chisel3/util/Decoupled.scala
+++ b/src/main/scala/chisel3/util/Decoupled.scala
@@ -16,6 +16,7 @@ import chisel3.internal.naming._ // can't use chisel3_ version because of compi
* while the consumer uses the flipped interface (inputs bits).
* The actual semantics of ready/valid are enforced via the use of concrete subclasses.
* @param gen the type of data to be wrapped in Ready/Valid
+ * @groupdesc Signals The actual hardware fields of the Bundle
*/
abstract class ReadyValidIO[+T <: Data](gen: T) extends Bundle
{
@@ -26,8 +27,19 @@ abstract class ReadyValidIO[+T <: Data](gen: T) extends Bundle
case _ => gen
}
+/** Indicates that the consumer is ready to accept the data this cycle
+ * @group Signals
+ */
val ready = Input(Bool())
+
+/** Indicates that the producer has put valid data in 'bits'
+ * @group Signals
+ */
val valid = Output(Bool())
+
+/** The data to be transferred when ready and valid are asserted at the same cycle
+ * @group Signals
+ */
val bits = Output(genType)
}
@@ -121,6 +133,7 @@ object Decoupled
* Additionally, once 'valid' is raised it will never be lowered until after
* 'ready' has also been raised.
* @param gen the type of data to be wrapped in IrrevocableIO
+ * @groupdesc Signals The actual hardware fields of the Bundle
*/
class IrrevocableIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen)
@@ -161,6 +174,7 @@ object DeqIO {
* @param gen The type of data to queue
* @param entries The max number of entries in the queue.
* @param hasFlush A boolean for whether the generated Queue is flushable
+ * @groupdesc Signals The hardware fields of the Bundle
*/
class QueueIO[T <: Data](private val gen: T, val entries: Int, val hasFlush: Boolean = false) extends Bundle
{ // See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs.
@@ -169,13 +183,21 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int, val hasFlush: Boo
* but internally, the queue implementation itself sits on the other side
* of the interface so uses the flipped instance.
*/
- /** I/O to enqueue data (client is producer, and Queue object is consumer), is [[Chisel.DecoupledIO]] flipped. */
+ /** I/O to enqueue data (client is producer, and Queue object is consumer), is [[Chisel.DecoupledIO]] flipped.
+ * @group Signals
+ */
val enq = Flipped(EnqIO(gen))
- /** I/O to dequeue data (client is consumer and Queue object is producer), is [[Chisel.DecoupledIO]]*/
+ /** I/O to dequeue data (client is consumer and Queue object is producer), is [[Chisel.DecoupledIO]]
+ * @group Signals
+ */
val deq = Flipped(DeqIO(gen))
- /** The current amount of data in the queue */
+ /** The current amount of data in the queue
+ * @group Signals
+ */
val count = Output(UInt(log2Ceil(entries + 1).W))
- /** When asserted, reset the enqueue and dequeue pointers, effectively flushing the queue (Optional IO for a flushable Queue)*/
+ /** When asserted, reset the enqueue and dequeue pointers, effectively flushing the queue (Optional IO for a flushable Queue)
+ * @group Signals
+ */
val flush = if (hasFlush) Some(Input(Bool())) else None
}
diff --git a/src/main/scala/chisel3/util/Valid.scala b/src/main/scala/chisel3/util/Valid.scala
index 4d348014..5d80502a 100644
--- a/src/main/scala/chisel3/util/Valid.scala
+++ b/src/main/scala/chisel3/util/Valid.scala
@@ -17,12 +17,17 @@ import chisel3._
* @tparam T the type of the data
* @param gen some data
* @see [[Valid$ Valid factory]] for concrete examples
+ * @groupdesc Signals The actual hardware fields of the Bundle
*/
class Valid[+T <: Data](gen: T) extends Bundle {
- /** A bit that will be asserted when `bits` is valid */
+ /** A bit that will be asserted when `bits` is valid
+ * @group Signals
+ */
val valid = Output(Bool())
- /** Some data */
+ /** The data to be transferred, qualified by `valid`
+ * @group Signals
+ */
val bits = Output(gen)
/** True when `valid` is asserted
@@ -173,13 +178,18 @@ class Pipe[T <: Data](val gen: T, val latency: Int = 1)(implicit compileOptions:
/** Interface for [[Pipe]]s composed of a [[Valid]] input and [[Valid]] output
* @define notAQueue
+ * @groupdesc Signals Hardware fields of the Bundle
*/
class PipeIO extends Bundle {
- /** [[Valid]] input */
+ /** [[Valid]] input
+ * @group Signals
+ */
val enq = Input(Valid(gen))
- /** [[Valid]] output. Data will appear here `latency` cycles after being valid at `enq`. */
+ /** [[Valid]] output. Data will appear here `latency` cycles after being valid at `enq`.
+ * @group Signals
+ */
val deq = Output(Valid(gen))
}
diff --git a/src/main/scala/chisel3/util/random/PRNG.scala b/src/main/scala/chisel3/util/random/PRNG.scala
index 9b42acf1..3a44385a 100644
--- a/src/main/scala/chisel3/util/random/PRNG.scala
+++ b/src/main/scala/chisel3/util/random/PRNG.scala
@@ -7,16 +7,23 @@ import chisel3.util.Valid
/** Pseudo Random Number Generators (PRNG) interface
* @param n the width of the LFSR
+ * @groupdesc Signals The actual hardware fields of the Bundle
*/
class PRNGIO(val n: Int) extends Bundle {
- /** A [[chisel3.util.Valid Valid]] interface that can be used to set the seed (internal PRNG state) */
+ /** A [[chisel3.util.Valid Valid]] interface that can be used to set the seed (internal PRNG state)
+ * @group Signals
+ */
val seed: Valid[Vec[Bool]] = Input(Valid(Vec(n, Bool())))
- /** When asserted, the PRNG will increment by one */
+ /** When asserted, the PRNG will increment by one
+ * @group Signals
+ */
val increment: Bool = Input(Bool())
- /** The current state of the PRNG */
+ /** The current state of the PRNG
+ * @group Signals
+ */
val out: Vec[Bool] = Output(Vec(n, Bool()))
}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala
index 43111fdd..eba412f1 100644
--- a/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala
+++ b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala
@@ -5,24 +5,20 @@ package chiselTests.experimental.hierarchy
import _root_.firrtl.annotations._
import chisel3.experimental.{annotate, BaseModule}
import chisel3.Data
-import chisel3.experimental.hierarchy.{Instance, Definition}
+import chisel3.experimental.hierarchy.{Instance, Definition, Hierarchy}
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 {
+ case class MarkChiselHierarchyAnnotation[B <: BaseModule](d: Hierarchy[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 mark[B <: BaseModule](d: Hierarchy[B], tag: String): Unit = annotate(MarkChiselHierarchyAnnotation(d, tag, true))
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))
+ def amark[B <: BaseModule](d: Hierarchy[B], tag: String): Unit = annotate(MarkChiselHierarchyAnnotation(d, tag, true))
}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala
index 94c0e551..d8ae7322 100644
--- a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala
+++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala
@@ -57,6 +57,18 @@ object Examples {
i1.in := i0.out
out := i1.out
}
+
+ @instantiable
+ class AddFour extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val definition = Definition(new AddTwoMixedModules)
+ @public val i0 = Instance(definition)
+ @public val i1 = Instance(definition)
+ i0.in := in
+ i1.in := i0.out
+ out := i1.out
+ }
@instantiable
class AggregatePortModule extends Module {
@public val io = IO(new Bundle {
@@ -192,4 +204,8 @@ object Examples {
class ConcreteHasBlah() extends HasBlah {
val blah = 10
}
+ @instantiable
+ class HasTypeParams[D <: Data](d: D) extends Module {
+ @public val blah = Wire(d)
+ }
}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
index 3866bf87..83084468 100644
--- a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
+++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
@@ -144,6 +144,15 @@ class InstanceSpec extends ChiselFunSpec with Utils {
val (_, annos) = getFirrtlAndAnnos(new Top)
annos should contain (MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire"))
}
+ it("1.11: should work on things with type parameters"){
+ class Top extends Module {
+ val definition = Definition(new HasTypeParams[UInt](UInt(3.W)))
+ val i0 = Instance(definition)
+ mark(i0.blah, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:HasTypeParams>blah".rt, "blah"))
+ }
}
describe("2: Annotations on designs not in the same chisel compilation") {
it("2.0: should work on an innerWire, marked in a different compilation") {
@@ -705,5 +714,142 @@ class InstanceSpec extends ChiselFunSpec with Utils {
}
}
}
+ describe("9: isA[..]") {
+ it("9.0: it should work on simple classes") {
+ class Top extends Module {
+ val d = Definition(new AddOne)
+ require(d.isA[AddOne])
+ }
+ getFirrtlAndAnnos(new Top)
+ }
+ it("9.1: it should not work on inner classes") {
+ class InnerClass extends Module
+ class Top extends Module {
+ val d = Definition(new InnerClass)
+ "require(d.isA[Module])" should compile // ensures that the test below is checking something useful
+ "require(d.isA[InnerClass])" shouldNot compile
+ }
+ getFirrtlAndAnnos(new Top)
+ }
+ it("9.2: it should work on super classes") {
+ class InnerClass extends Module
+ class Top extends Module {
+ val d = Definition(new InnerClass)
+ require(d.isA[Module])
+ }
+ getFirrtlAndAnnos(new Top)
+ }
+ it("9.2: it should work after casts") {
+ class Top extends Module {
+ val d0: Definition[Module] = Definition(new AddOne)
+ require(d0.isA[AddOne])
+ val d1: Definition[Module] = Definition((new AddOne).asInstanceOf[Module])
+ require(d1.isA[AddOne])
+ val i0: Instance[Module] = Instance(d0)
+ require(i0.isA[AddOne])
+ val i1: Instance[Module] = Instance(d1)
+ require(i1.isA[AddOne])
+ val i2: Instance[Module] = Instance(Definition(new AddOne))
+ require(i2.isA[AddOne])
+ }
+ getFirrtlAndAnnos(new Top)
+ }
+ }
+ describe("10: Select APIs") {
+ it("10.0: instancesOf") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddTwoMixedModules =>
+ val targets = aop.Select.instancesOf[AddOne](m.toDefinition).map { i: Instance[AddOne] => i.toTarget }
+ targets should be (Seq(
+ "~AddTwoMixedModules|AddTwoMixedModules/i0:AddOne".it,
+ "~AddTwoMixedModules|AddTwoMixedModules/i1:AddOne_2".it,
+ ))
+ })
+ getFirrtlAndAnnos(new AddTwoMixedModules, Seq(aspect))
+ }
+ it("10.1: instancesIn") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddTwoMixedModules =>
+ val insts = aop.Select.instancesIn(m.toDefinition)
+ val abs = insts.map { i: Instance[BaseModule] => i.toAbsoluteTarget }
+ val rel = insts.map { i: Instance[BaseModule] => i.toTarget }
+ abs should be (Seq(
+ "~AddTwoMixedModules|AddTwoMixedModules/i0:AddOne".it,
+ "~AddTwoMixedModules|AddTwoMixedModules/i1:AddOne_2".it,
+ ))
+ rel should be (Seq(
+ "~AddTwoMixedModules|AddTwoMixedModules/i0:AddOne".it,
+ "~AddTwoMixedModules|AddTwoMixedModules/i1:AddOne_2".it,
+ ))
+ })
+ getFirrtlAndAnnos(new AddTwoMixedModules, Seq(aspect))
+ }
+ it("10.2: allInstancesOf") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddFour =>
+ val insts = aop.Select.allInstancesOf[AddOne](m.toDefinition)
+ val abs = insts.map { i: Instance[AddOne] => i.in.toAbsoluteTarget }
+ val rel = insts.map { i: Instance[AddOne] => i.in.toTarget }
+ rel should be (Seq(
+ "~AddFour|AddFour/i0:AddTwoMixedModules/i0:AddOne>in".rt,
+ "~AddFour|AddFour/i0:AddTwoMixedModules/i1:AddOne_2>in".rt,
+ "~AddFour|AddFour/i1:AddTwoMixedModules/i0:AddOne>in".rt,
+ "~AddFour|AddFour/i1:AddTwoMixedModules/i1:AddOne_2>in".rt,
+ ))
+ abs should be (Seq(
+ "~AddFour|AddFour/i0:AddTwoMixedModules/i0:AddOne>in".rt,
+ "~AddFour|AddFour/i0:AddTwoMixedModules/i1:AddOne_2>in".rt,
+ "~AddFour|AddFour/i1:AddTwoMixedModules/i0:AddOne>in".rt,
+ "~AddFour|AddFour/i1:AddTwoMixedModules/i1:AddOne_2>in".rt,
+ ))
+ })
+ getFirrtlAndAnnos(new AddFour, Seq(aspect))
+ }
+ it("10.3: definitionsOf") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddTwoMixedModules =>
+ val targets = aop.Select.definitionsOf[AddOne](m.toDefinition).map { i: Definition[AddOne] => i.in.toTarget }
+ targets should be (Seq(
+ "~AddTwoMixedModules|AddOne>in".rt,
+ "~AddTwoMixedModules|AddOne_2>in".rt,
+ ))
+ })
+ getFirrtlAndAnnos(new AddTwoMixedModules, Seq(aspect))
+ }
+ it("10.4: definitionsIn") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddTwoMixedModules =>
+ val targets = aop.Select.definitionsIn(m.toDefinition).map { i: Definition[BaseModule] => i.toTarget }
+ targets should be (Seq(
+ "~AddTwoMixedModules|AddOne".mt,
+ "~AddTwoMixedModules|AddOne_2".mt,
+ ))
+ })
+ getFirrtlAndAnnos(new AddTwoMixedModules, Seq(aspect))
+ }
+ it("10.5: allDefinitionsOf") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddFour =>
+ val targets = aop.Select.allDefinitionsOf[AddOne](m.toDefinition).map { i: Definition[AddOne] => i.in.toTarget }
+ targets should be (Seq(
+ "~AddFour|AddOne>in".rt,
+ "~AddFour|AddOne_2>in".rt,
+ ))
+ })
+ getFirrtlAndAnnos(new AddFour, Seq(aspect))
+ }
+ it("10.6: Select.collectDeep should fail when combined with hierarchy package") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddFour =>
+ aop.Select.collectDeep(m) { case m: AddOne => m.toTarget }
+ })
+ intercept[Exception] { getFirrtlAndAnnos(new AddFour, Seq(aspect)) }
+ }
+ it("10.7: Select.getDeep should fail when combined with hierarchy package") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddFour =>
+ aop.Select.getDeep(m) { m: BaseModule => Nil }
+ })
+ intercept[Exception] { getFirrtlAndAnnos(new AddFour, Seq(aspect)) }
+ }
+ it("10.8: Select.instances should fail when combined with hierarchy package") {
+ val aspect = aop.inspecting.InspectingAspect({ m: AddFour =>
+ aop.Select.instances(m)
+ })
+ intercept[Exception] { getFirrtlAndAnnos(new AddFour, Seq(aspect)) }
+ }
+ }
}