diff options
| author | Aditya Naik | 2024-08-02 15:20:25 -0700 |
|---|---|---|
| committer | Aditya Naik | 2024-08-02 15:20:25 -0700 |
| commit | 8947a5900e390c19524ab3013b33f0a8ec07ea43 (patch) | |
| tree | 69426553c4a4155b30a3077c5c6b6e72d784e4e5 | |
| parent | b058bdcf8c142641aacd92f9938fb350a90e0762 (diff) | |
Update Module.scala
| -rw-r--r-- | core/src/main/scala/chisel3/Module.scala | 93 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/package.scala | 199 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/util/Naming.scala | 23 |
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 + } +} |
