summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorAditya Naik2024-08-02 15:20:25 -0700
committerAditya Naik2024-08-02 15:20:25 -0700
commit8947a5900e390c19524ab3013b33f0a8ec07ea43 (patch)
tree69426553c4a4155b30a3077c5c6b6e72d784e4e5 /core
parentb058bdcf8c142641aacd92f9938fb350a90e0762 (diff)
Update Module.scala
Diffstat (limited to 'core')
-rw-r--r--core/src/main/scala/chisel3/Module.scala93
-rw-r--r--core/src/main/scala/chisel3/internal/package.scala199
-rw-r--r--core/src/main/scala/chisel3/util/Naming.scala23
3 files changed, 266 insertions, 49 deletions
diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala
index 18749afa..212215ab 100644
--- a/core/src/main/scala/chisel3/Module.scala
+++ b/core/src/main/scala/chisel3/Module.scala
@@ -9,8 +9,9 @@ import chisel3.internal._
import chisel3.internal.Builder._
import chisel3.internal.firrtl._
import chisel3.experimental.BaseModule
-import _root_.firrtl.annotations.{IsModule, ModuleName, ModuleTarget}
+import _root_.firrtl.annotations.{IsModule, ModuleName, ModuleTarget, ReferenceTarget}
import _root_.firrtl.AnnotationSeq
+import chisel3.util.simpleClassName
object Module {
@@ -61,6 +62,8 @@ object Module {
val parentWhenStack = Builder.whenStack
// Save then clear clock and reset to prevent leaking scope, must be set again in the Module
+ // Note that Disable is a function of whatever the current reset is, so it does not need a port
+ // and thus does not change when we cross module boundaries
val (saveClock, saveReset) = (Builder.currentClock, Builder.currentReset)
val savePrefix = Builder.getPrefix
Builder.clearPrefix()
@@ -83,10 +86,6 @@ object Module {
"This is probably due to rewrapping a Module instance with Module()."
)
}
- Builder.currentModule = parent // Back to parent!
- Builder.whenStack = parentWhenStack
- Builder.currentClock = saveClock // Back to clock and reset scope
- Builder.currentReset = saveReset
// Only add the component if the module generates one
val componentOpt = module.generateComponent()
@@ -94,15 +93,15 @@ object Module {
Builder.components += component
}
+ // Reset Builder state *after* generating the component, so any
+ // atModuleBodyEnd generators are still within the scope of the
+ // current Module.
+ Builder.currentModule = parent // Back to parent!
+ Builder.whenStack = parentWhenStack
+ Builder.currentClock = saveClock // Back to clock and reset scope
+ Builder.currentReset = saveReset
Builder.setPrefix(savePrefix)
- // Handle connections at enclosing scope
- // We use _component because Modules that don't generate them may still have one
- if (Builder.currentModule.isDefined && module._component.isDefined) {
- val component = module._component.get
- pushCommand(DefInstance(module, component.ports))
- module.initializeInParent()
- }
module
}
@@ -170,8 +169,10 @@ abstract class Module extends RawModule {
final val clock: Clock = IO(Input(Clock())).suggestName("clock")
final val reset: Reset = IO(Input(mkReset)).suggestName("reset")
- // TODO It's hard to remove these deprecated override methods because they're used by
- // Chisel.QueueCompatibility which extends chisel3.Queue which extends chisel3.Module
+ override protected def implicitClock: Clock = clock
+ override protected def implicitReset: Reset = reset
+
+ // TODO Delete these
private var _override_clock: Option[Clock] = None
private var _override_reset: Option[Bool] = None
@deprecated("Use withClock at Module instantiation", "Chisel 3.5")
@@ -186,6 +187,7 @@ abstract class Module extends RawModule {
protected def override_reset_=(rhs: Option[Bool]): Unit = {
_override_reset = rhs
}
+ // End TODO Delete
private[chisel3] def mkReset: Reset = {
// Top module and compatibility mode use Bool for reset
@@ -299,7 +301,7 @@ package experimental {
//
protected var _closed = false
- /** Internal check if a Module is closed */
+ /** Internal check if a Module's constructor has finished executing */
private[chisel3] def isClosed = _closed
// Fresh Namespace because in Firrtl, Modules namespaces are disjoint with the global namespace
@@ -385,21 +387,7 @@ package experimental {
*
* @note If you want a custom or parametric name, override this method.
*/
- def desiredName: String = {
- /* The default module name is derived from the Java reflection derived class name. */
- val baseName = this.getClass.getName
-
- /* A sequence of string filters applied to the name */
- val filters: Seq[String => String] =
- Seq(((a: String) => raw"\$$+anon".r.replaceAllIn(a, "_Anon")) // Merge the "$$anon" name with previous name
- )
-
- filters
- .foldLeft(baseName) { case (str, filter) => filter(str) } // 1. Apply filters to baseName
- .split("\\.|\\$") // 2. Split string at '.' or '$'
- .filterNot(_.forall(_.isDigit)) // 3. Drop purely numeric names
- .last // 4. Use the last name
- }
+ def desiredName: String = simpleClassName(this.getClass)
/** Legalized name of this module. */
final lazy val name =
@@ -444,25 +432,32 @@ package experimental {
}
}
- /** Returns a FIRRTL ReferenceTarget that references this object, relative to an optional root.
- *
- * If `root` is defined, the target is a hierarchical path starting from `root`.
- *
- * If `root` is not defined, the target is a hierarchical path equivalent to `toAbsoluteTarget`.
- *
- * @note If `root` is defined, and has not finished elaboration, this must be called within `atModuleBodyEnd`.
- * @note The NamedComponent must be a descendant of `root`, if it is defined.
- * @note This doesn't have special handling for Views.
- */
- final def toRelativeTarget(root: Option[BaseModule]): ReferenceTarget = {
- val localTarget = toTarget
- def makeTarget(p: BaseModule) =
- p.toRelativeTarget(root).ref(localTarget.ref).copy(component = localTarget.component)
- _parent match {
- // case Some(ViewParent) => makeTarget(reifyParent) TODO add with datamirror
- case Some(parent) => makeTarget(parent)
- case None => localTarget
- }
+ /** Returns a FIRRTL ModuleTarget that references this object, relative to an optional root.
+ *
+ * If `root` is defined, the target is a hierarchical path starting from `root`.
+ *
+ * If `root` is not defined, the target is a hierarchical path equivalent to `toAbsoluteTarget`.
+ *
+ * @note If `root` is defined, and has not finished elaboration, this must be called within `atModuleBodyEnd`.
+ * @note The BaseModule must be a descendant of `root`, if it is defined.
+ * @note This doesn't have special handling for Views.
+ */
+ final def toRelativeTarget(root: Option[BaseModule]): IsModule = {
+ // If root was defined, and we are it, return this.
+ if (root.contains(this)) getTarget
+ // Otherwise check if root and _parent are defined.
+ else
+ (root, _parent) match {
+ // If root was defined, and we are not there yet, recurse up.
+ case (_, Some(parent)) => parent.toRelativeTarget(root).instOf(this.instanceName, name)
+ // If root was defined, and there is no parent, the root was not an ancestor.
+ case (Some(definedRoot), None) =>
+ throwException(
+ s"Requested .toRelativeTarget relative to ${definedRoot.name}, but it is not an ancestor of $this"
+ )
+ // If root was not defined, and there is no parent, return this.
+ case (None, None) => getTarget
+ }
}
/**
diff --git a/core/src/main/scala/chisel3/internal/package.scala b/core/src/main/scala/chisel3/internal/package.scala
new file mode 100644
index 00000000..25d9129f
--- /dev/null
+++ b/core/src/main/scala/chisel3/internal/package.scala
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3
+
+import firrtl.annotations.{IsModule, ModuleTarget}
+import chisel3.experimental.BaseModule
+import chisel3.internal.firrtl.{Component, DefModule} // TODO move to object ir
+import chisel3.internal.Builder.Prefix
+import chisel3.ChiselException
+
+import scala.util.Try
+import scala.annotation.implicitNotFound
+import scala.collection.mutable
+import scala.quoted
+import scala.quoted._
+
+package object internal {
+
+ /** Marker trait for modules that are not true modules */
+ private[chisel3] trait PseudoModule extends BaseModule
+
+ /* Check if a String name is a temporary name */
+ def isTemp(name: String): Boolean = name.nonEmpty && name.head == '_'
+
+ /** Creates a name String from a prefix and a seed
+ * @param prefix The prefix associated with the seed (must be in correct order, *not* reversed)
+ * @param seed The seed for computing the name (if available)
+ */
+ def buildName(seed: String, prefix: Prefix): String = {
+ // Don't bother copying the String if there's no prefix
+ if (prefix.isEmpty) {
+ seed
+ } else {
+ // Using Java's String builder to micro-optimize appending a String excluding 1st character
+ // for temporaries
+ val builder = new java.lang.StringBuilder()
+ // Starting with _ is the indicator of a temporary
+ val temp = isTemp(seed)
+ // Make sure the final result is also a temporary if this is a temporary
+ if (temp) {
+ builder.append('_')
+ }
+ prefix.foreach { p =>
+ builder.append(p)
+ builder.append('_')
+ }
+ if (temp) {
+ // We've moved the leading _ to the front, drop it here
+ builder.append(seed, 1, seed.length)
+ } else {
+ builder.append(seed)
+ }
+ builder.toString
+ }
+ }
+
+ // Sanitizes a name, e.g. from a `HasId`, by stripping all non ANSI-C characters
+ private[chisel3] def sanitize(s: String, leadingDigitOk: Boolean = false): String = {
+ // TODO what character set does FIRRTL truly support? using ANSI C for now
+ def legalStart(c: Char) = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
+ def legal(c: Char) = legalStart(c) || (c >= '0' && c <= '9')
+ val res = if (s.forall(legal)) s else s.filter(legal)
+ val headOk = (!res.isEmpty) && (leadingDigitOk || legalStart(res.head))
+ if (headOk) res else s"_$res"
+ }
+
+ // Workaround for https://github.com/chipsalliance/chisel/issues/4162
+ // We can't use the .asTypeOf workaround because this is used to implement .asTypeOf
+ private[chisel3] def _padHandleBool[A <: Bits](
+ x: A,
+ width: Int
+ )(using Quotes): A = x match {
+ case b: Bool if !b.isLit && width > 1 && Type.of[A] == Type.of[UInt] =>
+ val _pad = Wire(UInt(width.W))
+ _pad := b
+ _pad.asInstanceOf[A] // This cast is safe because we know A is UInt on this path
+ case u => u.pad(width)
+ }
+
+ // Resize that to this width (if known)
+ // TODO fix the "take not member of A" error here
+ // private[chisel3] def _resizeToWidth[A <: Bits](
+ // that: A,
+ // targetWidthOpt: Option[Int]
+ // )(fromUInt: UInt => A
+ // )(using Quotes): A =
+ // (targetWidthOpt, that.widthOption) match {
+ // case (Some(targetWidth), Some(thatWidth)) =>
+ // if (targetWidth == thatWidth) that
+ // else if (targetWidth > thatWidth) _padHandleBool(that, targetWidth)
+ // else fromUInt(that.take(targetWidth))
+ // case (Some(targetWidth), None) => fromUInt(_padHandleBool(that, targetWidth).take(targetWidth))
+ // case (None, Some(thatWidth)) => that
+ // case (None, None) => that
+ // }
+
+ /** Internal API for [[ViewParent]] */
+ sealed private[chisel3] class ViewParentAPI extends RawModule() with PseudoModule {
+ // We must provide `absoluteTarget` but not `toTarget` because otherwise they would be exactly
+ // the same and we'd have no way to distinguish the kind of target when renaming view targets in
+ // the Converter
+ // Note that this is not overriding .toAbsoluteTarget, that is a final def in BaseModule that delegates
+ // to this method
+ private[chisel3] val absoluteTarget: IsModule = ModuleTarget(this.circuitName, "_$$AbsoluteView$$_")
+
+ // This module is not instantiable
+ override private[chisel3] def generateComponent(): Option[Component] = None
+ override private[chisel3] def initializeInParent(): Unit = ()
+ // This module is not really part of the circuit
+ _parent = None
+
+ // Sigil to mark views, starts with '_' to make it a legal FIRRTL target
+ override def desiredName = "_$$View$$_"
+
+ private[chisel3] val fakeComponent: Component = DefModule(this, desiredName, Nil, Nil)
+ }
+
+ /** Special internal object representing the parent of all views
+ *
+ * @note this is a val instead of an object because of the need to wrap in Module(...)
+ * @note this is a lazy val so that calling functions in this package object doesn't create it
+ */
+ private[chisel3] lazy val ViewParent =
+ Module.apply(new ViewParentAPI)
+
+
+ // TODO this exists in cats.Traverse, should we just use that?
+ private[chisel3] implicit class ListSyntax[A](xs: List[A]) {
+ def mapAccumulate[B, C](z: B)(f: (B, A) => (B, C)): (B, List[C]) = {
+ val (zz, result) = xs.foldLeft((z, List.empty[C])) {
+ case ((acc, res), a) =>
+ val (accx, c) = f(acc, a)
+ (accx, c :: res)
+ }
+ (zz, result.reverse)
+ }
+ }
+
+ /** This is effectively a "LazyVal" box type, we can create the object but delay executing the argument
+ *
+ * @note This is similar to cats.Eval.later but we don't depend on Cats
+ */
+ private[chisel3] class Delayed[A](a: => A) {
+ lazy val value: A = a
+ }
+ private[chisel3] object Delayed {
+ def apply[A](a: => A): Delayed[A] = new Delayed(a)
+ }
+
+ /** The list of banned type alias words which will cause generation of bad FIRRTL. These are usually
+ * keyword tokens that would be automatically lexed by firtool, and so cause parsing errors.
+ */
+ private[chisel3] val illegalTypeAliases = Seq(
+ "FIRRTL",
+ "Clock",
+ "UInt",
+ "Reset",
+ "AsyncReset",
+ "Analog",
+ "Probe",
+ "RWProbe",
+ "version",
+ "type",
+ "circuit",
+ "parameter",
+ "input",
+ "output",
+ "extmodule",
+ "module",
+ "intmodule",
+ "intrinsic",
+ "defname",
+ "const",
+ "flip",
+ "reg",
+ "smem",
+ "cmem",
+ "mport",
+ "define",
+ "attach",
+ "inst",
+ "of",
+ "reset",
+ "printf",
+ "skip",
+ "node"
+ )
+
+ /** Similar to Seq.groupBy except that it preserves ordering of elements within each group */
+ private[chisel3] def groupByIntoSeq[A, K](xs: Iterable[A])(f: A => K): Seq[(K, Seq[A])] = {
+ val map = mutable.LinkedHashMap.empty[K, mutable.ListBuffer[A]]
+ for (x <- xs) {
+ val key = f(x)
+ val l = map.getOrElseUpdate(key, mutable.ListBuffer.empty[A])
+ l += x
+ }
+ map.view.map({ case (k, vs) => k -> vs.toList }).toList
+ }
+}
diff --git a/core/src/main/scala/chisel3/util/Naming.scala b/core/src/main/scala/chisel3/util/Naming.scala
new file mode 100644
index 00000000..a78744b2
--- /dev/null
+++ b/core/src/main/scala/chisel3/util/Naming.scala
@@ -0,0 +1,23 @@
+package chisel3.util
+
+/* Generates a safe 'simple class name' from the given class, avoiding `Malformed class name` exceptions from `getClass.getSimpleName`
+ * when Java 8 is used.
+ */
+object simpleClassName {
+
+ def apply[T](clazz: Class[T]): String = {
+ /* The default class name is derived from the Java reflection derived class name. */
+ val baseName = clazz.getName
+
+ /* A sequence of string filters applied to the name */
+ val filters: Seq[String => String] =
+ Seq(((a: String) => raw"\$$+anon".r.replaceAllIn(a, "_Anon")) // Merge the "$$anon" name with previous name
+ )
+
+ filters
+ .foldLeft(baseName) { case (str, filter) => filter(str) } // 1. Apply filters to baseName
+ .split("\\.|\\$") // 2. Split string at '.' or '$'
+ .filterNot(_.forall(_.isDigit)) // 3. Drop purely numeric names
+ .last // 4. Use the last name
+ }
+}