diff options
| author | Jack Koenig | 2021-09-17 21:01:26 -0700 |
|---|---|---|
| committer | Jack Koenig | 2021-09-17 21:01:26 -0700 |
| commit | 5c8c19345e6711279594cf1f9ddab33623c8eba7 (patch) | |
| tree | d9d6ced3934aa4a8be3dec19ddcefe50a7a93d5a /core/src/main/scala/chisel3/internal | |
| parent | e63b9667d89768e0ec6dc8a9153335cb48a213a7 (diff) | |
| parent | 958904cb2f2f65d02b2ab3ec6d9ec2e06d04e482 (diff) | |
Merge branch 'master' into 3.5-release
Diffstat (limited to 'core/src/main/scala/chisel3/internal')
| -rw-r--r-- | core/src/main/scala/chisel3/internal/BiConnect.scala | 43 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Binding.scala | 22 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Builder.scala | 133 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Error.scala | 113 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/MonoConnect.scala | 8 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Namer.scala | 14 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/firrtl/Converter.scala | 109 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/firrtl/IR.scala | 83 |
8 files changed, 414 insertions, 111 deletions
diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala index 1ee149ee..aa58cb95 100644 --- a/core/src/main/scala/chisel3/internal/BiConnect.scala +++ b/core/src/main/scala/chisel3/internal/BiConnect.scala @@ -3,12 +3,16 @@ package chisel3.internal import chisel3._ +import chisel3.experimental.dataview.reify import chisel3.experimental.{Analog, BaseModule, attach} import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl.{Connect, DefInvalid} + import scala.language.experimental.macros import chisel3.internal.sourceinfo._ +import scala.annotation.tailrec + /** * BiConnect.connect executes a bidirectional connection element-wise. * @@ -113,14 +117,33 @@ private[chisel3] object BiConnect { } } } - // Handle Records defined in Chisel._ code (change to NotStrict) - case (left_r: Record, right_r: Record) => (left_r.compileOptions, right_r.compileOptions) match { - case (ExplicitCompileOptions.NotStrict, _) => - left_r.bulkConnect(right_r)(sourceInfo, ExplicitCompileOptions.NotStrict) - case (_, ExplicitCompileOptions.NotStrict) => - left_r.bulkConnect(right_r)(sourceInfo, ExplicitCompileOptions.NotStrict) - case _ => recordConnect(sourceInfo, connectCompileOptions, left_r, right_r, context_mod) - } + // Handle Records defined in Chisel._ code by emitting a FIRRTL partial connect + case pair @ (left_r: Record, right_r: Record) => + val notStrict = + Seq(left_r.compileOptions, right_r.compileOptions).contains(ExplicitCompileOptions.NotStrict) + if (notStrict) { + // Traces flow from a child Data to its parent + @tailrec def traceFlow(currentlyFlipped: Boolean, data: Data): Boolean = { + import SpecifiedDirection.{Input => SInput, Flip => SFlip} + val sdir = data.specifiedDirection + val flipped = sdir == SInput || sdir == SFlip + data.binding.get match { + case ChildBinding(parent) => traceFlow(flipped ^ currentlyFlipped, parent) + case PortBinding(enclosure) => + val childPort = enclosure != context_mod + childPort ^ flipped ^ currentlyFlipped + case _ => true + } + } + def canBeSink(data: Data): Boolean = traceFlow(true, data) + def canBeSource(data: Data): Boolean = traceFlow(false, data) + // chisel3 <> is commutative but FIRRTL <- is not + val flipConnection = !canBeSink(left_r) || !canBeSource(right_r) + val (newLeft, newRight) = if (flipConnection) pair.swap else pair + newLeft.bulkConnect(newRight)(sourceInfo, ExplicitCompileOptions.NotStrict) + } else { + recordConnect(sourceInfo, connectCompileOptions, left_r, right_r, context_mod) + } // Handle Records connected to DontCare (change to NotStrict) case (left_r: Record, DontCare) => @@ -215,8 +238,10 @@ private[chisel3] object BiConnect { // This function checks if element-level connection operation allowed. // Then it either issues it or throws the appropriate exception. - def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: RawModule): Unit = { + def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, _left: Element, _right: Element, context_mod: RawModule): Unit = { import BindingDirection.{Internal, Input, Output} // Using extensively so import these + val left = reify(_left) + val right = reify(_right) // If left or right have no location, assume in context module // This can occur if one of them is a literal, unbound will error previously val left_mod: BaseModule = left.topBinding.location.getOrElse(context_mod) diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala index 4442c62e..a0dcc20c 100644 --- a/core/src/main/scala/chisel3/internal/Binding.scala +++ b/core/src/main/scala/chisel3/internal/Binding.scala @@ -6,6 +6,8 @@ import chisel3._ import chisel3.experimental.BaseModule import chisel3.internal.firrtl.LitArg +import scala.collection.immutable.VectorMap + /** Requires that a node is hardware ("bound") */ object requireIsHardware { @@ -110,12 +112,32 @@ case class ChildBinding(parent: Data) extends Binding { case class SampleElementBinding[T <: Data](parent: Vec[T]) extends Binding { def location = parent.topBinding.location } +/** Special binding for Mem types */ +case class MemTypeBinding[T <: Data](parent: MemBase[T]) extends Binding { + def location: Option[BaseModule] = parent._parent +} // A DontCare element has a specific Binding, somewhat like a literal. // It is a source (RHS). It may only be connected/applied to sinks. case class DontCareBinding() extends UnconstrainedBinding +// Views currently only support 1:1 Element-level mappings +private[chisel3] case class ViewBinding(target: Element) extends UnconstrainedBinding +/** Binding for Aggregate Views + * @param childMap Mapping from children of this view to each child's target + * @param target Optional Data this Aggregate views if the view is total and the target is a Data + */ +private[chisel3] case class AggregateViewBinding(childMap: Map[Data, Element], target: Option[Data]) extends UnconstrainedBinding + + +/** Binding for Data's returned from accessing an Instance/Definition members, if not readable/writable port */ +private[chisel3] case object CrossModuleBinding extends TopBinding { + def location = None +} + sealed trait LitBinding extends UnconstrainedBinding with ReadOnlyBinding // Literal binding attached to a element that is not part of a Bundle. case class ElementLitBinding(litArg: LitArg) extends LitBinding // Literal binding attached to the root of a Bundle, containing literal values of its children. case class BundleLitBinding(litMap: Map[Data, LitArg]) extends LitBinding +// Literal binding attached to the root of a Vec, containing literal values of its children. +case class VecLitBinding(litMap: VectorMap[Data, LitArg]) extends LitBinding diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index b7772aea..441abc92 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -6,10 +6,13 @@ import scala.util.DynamicVariable import scala.collection.mutable.ArrayBuffer import chisel3._ import chisel3.experimental._ +import chisel3.experimental.hierarchy.Instance import chisel3.internal.firrtl._ import chisel3.internal.naming._ import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget} -import _root_.firrtl.annotations.AnnotationUtils.{validComponentName} +import _root_.firrtl.annotations.AnnotationUtils.validComponentName +import _root_.firrtl.{AnnotationSeq, RenameMap} +import chisel3.experimental.dataview.{reify, reifySingleData} import chisel3.internal.Builder.Prefix import logger.LazyLogging @@ -17,6 +20,7 @@ import scala.collection.mutable private[chisel3] class Namespace(keywords: Set[String]) { private val names = collection.mutable.HashMap[String, Long]() + def copyTo(other: Namespace): Unit = names.foreach { case (s: String, l: Long) => other.names(s) = l } for (keyword <- keywords) names(keyword) = 1 @@ -83,8 +87,10 @@ trait InstanceId { private[chisel3] trait HasId extends InstanceId { private[chisel3] def _onModuleClose: Unit = {} - private[chisel3] val _parent: Option[BaseModule] = Builder.currentModule - _parent.foreach(_.addId(this)) + private[chisel3] var _parent: Option[BaseModule] = Builder.currentModule + + // Set if the returned top-level module of a nested call to the Chisel Builder, see Definition.apply + private[chisel3] var _circuit: Option[BaseModule] = None private[chisel3] val _id: Long = Builder.idGen.next @@ -215,32 +221,53 @@ private[chisel3] trait HasId extends InstanceId { 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 => computeName(None, None).get + } + + // Helper for reifying views if they map to a single Target + private[chisel3] def reifyTarget: Option[Data] = this match { + case d: Data => reifySingleData(d) // Only Data can be views + case bad => throwException(s"This shouldn't be possible - got $bad with ${_parent}") + } + + // Helper for reifying the parent of a view if the view maps to a single Target + private[chisel3] def reifyParent: BaseModule = reifyTarget.flatMap(_._parent).getOrElse(ViewParent) + // Implementation of public methods. def instanceName: String = _parent match { - case Some(p) => p._component match { - case Some(c) => _ref match { - case Some(arg) => arg fullName c - case None => computeName(None, None).get + case Some(ViewParent) => reifyTarget.map(_.instanceName).getOrElse(this.refName(ViewParent.fakeComponent)) + case Some(p) => + (p._component, this) match { + case (Some(c), _) => refName(c) + case (None, d: Data) if d.topBindingOpt == Some(CrossModuleBinding) => _ref.get.localName + case (None, _) => throwException(s"signalName/pathName should be called after circuit elaboration: $this, ${_parent}") } - case None => throwException("signalName/pathName should be called after circuit elaboration") - } case None => throwException("this cannot happen") } def pathName: String = _parent match { case None => instanceName + case Some(ViewParent) => s"${reifyParent.pathName}.$instanceName" case Some(p) => s"${p.pathName}.$instanceName" } def parentPathName: String = _parent match { + case Some(ViewParent) => reifyParent.pathName case Some(p) => p.pathName case None => throwException(s"$instanceName doesn't have a parent") } def parentModName: String = _parent match { + case Some(ViewParent) => reifyParent.name 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 { - case None => instanceName + case None => _circuit match { + case None => instanceName + case Some(o) => o.circuitName + } + case Some(ViewParent) => reifyParent.circuitName case Some(p) => p.circuitName } @@ -279,8 +306,12 @@ private[chisel3] trait NamedComponent extends HasId { val name = this.instanceName if (!validComponentName(name)) throwException(s"Illegal component name: $name (note: literals are illegal)") import _root_.firrtl.annotations.{Target, TargetToken} + val root = _parent.map { + case ViewParent => reifyParent + case other => other + }.get.getTarget // All NamedComponents will have a parent, only the top module can have None here Target.toTargetTokens(name).toList match { - case TargetToken.Ref(r) :: components => ReferenceTarget(this.circuitName, this.parentModName, Nil, r, components) + case TargetToken.Ref(r) :: components => root.ref(r).copy(component = components) case other => throw _root_.firrtl.annotations.Target.NamedException(s"Cannot convert $name into [[ReferenceTarget]]: $other") } @@ -288,8 +319,10 @@ private[chisel3] trait NamedComponent extends HasId { final def toAbsoluteTarget: ReferenceTarget = { val localTarget = toTarget + def makeTarget(p: BaseModule) = p.toAbsoluteTarget.ref(localTarget.ref).copy(component = localTarget.component) _parent match { - case Some(parent) => parent.toAbsoluteTarget.ref(localTarget.ref).copy(component = localTarget.component) + case Some(ViewParent) => makeTarget(reifyParent) + case Some(parent) => makeTarget(parent) case None => localTarget } } @@ -304,19 +337,29 @@ private[chisel3] class ChiselContext() { // Records the different prefixes which have been scoped at this point in time var prefixStack: Prefix = Nil + + // Views belong to a separate namespace (for renaming) + // The namespace outside of Builder context is useless, but it ensures that views can still be created + // and the resulting .toTarget is very clearly useless (_$$View$$_...) + val viewNamespace = Namespace.empty } -private[chisel3] class DynamicContext() { +private[chisel3] class DynamicContext(val annotationSeq: AnnotationSeq) { val globalNamespace = Namespace.empty val components = ArrayBuffer[Component]() val annotations = ArrayBuffer[ChiselAnnotation]() var currentModule: Option[BaseModule] = None + // This is only used for testing, it can be removed if the plugin becomes mandatory + var allowReflectiveAutoCloneType = true /** Contains a mapping from a elaborated module to their aspect * Set by [[ModuleAspect]] */ val aspectModule: mutable.HashMap[BaseModule, BaseModule] = mutable.HashMap.empty[BaseModule, BaseModule] + // Views that do not correspond to a single ReferenceTarget and thus require renaming + val unnamedViews: ArrayBuffer[Data] = ArrayBuffer.empty + // Set by object Module.apply before calling class Module constructor // Used to distinguish between no Module() wrapping, multiple wrappings, and rewrapping var readyForModuleConstr: Boolean = false @@ -325,6 +368,8 @@ private[chisel3] class DynamicContext() { var currentReset: Option[Reset] = None val errors = new ErrorLog val namingStack = new NamingStack + // Used to indicate if this is the top-level module of full elaboration, or from a Definition + var inDefinition: Boolean = false } private[chisel3] object Builder extends LazyLogging { @@ -339,6 +384,11 @@ private[chisel3] object Builder extends LazyLogging { dynamicContextVar.value.get } + // Returns the current dynamic context + def captureContext(): DynamicContext = dynamicContext + // Sets the current dynamic contents + def restoreContext(dc: DynamicContext) = dynamicContextVar.value = Some(dc) + // Ensure we have a thread-specific ChiselContext private val chiselContext = new ThreadLocal[ChiselContext]{ override def initialValue: ChiselContext = { @@ -365,8 +415,12 @@ private[chisel3] object Builder extends LazyLogging { def globalNamespace: Namespace = dynamicContext.globalNamespace def components: ArrayBuffer[Component] = dynamicContext.components def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations + def annotationSeq: AnnotationSeq = dynamicContext.annotationSeq def namingStack: NamingStack = dynamicContext.namingStack + def unnamedViews: ArrayBuffer[Data] = dynamicContext.unnamedViews + def viewNamespace: Namespace = chiselContext.get.viewNamespace + // Puts a prefix string onto the prefix stack def pushPrefix(d: String): Unit = { val context = chiselContext.get() @@ -400,6 +454,7 @@ private[chisel3] object Builder extends LazyLogging { case PortBinding(mod) if Builder.currentModule.contains(mod) => data.seedOpt case PortBinding(mod) => map2(mod.seedOpt, data.seedOpt)(_ + "_" + _) case (_: LitBinding | _: DontCareBinding) => None + case _ => Some("view_") // TODO implement } id match { case d: Data => recData(d) @@ -529,6 +584,22 @@ private[chisel3] object Builder extends LazyLogging { dynamicContext.currentReset = newReset } + def inDefinition: Boolean = { + dynamicContextVar.value + .map(_.inDefinition) + .getOrElse(false) + } + + // This should only be used for testing, must be true outside of Builder context + def allowReflectiveAutoCloneType: Boolean = { + dynamicContextVar.value + .map(_.allowReflectiveAutoCloneType) + .getOrElse(true) + } + def allowReflectiveAutoCloneType_=(value: Boolean): Unit = { + dynamicContext.allowReflectiveAutoCloneType = value + } + def forcedClock: Clock = currentClock.getOrElse( throwException("Error: No implicit clock.") ) @@ -588,6 +659,10 @@ private[chisel3] object Builder extends LazyLogging { * (Note: Map is Iterable[Tuple2[_,_]] and thus excluded) */ def nameRecursively(prefix: String, nameMe: Any, namer: (HasId, String) => Unit): Unit = nameMe match { + case (id: Instance[_]) => id.cloned match { + case Right(m: internal.BaseModule.ModuleClone[_]) => namer(m.getPorts, prefix) + case _ => + } case (id: HasId) => namer(id, prefix) case Some(elt) => nameRecursively(prefix, elt, namer) case (iter: Iterable[_]) if iter.hasDefiniteSize => @@ -633,21 +708,37 @@ private[chisel3] object Builder extends LazyLogging { } } - - def build[T <: RawModule](f: => T): (Circuit, T) = { - build(f, new DynamicContext()) + // Builds a RenameMap for all Views that do not correspond to a single Data + // These Data give a fake ReferenceTarget for .toTarget and .toReferenceTarget that the returned + // RenameMap can split into the constituent parts + private[chisel3] def makeViewRenameMap: RenameMap = { + val renames = RenameMap() + for (view <- unnamedViews) { + val localTarget = view.toTarget + val absTarget = view.toAbsoluteTarget + val elts = getRecursiveFields.lazily(view, "") + .collect { case (elt: Element, _) => elt } + for (elt <- elts) { + val targetOfView = reify(elt) + renames.record(localTarget, targetOfView.toTarget) + renames.record(absTarget, targetOfView.toAbsoluteTarget) + } + } + renames } - private [chisel3] def build[T <: RawModule](f: => T, dynamicContext: DynamicContext): (Circuit, T) = { + private [chisel3] def build[T <: BaseModule](f: => T, dynamicContext: DynamicContext): (Circuit, T) = { dynamicContextVar.withValue(Some(dynamicContext)) { + ViewParent // Must initialize the singleton in a Builder context or weird things can happen + // in tiny designs/testcases that never access anything in chisel3.internal checkScalaVersion() - logger.warn("Elaborating design...") + logger.info("Elaborating design...") val mod = f mod.forceName(None, mod.name, globalNamespace) - errors.checkpoint() - logger.warn("Done elaborating.") + errors.checkpoint(logger) + logger.info("Done elaborating.") - (Circuit(components.last.name, components, annotations), mod) + (Circuit(components.last.name, components.toSeq, annotations.toSeq, makeViewRenameMap), mod) } } initializeSingletons() diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala index 6a1794ce..d6e0c0e6 100644 --- a/core/src/main/scala/chisel3/internal/Error.scala +++ b/core/src/main/scala/chisel3/internal/Error.scala @@ -4,13 +4,84 @@ package chisel3.internal import scala.annotation.tailrec import scala.collection.mutable.{ArrayBuffer, LinkedHashMap} +import _root_.logger.Logger -class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause) { +object ExceptionHelpers { + + /** Root packages that are not typically relevant to Chisel user code. */ + final val packageTrimlist: Set[String] = Set("chisel3", "scala", "java", "jdk", "sun", "sbt") + + /** The object name of Chisel's internal `Builder`. */ + final val builderName: String = chisel3.internal.Builder.getClass.getName + + /** Return a stack trace element that looks like `... (someMessage)`. + * @param message an optional message to include + */ + def ellipsis(message: Option[String] = None): StackTraceElement = + new StackTraceElement("..", " ", message.getOrElse(""), -1) + + /** Utility methods that can be added to exceptions. + */ + implicit class ThrowableHelpers(throwable: Throwable) { + + /** For an exception, mutably trim a stack trace to user code only. + * + * This does the following actions to the stack trace: + * + * 1. From the top, remove elements while the (root) package matches the packageTrimlist + * 2. Optionally, from the bottom, remove elements until the class matches an anchor + * 3. From the anchor (or the bottom), remove elements while the (root) package matches the packageTrimlist + * + * @param packageTrimlist packages that should be removed from the stack trace + * @param anchor an optional class name at which user execution might begin, e.g., a main object + * @return nothing as this mutates the exception directly + */ + def trimStackTraceToUserCode( + packageTrimlist: Set[String] = packageTrimlist, + anchor: Option[String] = Some(builderName) + ): Unit = { + def inTrimlist(ste: StackTraceElement) = { + val packageName = ste.getClassName().takeWhile(_ != '.') + packageTrimlist.contains(packageName) + } + + // Step 1: Remove elements from the top in the package trimlist + ((a: Array[StackTraceElement]) => a.dropWhile(inTrimlist)) + // Step 2: Optionally remove elements from the bottom until the anchor + .andThen(_.reverse) + .andThen( a => + anchor match { + case Some(b) => a.dropWhile(ste => !ste.getClassName.startsWith(b)) + case None => a + } + ) + // Step 3: Remove elements from the bottom in the package trimlist + .andThen(_.dropWhile(inTrimlist)) + // Step 4: Reverse back to the original order + .andThen(_.reverse.toArray) + // Step 5: Add ellipsis stack trace elements and "--full-stacktrace" info + .andThen(a => + ellipsis() +: + a :+ + ellipsis() :+ + ellipsis(Some("Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace"))) + // Step 5: Mutate the stack trace in this exception + .andThen(throwable.setStackTrace(_)) + .apply(throwable.getStackTrace) + } + + } + +} + +class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause, true, true) { /** Package names whose stack trace elements should be trimmed when generating a trimmed stack trace */ + @deprecated("Use ExceptionHelpers.packageTrimlist. This will be removed in Chisel 3.6", "3.5") val blacklistPackages: Set[String] = Set("chisel3", "scala", "java", "sun", "sbt") /** The object name of Chisel's internal `Builder`. Everything stack trace element after this will be trimmed. */ + @deprecated("Use ExceptionHelpers.builderName. This will be removed in Chisel 3.6", "3.5") val builderName: String = chisel3.internal.Builder.getClass.getName /** Examine a [[Throwable]], to extract all its causes. Innermost cause is first. @@ -27,7 +98,7 @@ class ChiselException(message: String, cause: Throwable = null) extends Exceptio /** Returns true if an exception contains */ private def containsBuilder(throwable: Throwable): Boolean = throwable.getStackTrace().collectFirst { - case ste if ste.getClassName().startsWith(builderName) => throwable + 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 @@ -55,19 +126,12 @@ class ChiselException(message: String, cause: Throwable = null) extends Exceptio } val trimmedLeft = throwable.getStackTrace().view.dropWhile(isBlacklisted) - val trimmedReverse = trimmedLeft.reverse + val trimmedReverse = trimmedLeft.toIndexedSeq.reverse.view .dropWhile(ste => !ste.getClassName.startsWith(builderName)) .dropWhile(isBlacklisted) - trimmedReverse.reverse.toArray + trimmedReverse.toIndexedSeq.reverse.toArray } - /** trims the top of the stack of elements belonging to [[blacklistPackages]] - * then trims the bottom elements until it reaches [[builderName]] - * then continues trimming elements belonging to [[blacklistPackages]] - */ - @deprecated("This method will be removed in 3.4", "3.3") - def trimmedStackTrace: Array[StackTraceElement] = trimmedStackTrace(this) - def chiselStackTrace: String = { val trimmed = trimmedStackTrace(likelyCause) @@ -121,35 +185,36 @@ private[chisel3] class ErrorLog { } /** Throw an exception if any errors have yet occurred. */ - def checkpoint(): Unit = { + def checkpoint(logger: Logger): Unit = { deprecations.foreach { case ((message, sourceLoc), count) => - println(s"${ErrorLog.depTag} $sourceLoc ($count calls): $message") + logger.warn(s"${ErrorLog.depTag} $sourceLoc ($count calls): $message") } - errors foreach println + errors.foreach(e => logger.error(e.toString)) if (!deprecations.isEmpty) { - println(s"${ErrorLog.warnTag} ${Console.YELLOW}There were ${deprecations.size} deprecated function(s) used." + + logger.warn(s"${ErrorLog.warnTag} ${Console.YELLOW}There were ${deprecations.size} deprecated function(s) used." + s" These may stop compiling in a future release - you are encouraged to fix these issues.${Console.RESET}") - println(s"${ErrorLog.warnTag} Line numbers for deprecations reported by Chisel may be inaccurate; enable scalac compiler deprecation warnings via either of the following methods:") - println(s"${ErrorLog.warnTag} In the sbt interactive console, enter:") - println(s"""${ErrorLog.warnTag} set scalacOptions in ThisBuild ++= Seq("-unchecked", "-deprecation")""") - println(s"${ErrorLog.warnTag} or, in your build.sbt, add the line:") - println(s"""${ErrorLog.warnTag} scalacOptions := Seq("-unchecked", "-deprecation")""") + logger.warn(s"${ErrorLog.warnTag} Line numbers for deprecations reported by Chisel may be inaccurate; enable scalac compiler deprecation warnings via either of the following methods:") + logger.warn(s"${ErrorLog.warnTag} In the sbt interactive console, enter:") + logger.warn(s"""${ErrorLog.warnTag} set scalacOptions in ThisBuild ++= Seq("-unchecked", "-deprecation")""") + logger.warn(s"${ErrorLog.warnTag} or, in your build.sbt, add the line:") + logger.warn(s"""${ErrorLog.warnTag} scalacOptions := Seq("-unchecked", "-deprecation")""") } val allErrors = errors.filter(_.isFatal) val allWarnings = errors.filter(!_.isFatal) if (!allWarnings.isEmpty && !allErrors.isEmpty) { - println(s"${ErrorLog.errTag} There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} and ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") + logger.warn(s"${ErrorLog.errTag} There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} and ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") } else if (!allWarnings.isEmpty) { - println(s"${ErrorLog.warnTag} There were ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") + logger.warn(s"${ErrorLog.warnTag} There were ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") } else if (!allErrors.isEmpty) { - println(s"${ErrorLog.errTag} There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} during hardware elaboration.") + logger.warn(s"${ErrorLog.errTag} There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} during hardware elaboration.") } if (!allErrors.isEmpty) { - throwException("Fatal errors during hardware elaboration") + throw new ChiselException("Fatal errors during hardware elaboration. Look above for error list.") + with scala.util.control.NoStackTrace } else { // No fatal errors, clear accumulated warnings since they've been reported errors.clear() diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala index 2155894a..5cbab329 100644 --- a/core/src/main/scala/chisel3/internal/MonoConnect.scala +++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala @@ -5,7 +5,9 @@ package chisel3.internal import chisel3._ import chisel3.experimental.{Analog, BaseModule, EnumType, FixedPoint, Interval, UnsafeEnum} import chisel3.internal.Builder.pushCommand +import chisel3.experimental.dataview.reify import chisel3.internal.firrtl.{Connect, DefInvalid} + import scala.language.experimental.macros import chisel3.internal.sourceinfo.SourceInfo @@ -103,6 +105,8 @@ private[chisel3] object MonoConnect { elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) case (sink_e: ResetType, source_e: Reset) => elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: Reset, source_e: ResetType) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) case (sink_e: EnumType, source_e: UnsafeEnum) => elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) case (sink_e: EnumType, source_e: EnumType) if sink_e.typeEquivalent(source_e) => @@ -186,8 +190,10 @@ private[chisel3] object MonoConnect { // This function checks if element-level connection operation allowed. // Then it either issues it or throws the appropriate exception. - def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Element, source: Element, context_mod: RawModule): Unit = { + def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, _sink: Element, _source: Element, context_mod: RawModule): Unit = { import BindingDirection.{Internal, Input, Output} // Using extensively so import these + val sink = reify(_sink) + val source = reify(_source) // If source has no location, assume in context module // This can occur if is a literal, unbound will error previously val sink_mod: BaseModule = sink.topBinding.location.getOrElse(throw UnwritableSinkException) diff --git a/core/src/main/scala/chisel3/internal/Namer.scala b/core/src/main/scala/chisel3/internal/Namer.scala index 1694d71d..c6e36cb6 100644 --- a/core/src/main/scala/chisel3/internal/Namer.scala +++ b/core/src/main/scala/chisel3/internal/Namer.scala @@ -8,9 +8,8 @@ import chisel3.experimental.NoChiselNamePrefix import scala.collection.mutable.Stack import scala.collection.mutable.ListBuffer -import scala.collection.JavaConversions._ - import java.util.IdentityHashMap +import scala.collection.JavaConverters._ /** Recursive Function Namer overview * @@ -81,7 +80,14 @@ class NamingContext extends NamingContextInterface { def addDescendant(ref: Any, descendant: NamingContext) { ref match { case ref: AnyRef => - descendants.getOrElseUpdate(ref, ListBuffer[NamingContext]()) += descendant + // getOrElseUpdate + val l = descendants.get(ref) + val buf = if (l != null) l else { + val value = ListBuffer[NamingContext]() + descendants.put(ref, value) + value + } + buf += descendant case _ => anonymousDescendants += descendant } } @@ -111,7 +117,7 @@ class NamingContext extends NamingContextInterface { } } - for (descendant <- descendants.values().flatten) { + for (descendant <- descendants.values.asScala.flatten) { // Where we have a broken naming link, just ignore the missing parts descendant.namePrefix(prefix) } diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala index aefbf8ab..f56c3b15 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -3,12 +3,13 @@ package chisel3.internal.firrtl import chisel3._ import chisel3.experimental._ -import chisel3.internal.sourceinfo.{NoSourceInfo, SourceLine, SourceInfo} +import chisel3.internal.sourceinfo.{NoSourceInfo, SourceInfo, SourceLine, UnlocatableSourceInfo} import firrtl.{ir => fir} -import chisel3.internal.{castToInt, throwException} +import chisel3.internal.{HasId, castToInt, throwException} import scala.annotation.tailrec import scala.collection.immutable.Queue +import scala.collection.immutable.LazyList // Needed for 2.12 alias private[chisel3] object Converter { // TODO modeled on unpack method on Printable, refactor? @@ -24,6 +25,24 @@ private[chisel3] object Converter { case Percent => ("%%", List.empty) } + private def reportInternalError(msg: String): Nothing = { + val link = "https://github.com/chipsalliance/chisel3/issues/new" + val fullMsg = s"Internal Error! $msg This is a bug in Chisel, please file an issue at '$link'" + throwException(fullMsg) + } + + def getRef(id: HasId, sourceInfo: SourceInfo): Arg = + id.getOptionRef.getOrElse { + val module = id._parent.map(m => s" '$id' was defined in module '$m'.").getOrElse("") + val loc = sourceInfo.makeMessage(" " + _) + reportInternalError(s"Could not get ref for '$id'$loc!$module") + } + + private def clonedModuleIOError(mod: BaseModule, name: String, sourceInfo: SourceInfo): Nothing = { + val loc = sourceInfo.makeMessage(" " + _) + reportInternalError(s"Trying to convert a cloned IO of $mod inside of $mod itself$loc!") + } + def convert(info: SourceInfo): fir.Info = info match { case _: NoSourceInfo => fir.NoInfo case SourceLine(fn, line, col) => fir.FileInfo(fir.StringLit(s"$fn $line:$col")) @@ -41,37 +60,40 @@ private[chisel3] object Converter { // TODO // * Memoize? // * Move into the Chisel IR? - def convert(arg: Arg, ctx: Component): fir.Expression = arg match { + def convert(arg: Arg, ctx: Component, info: SourceInfo): fir.Expression = arg match { case Node(id) => - convert(id.getRef, ctx) + convert(getRef(id, info), ctx, info) case Ref(name) => fir.Reference(name, fir.UnknownType) case Slot(imm, name) => - fir.SubField(convert(imm, ctx), name, fir.UnknownType) + fir.SubField(convert(imm, ctx, info), name, fir.UnknownType) case Index(imm, ILit(idx)) => - fir.SubIndex(convert(imm, ctx), castToInt(idx, "Index"), fir.UnknownType) + fir.SubIndex(convert(imm, ctx, info), castToInt(idx, "Index"), fir.UnknownType) case Index(imm, value) => - fir.SubAccess(convert(imm, ctx), convert(value, ctx), fir.UnknownType) + fir.SubAccess(convert(imm, ctx, info), convert(value, ctx, info), fir.UnknownType) case ModuleIO(mod, name) => if (mod eq ctx.id) fir.Reference(name, fir.UnknownType) - else fir.SubField(fir.Reference(mod.getRef.name, fir.UnknownType), name, fir.UnknownType) + else fir.SubField(fir.Reference(getRef(mod, info).name, fir.UnknownType), name, fir.UnknownType) + case ModuleCloneIO(mod, name) => + if (mod eq ctx.id) clonedModuleIOError(mod, name, info) + else fir.Reference(name) case u @ ULit(n, UnknownWidth()) => fir.UIntLiteral(n, fir.IntWidth(u.minWidth)) case ULit(n, w) => fir.UIntLiteral(n, convert(w)) case slit @ SLit(n, w) => fir.SIntLiteral(n, convert(w)) val unsigned = if (n < 0) (BigInt(1) << slit.width.get) + n else n - val uint = convert(ULit(unsigned, slit.width), ctx) + val uint = convert(ULit(unsigned, slit.width), ctx, info) fir.DoPrim(firrtl.PrimOps.AsSInt, Seq(uint), Seq.empty, fir.UnknownType) // TODO Simplify case fplit @ FPLit(n, w, bp) => val unsigned = if (n < 0) (BigInt(1) << fplit.width.get) + n else n - val uint = convert(ULit(unsigned, fplit.width), ctx) + val uint = convert(ULit(unsigned, fplit.width), ctx, info) val lit = bp.asInstanceOf[KnownBinaryPoint].value fir.DoPrim(firrtl.PrimOps.AsFixedPoint, Seq(uint), Seq(lit), fir.UnknownType) case intervalLit @ IntervalLit(n, w, bp) => val unsigned = if (n < 0) (BigInt(1) << intervalLit.width.get) + n else n - val uint = convert(ULit(unsigned, intervalLit.width), ctx) + val uint = convert(ULit(unsigned, intervalLit.width), ctx, info) val lit = bp.asInstanceOf[KnownBinaryPoint].value fir.DoPrim(firrtl.PrimOps.AsInterval, Seq(uint), Seq(n, n, lit), fir.UnknownType) case lit: ILit => @@ -84,7 +106,7 @@ private[chisel3] object Converter { val consts = e.args.collect { case ILit(i) => i } val args = e.args.flatMap { case _: ILit => None - case other => Some(convert(other, ctx)) + case other => Some(convert(other, ctx, e.sourceInfo)) } val expr = e.op.name match { case "mux" => @@ -95,44 +117,45 @@ private[chisel3] object Converter { } Some(fir.DefNode(convert(e.sourceInfo), e.name, expr)) case e @ DefWire(info, id) => - Some(fir.DefWire(convert(info), e.name, extractType(id))) + Some(fir.DefWire(convert(info), e.name, extractType(id, info))) case e @ DefReg(info, id, clock) => - Some(fir.DefRegister(convert(info), e.name, extractType(id), convert(clock, ctx), - firrtl.Utils.zero, convert(id.getRef, ctx))) + Some(fir.DefRegister(convert(info), e.name, extractType(id, info), convert(clock, ctx, info), + firrtl.Utils.zero, convert(getRef(id, info), ctx, info))) case e @ DefRegInit(info, id, clock, reset, init) => - Some(fir.DefRegister(convert(info), e.name, extractType(id), convert(clock, ctx), - convert(reset, ctx), convert(init, ctx))) + Some(fir.DefRegister(convert(info), e.name, extractType(id, info), convert(clock, ctx, info), + convert(reset, ctx, info), convert(init, ctx, info))) case e @ DefMemory(info, id, t, size) => - Some(firrtl.CDefMemory(convert(info), e.name, extractType(t), size, false)) + Some(firrtl.CDefMemory(convert(info), e.name, extractType(t, info), size, false)) case e @ DefSeqMemory(info, id, t, size, ruw) => - Some(firrtl.CDefMemory(convert(info), e.name, extractType(t), size, true, ruw)) + Some(firrtl.CDefMemory(convert(info), e.name, extractType(t, info), size, true, ruw)) case e: DefMemPort[_] => + val info = e.sourceInfo Some(firrtl.CDefMPort(convert(e.sourceInfo), e.name, fir.UnknownType, - e.source.fullName(ctx), Seq(convert(e.index, ctx), convert(e.clock, ctx)), convert(e.dir))) + e.source.fullName(ctx), Seq(convert(e.index, ctx, info), convert(e.clock, ctx, info)), convert(e.dir))) case Connect(info, loc, exp) => - Some(fir.Connect(convert(info), convert(loc, ctx), convert(exp, ctx))) + Some(fir.Connect(convert(info), convert(loc, ctx, info), convert(exp, ctx, info))) case BulkConnect(info, loc, exp) => - Some(fir.PartialConnect(convert(info), convert(loc, ctx), convert(exp, ctx))) + Some(fir.PartialConnect(convert(info), convert(loc, ctx, info), convert(exp, ctx, info))) case Attach(info, locs) => - Some(fir.Attach(convert(info), locs.map(l => convert(l, ctx)))) + Some(fir.Attach(convert(info), locs.map(l => convert(l, ctx, info)))) case DefInvalid(info, arg) => - Some(fir.IsInvalid(convert(info), convert(arg, ctx))) + Some(fir.IsInvalid(convert(info), convert(arg, ctx, info))) case e @ DefInstance(info, id, _) => Some(fir.DefInstance(convert(info), e.name, id.name)) case Stop(info, clock, ret) => - Some(fir.Stop(convert(info), ret, convert(clock, ctx), firrtl.Utils.one)) - case Printf(info, clock, pable) => + Some(fir.Stop(convert(info), ret, convert(clock, ctx, info), firrtl.Utils.one)) + case e @ Printf(_, info, clock, pable) => val (fmt, args) = unpack(pable, ctx) Some(fir.Print(convert(info), fir.StringLit(fmt), - args.map(a => convert(a, ctx)), convert(clock, ctx), firrtl.Utils.one)) - case Verification(op, info, clk, pred, msg) => + args.map(a => convert(a, ctx, info)), convert(clock, ctx, info), firrtl.Utils.one, e.name)) + case e @ Verification(_, op, info, clk, pred, msg) => val firOp = op match { case Formal.Assert => fir.Formal.Assert case Formal.Assume => fir.Formal.Assume case Formal.Cover => fir.Formal.Cover } - Some(fir.Verification(firOp, convert(info), convert(clk, ctx), - convert(pred, ctx), firrtl.Utils.one, fir.StringLit(msg))) + Some(fir.Verification(firOp, convert(info), convert(clk, ctx, info), + convert(pred, ctx, info), firrtl.Utils.one, fir.StringLit(msg), e.name)) case _ => None } @@ -173,7 +196,7 @@ private[chisel3] object Converter { // Please see WhenFrame for more details case None => cmds.head match { case WhenBegin(info, pred) => - val when = fir.Conditionally(convert(info), convert(pred, ctx), fir.EmptyStmt, fir.EmptyStmt) + val when = fir.Conditionally(convert(info), convert(pred, ctx, info), fir.EmptyStmt, fir.EmptyStmt) val frame = WhenFrame(when, acc, false) rec(Queue.empty, frame +: scope)(cmds.tail) case WhenEnd(info, depth, _) => @@ -221,7 +244,9 @@ private[chisel3] object Converter { case d => d.specifiedDirection } - def extractType(data: Data, clearDir: Boolean = false): fir.Type = data match { + def extractType(data: Data, info: SourceInfo): fir.Type = extractType(data, false, info) + + def extractType(data: Data, clearDir: Boolean, info: SourceInfo): fir.Type = data match { case _: Clock => fir.ClockType case _: AsyncReset => fir.AsyncResetType case _: ResetType => fir.ResetType @@ -231,16 +256,16 @@ private[chisel3] object Converter { case d: FixedPoint => fir.FixedType(convert(d.width), convert(d.binaryPoint)) case d: Interval => fir.IntervalType(d.range.lowerBound, d.range.upperBound, d.range.firrtlBinaryPoint) case d: Analog => fir.AnalogType(convert(d.width)) - case d: Vec[_] => fir.VectorType(extractType(d.sample_element, clearDir), d.length) + case d: Vec[_] => fir.VectorType(extractType(d.sample_element, clearDir, info), d.length) case d: Record => val childClearDir = clearDir || d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output def eltField(elt: Data): fir.Field = (childClearDir, firrtlUserDirOf(elt)) match { - case (true, _) => fir.Field(elt.getRef.name, fir.Default, extractType(elt, true)) + case (true, _) => fir.Field(getRef(elt, info).name, fir.Default, extractType(elt, true, info)) case (false, SpecifiedDirection.Unspecified | SpecifiedDirection.Output) => - fir.Field(elt.getRef.name, fir.Default, extractType(elt, false)) + fir.Field(getRef(elt, info).name, fir.Default, extractType(elt, false, info)) case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) => - fir.Field(elt.getRef.name, fir.Flip, extractType(elt, false)) + fir.Field(getRef(elt, info).name, fir.Flip, extractType(elt, false, info)) } fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) }) } @@ -251,6 +276,7 @@ private[chisel3] object Converter { case StringParam(value) => fir.StringParam(name, fir.StringLit(value)) case RawParam(value) => fir.RawStringParam(name, value) } + def convert(port: Port, topDir: SpecifiedDirection = SpecifiedDirection.Unspecified): fir.Port = { val resolvedDir = SpecifiedDirection.fromParent(topDir, port.dir) val dir = resolvedDir match { @@ -261,8 +287,9 @@ private[chisel3] object Converter { case SpecifiedDirection.Input | SpecifiedDirection.Output => true case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => false } - val tpe = extractType(port.id, clearDir) - fir.Port(fir.NoInfo, port.id.getRef.name, dir, tpe) + val info = UnlocatableSourceInfo // Unfortunately there is no source locator for ports ATM + val tpe = extractType(port.id, clearDir, info) + fir.Port(fir.NoInfo, getRef(port.id, info).name, dir, tpe) } def convert(component: Component): fir.DefModule = component match { @@ -275,5 +302,11 @@ private[chisel3] object Converter { def convert(circuit: Circuit): fir.Circuit = fir.Circuit(fir.NoInfo, circuit.components.map(convert), circuit.name) + + // TODO Unclear if this should just be the default + def convertLazily(circuit: Circuit): fir.Circuit = { + val lazyModules = LazyList() ++ circuit.components + fir.Circuit(fir.NoInfo, lazyModules.map(convert), circuit.name) + } } diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala index 095c8a05..0b568548 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -3,13 +3,13 @@ package chisel3.internal.firrtl import firrtl.{ir => fir} - import chisel3._ import chisel3.internal._ import chisel3.internal.sourceinfo.SourceInfo import chisel3.experimental._ import _root_.firrtl.{ir => firrtlir} -import _root_.firrtl.PrimOps +import _root_.firrtl.{PrimOps, RenameMap} +import _root_.firrtl.annotations.Annotation import scala.collection.immutable.NumericRange import scala.math.BigDecimal.RoundingMode @@ -65,13 +65,19 @@ object PrimOp { } abstract class Arg { - def fullName(ctx: Component): String = name + def localName: String = name + def contextualName(ctx: Component): String = name + def fullName(ctx: Component): String = contextualName(ctx) def name: String } case class Node(id: HasId) extends Arg { - override def fullName(ctx: Component): String = id.getOptionRef match { - case Some(arg) => arg.fullName(ctx) + override def contextualName(ctx: Component): String = id.getOptionRef match { + case Some(arg) => arg.contextualName(ctx) + case None => id.instanceName + } + override def localName: String = id.getOptionRef match { + case Some(arg) => arg.localName case None => id.instanceName } def name: String = id.getOptionRef match { @@ -83,7 +89,7 @@ case class Node(id: HasId) extends Arg { abstract class LitArg(val num: BigInt, widthArg: Width) extends Arg { private[chisel3] def forcedWidth = widthArg.known private[chisel3] def width: Width = if (forcedWidth) widthArg else Width(minWidth) - override def fullName(ctx: Component): String = name + override def contextualName(ctx: Component): String = name // Ensure the node representing this LitArg has a ref to it and a literal binding. def bindLitArg[T <: Element](elem: T): T = { elem.bind(ElementLitBinding(this)) @@ -91,6 +97,14 @@ abstract class LitArg(val num: BigInt, widthArg: Width) extends Arg { elem } + /** Provides a mechanism that LitArgs can have their width adjusted + * to match other members of a VecLiteral + * + * @param newWidth the new width for this + * @return + */ + def cloneWithWidth(newWidth: Width): this.type + protected def minWidth: Int if (forcedWidth) { require(widthArg.get >= minWidth, @@ -106,6 +120,10 @@ case class ULit(n: BigInt, w: Width) extends LitArg(n, w) { def name: String = "UInt" + width + "(\"h0" + num.toString(16) + "\")" def minWidth: Int = 1 max n.bitLength + def cloneWithWidth(newWidth: Width): this.type = { + ULit(n, newWidth).asInstanceOf[this.type] + } + require(n >= 0, s"UInt literal ${n} is negative") } @@ -115,6 +133,10 @@ case class SLit(n: BigInt, w: Width) extends LitArg(n, w) { s"asSInt(${ULit(unsigned, width).name})" } def minWidth: Int = 1 + n.bitLength + + def cloneWithWidth(newWidth: Width): this.type = { + SLit(n, newWidth).asInstanceOf[this.type] + } } case class FPLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n, w) { @@ -123,6 +145,10 @@ case class FPLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n s"asFixedPoint(${ULit(unsigned, width).name}, ${binaryPoint.asInstanceOf[KnownBinaryPoint].value})" } def minWidth: Int = 1 + n.bitLength + + def cloneWithWidth(newWidth: Width): this.type = { + FPLit(n, newWidth, binaryPoint).asInstanceOf[this.type] + } } case class IntervalLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n, w) { @@ -135,20 +161,45 @@ case class IntervalLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends Li IntervalRange.getBound(isClosed = true, BigDecimal(n)), IntervalRange.getRangeWidth(binaryPoint)) } def minWidth: Int = 1 + n.bitLength + + def cloneWithWidth(newWidth: Width): this.type = { + IntervalLit(n, newWidth, binaryPoint).asInstanceOf[this.type] + } } case class Ref(name: String) extends Arg +/** Arg for ports of Modules + * @param mod the module this port belongs to + * @param name the name of the port + */ case class ModuleIO(mod: BaseModule, name: String) extends Arg { - override def fullName(ctx: Component): String = + override def contextualName(ctx: Component): String = if (mod eq ctx.id) name else s"${mod.getRef.name}.$name" } +/** Ports of cloned modules (CloneModuleAsRecord) + * @param mod The original module for which these ports are a clone + * @param name the name of the module instance + */ +case class ModuleCloneIO(mod: BaseModule, name: String) extends Arg { + override def localName = "" + override def contextualName(ctx: Component): String = + // NOTE: mod eq ctx.id only occurs in Target and Named-related APIs + if (mod eq ctx.id) localName else name +} case class Slot(imm: Node, name: String) extends Arg { - override def fullName(ctx: Component): String = - if (imm.fullName(ctx).isEmpty) name else s"${imm.fullName(ctx)}.${name}" + override def contextualName(ctx: Component): String = { + val immName = imm.contextualName(ctx) + if (immName.isEmpty) name else s"$immName.$name" + } + override def localName: String = { + val immName = imm.localName + if (immName.isEmpty) name else s"$immName.$name" + } } case class Index(imm: Arg, value: Arg) extends Arg { def name: String = s"[$value]" - override def fullName(ctx: Component): String = s"${imm.fullName(ctx)}[${value.fullName(ctx)}]" + override def contextualName(ctx: Component): String = s"${imm.contextualName(ctx)}[${value.contextualName(ctx)}]" + override def localName: String = s"${imm.localName}[${value.localName}]" } object Width { @@ -158,6 +209,7 @@ object Width { sealed abstract class Width { type W = Int + def min(that: Width): Width = this.op(that, _ min _) def max(that: Width): Width = this.op(that, _ max _) def + (that: Width): Width = this.op(that, _ + _) def + (that: Int): Width = this.op(this, (a, b) => a + that) @@ -734,14 +786,14 @@ case class Attach(sourceInfo: SourceInfo, locs: Seq[Node]) extends Command case class ConnectInit(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command case class Stop(sourceInfo: SourceInfo, clock: Arg, ret: Int) extends Command case class Port(id: Data, dir: SpecifiedDirection) -case class Printf(sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Command +case class Printf(id: printf.Printf, sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Definition object Formal extends Enumeration { val Assert = Value("assert") val Assume = Value("assume") val Cover = Value("cover") } -case class Verification(op: Formal.Value, sourceInfo: SourceInfo, clock: Arg, - predicate: Arg, message: String) extends Command +case class Verification[T <: BaseSim](id: T, op: Formal.Value, sourceInfo: SourceInfo, clock: Arg, + predicate: Arg, message: String) extends Definition abstract class Component extends Arg { def id: BaseModule def name: String @@ -750,4 +802,7 @@ abstract class Component extends Arg { case class DefModule(id: RawModule, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component case class DefBlackBox(id: BaseBlackBox, name: String, ports: Seq[Port], topDir: SpecifiedDirection, params: Map[String, Param]) extends Component -case class Circuit(name: String, components: Seq[Component], annotations: Seq[ChiselAnnotation] = Seq.empty) +case class Circuit(name: String, components: Seq[Component], annotations: Seq[ChiselAnnotation], renames: RenameMap) { + def firrtlAnnotations: Iterable[Annotation] = annotations.flatMap(_.toFirrtl.update(renames)) + +} |
