summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/scala/chisel3/Aggregate.scala8
-rw-r--r--core/src/main/scala/chisel3/Annotation.scala2
-rw-r--r--core/src/main/scala/chisel3/Bits.scala2
-rw-r--r--core/src/main/scala/chisel3/Data.scala16
-rw-r--r--core/src/main/scala/chisel3/Module.scala39
-rw-r--r--core/src/main/scala/chisel3/RawModule.scala131
-rw-r--r--core/src/main/scala/chisel3/experimental/ChiselEnum.scala2
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala266
-rw-r--r--core/src/main/scala/chisel3/internal/Error.scala2
-rw-r--r--core/src/main/scala/chisel3/internal/firrtl/IR.scala7
-rw-r--r--core/src/main/scala/chisel3/package.scala77
11 files changed, 357 insertions, 195 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala
index ab7fe53f..2c4f67db 100644
--- a/core/src/main/scala/chisel3/Aggregate.scala
+++ b/core/src/main/scala/chisel3/Aggregate.scala
@@ -148,8 +148,9 @@ class Vec[T <: Data] private[chisel3] (gen: => T, val vec_length: Int) extends A
// simpler.
private lazy val self: Seq[T] = {
val _self = Vector.fill(vec_length)(gen)
+ val thisNode = Node(this) // Share the same Node for all elements.
for ((elt, i) <- _self.zipWithIndex)
- elt.setRef(this, i)
+ elt.setRef(thisNode, i)
_self
}
@@ -225,7 +226,7 @@ class Vec[T <: Data] private[chisel3] (gen: => T, val vec_length: Int) extends A
port.bind(ChildBinding(this), reconstructedResolvedDirection)
val i = Vec.truncateIndex(p, length)
- port.setRef(this, i)
+ port.setRef(Node(this), i)
port
}
@@ -728,8 +729,9 @@ abstract class Record extends Aggregate {
!opaqueType || (elements.size == 1 && elements.head._1 == ""),
s"Opaque types must have exactly one element with an empty name, not ${elements.size}: ${elements.keys.mkString(", ")}"
)
+ val thisNode = Node(this) // Share the same Node for all elements.
for ((name, elt) <- elements) {
- elt.setRef(this, _namespace.name(name, leadingDigitOk = true), opaque = opaqueType)
+ elt.setRef(thisNode, _namespace.name(name, leadingDigitOk = true), opaque = opaqueType)
}
}
diff --git a/core/src/main/scala/chisel3/Annotation.scala b/core/src/main/scala/chisel3/Annotation.scala
index f527182f..fa51fdcd 100644
--- a/core/src/main/scala/chisel3/Annotation.scala
+++ b/core/src/main/scala/chisel3/Annotation.scala
@@ -3,7 +3,7 @@
package chisel3.experimental
import scala.language.existentials
-import chisel3.internal.{Builder, InstanceId}
+import chisel3.internal.Builder
import chisel3.{Data, RawModule}
import firrtl.Transform
import firrtl.annotations._
diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala
index 927b1c19..b0fb82e1 100644
--- a/core/src/main/scala/chisel3/Bits.scala
+++ b/core/src/main/scala/chisel3/Bits.scala
@@ -553,7 +553,7 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U
"Chisel 3.5"
)
-def unary_! : Bool = this === 0.U(1.W)
+ def unary_! : Bool = this === 0.U(1.W)
override def <<(that: Int): UInt =
binop(UInt(this.width + that), ShiftLeftOp, validateShiftAmount(that))
diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala
index 1a7a0244..370a79db 100644
--- a/core/src/main/scala/chisel3/Data.scala
+++ b/core/src/main/scala/chisel3/Data.scala
@@ -446,7 +446,7 @@ abstract class Data extends HasId with NamedComponent {
override def autoSeed(name: String): this.type = {
topBindingOpt match {
// Ports are special in that the autoSeed will keep the first name, not the last name
- case Some(PortBinding(m)) if hasAutoSeed && Builder.currentModule.contains(m) => this
+ case Some(PortBinding(m)) if hasSeed && Builder.currentModule.contains(m) => this
case _ => super.autoSeed(name)
}
}
@@ -604,12 +604,12 @@ abstract class Data extends HasId with NamedComponent {
): Unit = {
// requireIsHardware(this, s"data to be bulk-connected")
// requireIsHardware(that, s"data to be bulk-connected")
- (this.topBinding, that.topBinding) match {
- case (_: ReadOnlyBinding, _: ReadOnlyBinding) => throwException(s"Both $this and $that are read-only")
- // DontCare cannot be a sink (LHS)
- case (_: DontCareBinding, _) => throw BiConnect.DontCareCantBeSink
- case _ => // fine
- }
+ (this.topBinding, that.topBinding) match {
+ case (_: ReadOnlyBinding, _: ReadOnlyBinding) => throwException(s"Both $this and $that are read-only")
+ // DontCare cannot be a sink (LHS)
+ case (_: DontCareBinding, _) => throw BiConnect.DontCareCantBeSink
+ case _ => // fine
+ }
try {
BiConnect.connect(this, that, Builder.currentModule.get)
} catch {
@@ -1046,7 +1046,7 @@ object WireDefault {
/** RHS (source) for Invalidate API.
* Causes connection logic to emit a DefInvalid when connected to an output port (or wire).
*/
-final case object DontCare extends Element {
+case object DontCare extends Element {
// This object should be initialized before we execute any user code that refers to it,
// otherwise this "Chisel" object will end up on the UserModule's id list.
// We make it private to chisel3 so it has to be accessed through the package object.
diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala
index 3419cda0..727a1320 100644
--- a/core/src/main/scala/chisel3/Module.scala
+++ b/core/src/main/scala/chisel3/Module.scala
@@ -242,6 +242,13 @@ package experimental {
abstract class BaseModule extends HasId {
_parent.foreach(_.addId(this))
+ // Set if the returned top-level module of a nested call to the Chisel Builder, see Definition.apply
+ private var _circuitVar: BaseModule = null // using nullable var for better memory usage
+ private[chisel3] def _circuit: Option[BaseModule] = Option(_circuitVar)
+ private[chisel3] def _circuit_=(target: Option[BaseModule]): Unit = {
+ _circuitVar = target.getOrElse(null)
+ }
+
//
// Builder Internals - this tracks which Module RTL construction belongs to.
//
@@ -409,6 +416,27 @@ 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
+ }
+ }
+
/**
* Internal API. Returns a list of this module's generated top-level ports as a map of a String
* (FIRRTL name) to the IO object. Only valid after the module is closed.
@@ -454,17 +482,6 @@ package experimental {
names
}
- /** Invokes _onModuleClose on HasIds found via reflection but not bound to hardware
- * (thus not part of _ids)
- * This maintains old naming behavior for non-hardware Data
- */
- private[chisel3] def closeUnboundIds(names: HashMap[HasId, String]): Unit = {
- val idLookup = _ids.toSet
- for ((id, _) <- names if !idLookup(id)) {
- id._onModuleClose
- }
- }
-
/** Compatibility function. Allows Chisel2 code which had ports without the IO wrapper to
* compile under Bindings checks. Does nothing in non-compatibility mode.
*
diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala
index 8eeda03c..bab1a5b1 100644
--- a/core/src/main/scala/chisel3/RawModule.scala
+++ b/core/src/main/scala/chisel3/RawModule.scala
@@ -3,30 +3,105 @@
package chisel3
import scala.util.Try
-import scala.annotation.nowarn
import chisel3.experimental.BaseModule
import chisel3.internal._
-import chisel3.internal.BaseModule.ModuleClone
import chisel3.internal.Builder._
import chisel3.internal.firrtl._
import _root_.firrtl.annotations.{IsModule, ModuleTarget}
import scala.collection.immutable.VectorBuilder
+import scala.collection.mutable.ArrayBuffer
/** Abstract base class for Modules that contain Chisel RTL.
* This abstract base class is a user-defined module which does not include implicit clock and reset and supports
* multiple IO() declarations.
*/
-@nowarn("msg=class Port") // delete when Port becomes private
abstract class RawModule extends BaseModule {
+
+ /** Hook to invoke hardware generators after the rest of the Module is constructed.
+ *
+ * This is a power-user API, and should not normally be needed.
+ *
+ * In rare cases, it is necessary to run hardware generators at a late stage, but still within the scope of the
+ * Module. In these situations, atModuleBodyEnd may be used to register such generators. For example:
+ *
+ * {{{
+ * class Example extends RawModule {
+ * atModuleBodyEnd {
+ * val extraPort0 = IO(Output(Bool()))
+ * extraPort0 := 0.B
+ * }
+ * }
+ * }}}
+ *
+ * Any generators registered with atModuleBodyEnd are the last code to execute when the Module is constructed. The
+ * execution order is:
+ *
+ * - The constructors of any super classes or traits the Module extends
+ * - The constructor of the Module itself
+ * - The atModuleBodyEnd generators
+ *
+ * The atModuleBodyEnd generators execute in the lexical order they appear in the Module constructor.
+ *
+ * For example:
+ *
+ * {{{
+ * trait Parent {
+ * // Executes first.
+ * val foo = ...
+ * }
+ *
+ * class Example extends Parent {
+ * // Executes second.
+ * val bar = ...
+ *
+ * atModuleBodyEnd {
+ * // Executes fourth.
+ * val qux = ...
+ * }
+ *
+ * atModuleBodyEnd {
+ * // Executes fifth.
+ * val quux = ...
+ * }
+ *
+ * // Executes third..
+ * val baz = ...
+ * }
+ * }}}
+ *
+ * If atModuleBodyEnd is used in a Definition, any generated hardware will be included in the Definition. However, it
+ * is currently not possible to annotate any val within atModuleBodyEnd as @public.
+ */
+ protected def atModuleBodyEnd(gen: => Unit): Unit = {
+ _atModuleBodyEnd += { () => gen }
+ }
+ private val _atModuleBodyEnd = new ArrayBuffer[() => Unit]
+
//
// RTL construction internals
//
// Perhaps this should be an ArrayBuffer (or ArrayBuilder), but DefModule is public and has Seq[Command]
// so our best option is to share a single Seq datastructure with that
private val _commands = new VectorBuilder[Command]()
- private[chisel3] def addCommand(c: Command) = {
+
+ /** The current region to which commands will be added. */
+ private var _currentRegion = _commands
+
+ private[chisel3] def changeRegion(newRegion: VectorBuilder[Command]): Unit = {
+ _currentRegion = newRegion
+ }
+
+ private[chisel3] def withRegion[A](newRegion: VectorBuilder[Command])(thunk: => A): A = {
+ var oldRegion = _currentRegion
+ changeRegion(newRegion)
+ val result = thunk
+ changeRegion(oldRegion)
+ result
+ }
+
+ private[chisel3] def addCommand(c: Command): Unit = {
require(!_closed, "Can't write to module after module close")
- _commands += c
+ _currentRegion += c
}
protected def getCommands: Seq[Command] = {
require(_closed, "Can't get commands before module close")
@@ -159,49 +234,3 @@ trait RequireSyncReset extends Module {
override private[chisel3] def mkReset: Bool = Bool()
}
-/** Mix with a [[RawModule]] to automatically connect DontCare to the module's ports, wires, and children instance IOs. */
-
-package object internal {
-
- import scala.annotation.implicitNotFound
- @implicitNotFound("You are trying to access a macro-only API. Please use the @public annotation instead.")
- trait MacroGenerated
-
- /** Marker trait for modules that are not true modules */
- private[chisel3] trait PseudoModule extends BaseModule
-
- /* 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
- }
- }
-}
diff --git a/core/src/main/scala/chisel3/experimental/ChiselEnum.scala b/core/src/main/scala/chisel3/experimental/ChiselEnum.scala
index 9d69fe37..d0ad6949 100644
--- a/core/src/main/scala/chisel3/experimental/ChiselEnum.scala
+++ b/core/src/main/scala/chisel3/experimental/ChiselEnum.scala
@@ -7,7 +7,7 @@ import chisel3._
import chisel3.internal.Builder.pushOp
import chisel3.internal.firrtl.PrimOp._
import chisel3.internal.firrtl._
-import chisel3.internal.{throwException, Binding, Builder, ChildBinding, ConstrainedBinding, InstanceId}
+import chisel3.internal.{throwException, Binding, Builder, ChildBinding, ConstrainedBinding}
import firrtl.annotations._
object EnumAnnotations {
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index 5a6cd3a7..5782a2f5 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -11,13 +11,14 @@ import chisel3.internal.naming._
import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget}
import _root_.firrtl.annotations.AnnotationUtils.validComponentName
import _root_.firrtl.{AnnotationSeq, RenameMap}
+import _root_.firrtl.{ir => fir}
import chisel3.internal.Builder.Prefix
import logger.LazyLogging
import scala.collection.mutable
import scala.annotation.tailrec
-private[chisel3] class Namespace(keywords: Set[String]) {
+private[chisel3] class Namespace(keywords: Set[String], separator: Char = '_') {
// This HashMap is compressed, not every name in the namespace is present here.
// If the same name is requested multiple times, it only takes 1 entry in the HashMap and the
// value is incremented for each time the name is requested.
@@ -25,13 +26,12 @@ private[chisel3] class Namespace(keywords: Set[String]) {
// checking if a name is present in the Namespace is more complex than just checking the HashMap,
// see getIndex below.
private val names = collection.mutable.HashMap[String, Long]()
- def copyTo(other: Namespace): Unit = names.foreach { case (s: String, l: Long) => other.names(s) = l }
for (keyword <- keywords)
names(keyword) = 1
@tailrec
private def rename(n: String, index: Long): String = {
- val tryName = s"${n}_${index}"
+ val tryName = s"${n}${separator}${index}"
if (names.contains(tryName)) {
rename(n, index + 1)
} else {
@@ -40,15 +40,6 @@ private[chisel3] class Namespace(keywords: Set[String]) {
}
}
- private 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"
- }
-
/** Checks if `n` ends in `_\d+` and returns the substring before `_` if so, null otherwise */
// TODO can and should this be folded in to sanitize? Same iteration as the forall?
private def prefix(n: String): Int = {
@@ -58,8 +49,8 @@ private[chisel3] class Namespace(keywords: Set[String]) {
i -= 1
}
// Will get i == 0 for all digits or _\d+ with empty prefix, those have no prefix so returning 0 is correct
- if (i == n.size) 0 // no digits
- else if (n(i) != '_') 0 // no _
+ if (i >= (n.size - 1)) 0 // no digits
+ else if (n(i) != separator) 0 // no _
else i
}
@@ -99,6 +90,7 @@ private[chisel3] class Namespace(keywords: Set[String]) {
private[chisel3] object Namespace {
/** Constructs an empty Namespace */
+ def empty(separator: Char): Namespace = new Namespace(Set.empty[String], separator)
def empty: Namespace = new Namespace(Set.empty[String])
}
@@ -111,28 +103,7 @@ private[chisel3] class IdGen {
def value: Long = counter
}
-/** Public API to access Node/Signal names.
- * currently, the node's name, the full path name, and references to its parent Module and component.
- * These are only valid once the design has been elaborated, and should not be used during its construction.
- */
-trait InstanceId {
- def instanceName: String
- def pathName: String
- def parentPathName: String
- def parentModName: String
-
- /** Returns a FIRRTL Named that refers to this object in the elaborated hardware graph */
- def toNamed: Named
-
- /** Returns a FIRRTL IsMember that refers to this object in the elaborated hardware graph */
- def toTarget: IsMember
-
- /** Returns a FIRRTL IsMember that refers to the absolute path to this object in the elaborated hardware graph */
- def toAbsoluteTarget: IsMember
-}
-
-private[chisel3] trait HasId extends InstanceId {
- private[chisel3] def _onModuleClose: Unit = {}
+private[chisel3] trait HasId extends chisel3.InstanceId {
// using nullable var for better memory usage
private var _parentVar: BaseModule = Builder.currentModule.getOrElse(null)
private[chisel3] def _parent: Option[BaseModule] = Option(_parentVar)
@@ -140,38 +111,24 @@ private[chisel3] trait HasId extends InstanceId {
_parentVar = target.getOrElse(null)
}
- // Set if the returned top-level module of a nested call to the Chisel Builder, see Definition.apply
- private var _circuitVar: BaseModule = null // using nullable var for better memory usage
- private[chisel3] def _circuit: Option[BaseModule] = Option(_circuitVar)
- private[chisel3] def _circuit_=(target: Option[BaseModule]): Unit = {
- _circuitVar = target.getOrElse(null)
- }
-
private[chisel3] val _id: Long = Builder.idGen.next
// TODO: remove this, but its removal seems to cause a nasty Scala compiler crash.
override def hashCode: Int = super.hashCode()
override def equals(that: Any): Boolean = super.equals(that)
- // Contains suggested seed (user-decided seed)
+ // Did the user suggest a name? This overrides names suggested by the compiler plugin.
+ private var _nameIsSuggested: Boolean = false
+ // Contains name seed (either user suggested or generated by the plugin).
private var suggested_seedVar: String = null // using nullable var for better memory usage
private def suggested_seed: Option[String] = Option(suggested_seedVar)
- // Contains the seed computed automatically by the compiler plugin
- private var auto_seedVar: String = null // using nullable var for better memory usage
- private def auto_seed: Option[String] = Option(auto_seedVar)
-
// Prefix for use in naming
// - Defaults to prefix at time when object is created
// - Overridden when [[suggestSeed]] or [[autoSeed]] is called
+ // - Note that suggestSeed does *not* prevent autoSeed from changing this
private var naming_prefix: Prefix = Builder.getPrefix
- // Post-seed hooks called to carry the suggested seeds to other candidates as needed
- private var suggest_postseed_hooks: List[String => Unit] = Nil
-
- // Post-seed hooks called to carry the auto seeds to other candidates as needed
- private var auto_postseed_hooks: List[String => Unit] = Nil
-
/** Takes the last seed suggested. Multiple calls to this function will take the last given seed, unless
* this HasId is a module port (see overridden method in Data.scala).
*
@@ -187,8 +144,9 @@ private[chisel3] trait HasId extends InstanceId {
private[chisel3] def autoSeed(seed: String): this.type = forceAutoSeed(seed)
// Bypass the overridden behavior of autoSeed in [[Data]], apply autoSeed even to ports
private[chisel3] def forceAutoSeed(seed: String): this.type = {
- auto_seedVar = seed
- for (hook <- auto_postseed_hooks.reverse) { hook(seed) }
+ if (!_nameIsSuggested) {
+ suggested_seedVar = seed
+ }
naming_prefix = Builder.getPrefix
this
}
@@ -208,33 +166,22 @@ private[chisel3] trait HasId extends InstanceId {
* If the final computed name conflicts with another name, it may get uniquified by appending
* a digit at the end.
*
- * Is a higher priority than [[autoSeed]], in that regardless of whether [[autoSeed]]
+ * Is a higher priority than `autoSeed`, in that regardless of whether `autoSeed`
* was called, [[suggestName]] will always take precedence.
*
* @param seed The seed for the name of this component
* @return this object
*/
def suggestName(seed: => String): this.type = {
- if (suggested_seed.isEmpty) {
+ if (!_nameIsSuggested) {
suggested_seedVar = seed
// Only set the prefix if a seed hasn't been suggested
naming_prefix = Builder.getPrefix
- for (hook <- suggest_postseed_hooks.reverse) { hook(seed) }
+ _nameIsSuggested = true
}
this
}
- // Internal version of .suggestName that can override a user-suggested name
- // This only exists for maintaining "val io" naming in compatibility-mode Modules without IO
- // wrapping
- private[chisel3] def forceFinalName(seed: String): this.type = {
- // This could be called with user prefixes, ignore them
- noPrefix {
- suggested_seedVar = seed
- this.suggestName(seed)
- }
- }
-
/** Computes the name of this HasId, if one exists
* @param defaultSeed Optionally provide default seed for computing the name
* @return the name, if it can be computed
@@ -249,24 +196,47 @@ private[chisel3] trait HasId extends InstanceId {
*
* @return the current calculation of a name, if it exists
*/
- private[chisel3] def seedOpt: Option[String] = suggested_seed.orElse(auto_seed)
+ private[chisel3] def seedOpt: Option[String] = suggested_seed
/** @return Whether either autoName or suggestName has been called */
def hasSeed: Boolean = seedOpt.isDefined
- private[chisel3] def hasAutoSeed: Boolean = auto_seed.isDefined
-
- private[chisel3] def addSuggestPostnameHook(hook: String => Unit): Unit = suggest_postseed_hooks ::= hook
- private[chisel3] def addAutoPostnameHook(hook: String => Unit): Unit = auto_postseed_hooks ::= hook
-
// Uses a namespace to convert suggestion into a true name
// Will not do any naming if the reference already assigned.
// (e.g. tried to suggest a name to part of a Record)
- private[chisel3] def forceName(default: => String, namespace: Namespace): Unit =
+ private[chisel3] def forceName(
+ default: => String,
+ namespace: Namespace,
+ errorIfDup: Boolean = false,
+ refBuilder: String => Arg = Ref(_)
+ ): Unit =
if (_ref.isEmpty) {
- val candidate_name = _computeName(Some(default)).get
+ val candidate_name = _computeName(Some(default).filterNot(_ => errorIfDup)).getOrElse {
+ throwException(
+ s"Attempted to name a nameless IO port ($this): this is usually caused by instantiating an IO but not assigning it to a val.\n" +
+ s"Assign $this to a val, or explicitly call suggestName to seed a unique name"
+ )
+ }
+
+ val sanitized = sanitize(candidate_name)
val available_name = namespace.name(candidate_name)
- setRef(Ref(available_name))
+
+ // Check for both cases of name duplication
+ if (errorIfDup && (available_name != sanitized)) {
+ // If sanitization occurred, then the sanitized name duplicate an existing name
+ if ((candidate_name != sanitized)) {
+ Builder.error(
+ s"Attempted to name $this with an unsanitary name '$candidate_name': sanitization results in a duplicated name '$sanitized'. Please seed a more unique name"
+ )
+ } else {
+ // Otherwise the candidate name duplicates an existing name
+ Builder.error(
+ s"Attempted to name $this with a duplicated name '$candidate_name'. Use suggestName to seed a unique name"
+ )
+ }
+ }
+
+ setRef(refBuilder(available_name))
// Clear naming prefix to free memory
naming_prefix = Nil
}
@@ -279,32 +249,26 @@ private[chisel3] trait HasId extends InstanceId {
_refVar = imm
}
}
- private[chisel3] def setRef(parent: HasId, name: String, opaque: Boolean = false): Unit = {
- if (!opaque) setRef(Slot(Node(parent), name))
- else setRef(OpaqueSlot(Node(parent)))
+ private[chisel3] def setRef(parent: Node, name: String, opaque: Boolean = false): Unit = {
+ if (!opaque) setRef(Slot(parent, name))
+ else setRef(OpaqueSlot(parent))
}
- private[chisel3] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index)))
- private[chisel3] def setRef(parent: HasId, index: UInt): Unit = setRef(Index(Node(parent), index.ref))
+ private[chisel3] def setRef(parent: Node, index: Int): Unit = setRef(LitIndex(parent, index))
+ private[chisel3] def setRef(parent: Node, index: UInt): Unit = index.litOption match {
+ case Some(lit) if lit.isValidInt => setRef(LitIndex(parent, lit.intValue))
+ case _ => setRef(Index(parent, index.ref))
+ }
private[chisel3] def getRef: Arg = _ref.get
private[chisel3] def getOptionRef: Option[Arg] = _ref
private def refName(c: Component): String = _ref match {
case Some(arg) => arg.fullName(c)
- case None =>
- // This is super hacky but this is just for a short term deprecation
- // These accesses occur after Chisel elaboration so we cannot use the normal
- // Builder.deprecated mechanism, we have to create our own one off ErrorLog and print the
- // warning right away.
- // It's especially bad because --warnings-as-errors does not work with these warnings
- val errors = new ErrorLog(false)
- val logger = new _root_.logger.Logger(this.getClass.getName)
- val msg =
- "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated" + _errorContext + ". " +
- "This will become an error in Chisel 3.6."
- errors.deprecated(msg, None)
- errors.checkpoint(logger)
- _computeName(None).get
+ case None => {
+ throwException(
+ "You cannot access the .instanceName or .toTarget of non-hardware Data" + _errorContext
+ )
+ }
}
private[chisel3] def _errorContext: String = {
@@ -314,6 +278,7 @@ private[chisel3] trait HasId extends InstanceId {
}
val parentGuess: String = _parent match {
+ // case Some(ViewParent) => s", in module '${reifyParent.pathName}'" TODO with datamirror
case Some(p) => s", in module '${p.pathName}'"
case None => ""
}
@@ -345,13 +310,18 @@ private[chisel3] trait HasId extends InstanceId {
case Some(p) => p.name
case None => throwException(s"$instanceName doesn't have a parent")
}
- // TODO Should this be public?
- protected def circuitName: String = _parent match {
+ def circuitName: String = _parent match {
case None =>
- _circuit match {
- case None => instanceName
- case Some(o) => o.circuitName
+ // Only modules have circuits
+ this match {
+ case b: BaseModule =>
+ b._circuit match {
+ case Some(c) => c.circuitName
+ case None => instanceName
+ }
+ case _ => instanceName
}
+ // case Some(ViewParent) => reifyParent.circuitName TODO with datamirror
case Some(p) => p.circuitName
}
@@ -414,6 +384,26 @@ private[chisel3] trait NamedComponent extends HasId {
}
}
+ /** 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(parent) => makeTarget(parent)
+ case None => localTarget
+ }
+ }
+
private def assertValidTarget(): Unit = {
val isVecSubaccess = getOptionRef.map {
case Index(_, _: ULit) => true // Vec literal indexing
@@ -451,10 +441,6 @@ private[chisel3] class DynamicContext(
val newAnnotations = ArrayBuffer[ChiselMultiAnnotation]()
var currentModule: Option[BaseModule] = None
- // Enum annotations are added every time a ChiselEnum is bound
- // To keep the number down, we keep them unique in the annotations
- val enumAnnos = mutable.HashSet[ChiselAnnotation]()
-
/** Contains a mapping from a elaborated module to their aspect
* Set by [[ModuleAspect]]
*/
@@ -482,6 +468,9 @@ private[chisel3] object Builder extends LazyLogging {
dynamicContextVar.value.get
}
+ /** Check if we are in a Builder context */
+ def inContext: Boolean = dynamicContextVar.value.isDefined
+
// Used to suppress warnings when casting from a UInt to an Enum
var suppressEnumCastWarning: Boolean = false
@@ -513,11 +502,21 @@ private[chisel3] object Builder extends LazyLogging {
def idGen: IdGen = chiselContext.get.idGen
- def globalNamespace: Namespace = dynamicContext.globalNamespace
+ def globalNamespace: Namespace = dynamicContext.globalNamespace
+ // def globalIdentifierNamespace: Namespace = dynamicContext.globalIdentifierNamespace
+
+ // A mapping from previously named bundles to their hashed structural/FIRRTL types, for
+ // disambiguation purposes when emitting type aliases
+ // Records are used as the key for this map to both represent their alias name and preserve
+ // the chisel Bundle structure when passing everything off to the Converter
+ private[chisel3] val aliasMap: mutable.LinkedHashMap[String, (fir.Type)] =
+ mutable.LinkedHashMap.empty[String, (fir.Type)]
+
def components: ArrayBuffer[Component] = dynamicContext.components
+
def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations
- def enumAnnos: mutable.HashSet[ChiselAnnotation] = dynamicContext.enumAnnos
+ // def contextCache: BuilderContextCache = dynamicContext.contextCache
// TODO : Unify this with annotations in the future - done this way for backward compatability
def newAnnotations: ArrayBuffer[ChiselMultiAnnotation] = dynamicContext.newAnnotations
@@ -542,8 +541,11 @@ private[chisel3] object Builder extends LazyLogging {
case Slot(_, field) => Some(field) // Record
case OpaqueSlot(_) => None // OpaqueSlots don't contribute to the name
case Index(_, ILit(n)) => Some(n.toString) // Vec static indexing
+ case LitIndex(_, n) => Some(n.toString) // Vec static indexing
case Index(_, ULit(n, _)) => Some(n.toString) // Vec lit indexing
case Index(_, _: Node) => None // Vec dynamic indexing
+ case f =>
+ throw new InternalErrorException(s"Match Error: field=$f")
}
def map2[A, B](a: Option[A], b: Option[A])(f: (A, A) => B): Option[B] =
a.flatMap(ax => b.map(f(ax, _)))
@@ -666,9 +668,11 @@ private[chisel3] object Builder extends LazyLogging {
}
def forcedClock: Clock = currentClock.getOrElse(
+ // TODO add implicit clock change to Builder.exception
throwException("Error: No implicit clock.")
)
def forcedReset: Reset = currentReset.getOrElse(
+ // TODO add implicit clock change to Builder.exception
throwException("Error: No implicit reset.")
)
@@ -698,28 +702,39 @@ private[chisel3] object Builder extends LazyLogging {
for ((elt, i) <- iter.zipWithIndex) {
nameRecursively(s"${prefix}_${i}", elt, namer)
}
+ case product: Product =>
+ product.productIterator.zip(product.productElementNames).foreach {
+ case (elt, fullName) =>
+ val name = fullName.stripPrefix("_")
+ val prefixedName = if (name.nonEmpty) s"${prefix}_${name}" else prefix
+ nameRecursively(prefixedName, elt, namer)
+ }
case _ => // Do nothing
}
def errors: ErrorLog = dynamicContext.errors
def error(m: => String): Unit = {
// If --throw-on-first-error is requested, throw an exception instead of aggregating errors
- if (dynamicContextVar.value.isDefined && !dynamicContextVar.value.get.throwOnFirstError) {
+ if (dynamicContextVar.value.isDefined) {
errors.error(m)
} else {
throwException(m)
}
}
- def warning(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.warning(m)
+
def warningNoLoc(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.warningNoLoc(m)
- def deprecated(m: => String, location: Option[String] = None): Unit =
+
+ def warning(warning: Warning): Unit =
+ if (dynamicContextVar.value.isDefined) errors.warning("warn") // TODO fix when synced Error.scala
+
+ def deprecated(m: => String, location: Option[String] = None): Unit =
if (dynamicContextVar.value.isDefined) errors.deprecated(m, location)
/** Record an exception as an error, and throw it.
*
* @param m exception message
*/
- @throws(classOf[ChiselException])
+ @throws(classOf[chisel3.ChiselException])
def exception(m: => String): Nothing = {
error(m)
throwException(m)
@@ -730,8 +745,21 @@ private[chisel3] object Builder extends LazyLogging {
dynamicContext: DynamicContext,
forceModName: Boolean = true
): (Circuit, T) = {
+ buildImpl(f, dynamicContext, forceModName)
+ }
+
+ private[chisel3] def buildImpl[T <: BaseModule](
+ f: => T,
+ dynamicContext: DynamicContext,
+ forceModName: Boolean = true
+ ): (Circuit, T) = {
dynamicContextVar.withValue(Some(dynamicContext)) {
- // in tiny designs/testcases that never access anything in chisel3.internal
+ // in tiny designs/testcases that never access anything in
+ // chisel3.internal Must initialize the singleton or
+ // OutOfMemoryErrors and StackOverflowErrors will instead report
+ // as "java.lang.NoClassDefFoundError: Could not initialize
+ // class scala.util.control.NonFatal$".
+ scala.util.control.NonFatal: Unit
logger.info("Elaborating design...")
val mod = f
if (forceModName) { // This avoids definition name index skipping with D/I
@@ -740,7 +768,15 @@ private[chisel3] object Builder extends LazyLogging {
errors.checkpoint(logger)
logger.info("Done elaborating.")
- (Circuit(components.last.name, components.toSeq, annotations.toSeq, null, newAnnotations.toSeq), mod)
+ (
+ Circuit(
+ components.last.name,
+ components.toSeq,
+ annotations.toSeq,
+ null,
+ newAnnotations.toSeq),
+ mod
+ )
}
}
initializeSingletons()
diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala
index da968d2a..b7c4be35 100644
--- a/core/src/main/scala/chisel3/internal/Error.scala
+++ b/core/src/main/scala/chisel3/internal/Error.scala
@@ -324,7 +324,7 @@ private class Error(msg: => String, line: Option[StackTraceElement]) extends Log
def format: String = tag("error", Console.RED)
}
-private class Warning(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) {
+class Warning(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) {
def format: String = tag("warn", Console.YELLOW)
}
diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
index 551b6138..6789b041 100644
--- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -209,6 +209,13 @@ case class Index(imm: Arg, value: Arg) extends Arg {
override def localName: String = s"${imm.localName}[${value.localName}]"
}
+// Like index above, except the index is a literal, used for elements of Vecs
+case class LitIndex(imm: Arg, value: Int) extends Arg {
+ def name: String = s"[$value]"
+ override def contextualName(ctx: Component): String = s"${imm.contextualName(ctx)}[$value]"
+ override def localName: String = s"${imm.localName}[$value]"
+}
+
object Width {
def apply(x: Int): Width = KnownWidth(x)
def apply(): Width = UnknownWidth()
diff --git a/core/src/main/scala/chisel3/package.scala b/core/src/main/scala/chisel3/package.scala
index 72a3515c..6633e7ab 100644
--- a/core/src/main/scala/chisel3/package.scala
+++ b/core/src/main/scala/chisel3/package.scala
@@ -1,11 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
import chisel3.internal.firrtl.BinaryPoint
+import chisel3.internal.ExceptionHelpers
import java.util.{MissingFormatArgumentException, UnknownFormatConversionException}
-import scala.collection.mutable
import chisel3.experimental.VecLiterals._
import chisel3.experimental.BundleLiterals._
+import scala.collection.mutable
+import scala.annotation.tailrec
+
+
/** This package contains the main chisel3 API.
*/
package object chisel3 {
@@ -181,8 +185,17 @@ package object chisel3 {
// object UInt extends UIntFactory
// object SInt extends SIntFactory
// object Bool extends BoolFactory
+ /** Public API to access Node/Signal names.
+ * currently, the node's name, the full path name, and references to its parent Module and component.
+ * These are only valid once the design has been elaborated, and should not be used during its construction.
+ */
- type InstanceId = internal.InstanceId
+ trait InstanceId {
+ def instanceName: String
+ def pathName: String
+ def parentPathName: String
+ def parentModName: String
+ }
@deprecated("MultiIOModule is now just Module", "Chisel 3.5")
type MultiIOModule = chisel3.Module
@@ -332,7 +345,65 @@ package object chisel3 {
implicit def string2Printable(str: String): Printable = PString(str)
- type ChiselException = internal.ChiselException
+ class InternalErrorException(message: String, cause: Throwable = null)
+ extends ChiselException(
+ "Internal Error: Please file an issue at https://github.com/chipsalliance/chisel3/issues:" + message,
+ cause
+ )
+
+ class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause, true, true) {
+
+ /** Examine a [[Throwable]], to extract all its causes. Innermost cause is first.
+ * @param throwable an exception to examine
+ * @return a sequence of all the causes with innermost cause first
+ */
+ @tailrec
+ private def getCauses(throwable: Throwable, acc: Seq[Throwable] = Seq.empty): Seq[Throwable] =
+ throwable.getCause() match {
+ case null => throwable +: acc
+ case a => getCauses(a, throwable +: acc)
+ }
+
+ /** Returns true if an exception contains */
+ private def containsBuilder(throwable: Throwable): Boolean =
+ throwable
+ .getStackTrace()
+ .collectFirst {
+ case ste if ste.getClassName().startsWith(ExceptionHelpers.builderName) => throwable
+ }
+ .isDefined
+
+ /** Examine this [[ChiselException]] and it's causes for the first [[Throwable]] that contains a stack trace including
+ * a stack trace element whose declaring class is the [[ExceptionHelpers.builderName]]. If no such element exists, return this
+ * [[ChiselException]].
+ */
+ private lazy val likelyCause: Throwable =
+ getCauses(this).collectFirst { case a if containsBuilder(a) => a }.getOrElse(this)
+
+ /** For an exception, return a stack trace trimmed to user code only
+ *
+ * This does the following actions:
+ *
+ * 1. Trims the top of the stack trace while elements match [[ExceptionHelpers.packageTrimlist]]
+ * 2. Trims the bottom of the stack trace until an element matches [[ExceptionHelpers.builderName]]
+ * 3. Trims from the [[ExceptionHelpers.builderName]] all [[ExceptionHelpers.packageTrimlist]]
+ *
+ * @param throwable the exception whose stack trace should be trimmed
+ * @return an array of stack trace elements
+ */
+ private def trimmedStackTrace(throwable: Throwable): Array[StackTraceElement] = {
+ def isBlacklisted(ste: StackTraceElement) = {
+ val packageName = ste.getClassName().takeWhile(_ != '.')
+ ExceptionHelpers.packageTrimlist.contains(packageName)
+ }
+
+ val trimmedLeft = throwable.getStackTrace().view.dropWhile(isBlacklisted)
+ val trimmedReverse = trimmedLeft.toIndexedSeq.reverse.view
+ .dropWhile(ste => !ste.getClassName.startsWith(ExceptionHelpers.builderName))
+ .dropWhile(isBlacklisted)
+ trimmedReverse.toIndexedSeq.reverse.toArray
+ }
+ }
// Debugger/Tester access to internal Chisel data structures and methods.
def getDataElements(a: Aggregate): Seq[Element] = {