summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/scala/chisel3/Aggregate.scala191
-rw-r--r--core/src/main/scala/chisel3/Bits.scala2
-rw-r--r--core/src/main/scala/chisel3/BlackBox.scala12
-rw-r--r--core/src/main/scala/chisel3/Data.scala116
-rw-r--r--core/src/main/scala/chisel3/Mem.scala19
-rw-r--r--core/src/main/scala/chisel3/Module.scala66
-rw-r--r--core/src/main/scala/chisel3/Printable.scala13
-rw-r--r--core/src/main/scala/chisel3/Printf.scala55
-rw-r--r--core/src/main/scala/chisel3/RawModule.scala14
-rw-r--r--core/src/main/scala/chisel3/StrongEnum.scala57
-rw-r--r--core/src/main/scala/chisel3/VerificationStatement.scala284
-rw-r--r--core/src/main/scala/chisel3/experimental/Analog.scala3
-rw-r--r--core/src/main/scala/chisel3/experimental/Attach.scala (renamed from core/src/main/scala/chisel3/Attach.scala)0
-rw-r--r--core/src/main/scala/chisel3/experimental/OpaqueType.scala27
-rw-r--r--core/src/main/scala/chisel3/experimental/Trace.scala50
-rw-r--r--core/src/main/scala/chisel3/experimental/dataview/DataView.scala23
-rw-r--r--core/src/main/scala/chisel3/experimental/dataview/package.scala14
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala2
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala17
-rw-r--r--core/src/main/scala/chisel3/experimental/package.scala28
-rw-r--r--core/src/main/scala/chisel3/internal/BiConnect.scala9
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala74
-rw-r--r--core/src/main/scala/chisel3/internal/Error.scala46
-rw-r--r--core/src/main/scala/chisel3/internal/MonoConnect.scala30
-rw-r--r--core/src/main/scala/chisel3/internal/firrtl/Converter.scala133
-rw-r--r--core/src/main/scala/chisel3/internal/firrtl/IR.scala6
26 files changed, 997 insertions, 294 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala
index 82c02cbc..b6836ea7 100644
--- a/core/src/main/scala/chisel3/Aggregate.scala
+++ b/core/src/main/scala/chisel3/Aggregate.scala
@@ -8,7 +8,7 @@ import chisel3.experimental.dataview.{isView, reifySingleData, InvalidViewExcept
import scala.collection.immutable.{SeqMap, VectorMap}
import scala.collection.mutable.{HashSet, LinkedHashMap}
import scala.language.experimental.macros
-import chisel3.experimental.{BaseModule, BundleLiteralException, ChiselEnum, EnumType, VecLiteralException}
+import chisel3.experimental.{BaseModule, BundleLiteralException, ChiselEnum, EnumType, OpaqueType, VecLiteralException}
import chisel3.internal._
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._
@@ -23,52 +23,6 @@ class AliasedAggregateFieldException(message: String) extends ChiselException(me
* of) other Data objects.
*/
sealed abstract class Aggregate extends Data {
- private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = {
- _parent.foreach(_.addId(this))
- binding = target
-
- val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
- val duplicates = getElements.groupBy(identity).collect { case (x, elts) if elts.size > 1 => x }
- if (!duplicates.isEmpty) {
- this match {
- case b: Record =>
- // show groups of names of fields with duplicate id's
- // The sorts make the displayed order of fields deterministic and matching the order of occurrence in the Bundle.
- // It's a bit convoluted but happens rarely and makes the error message easier to understand
- val dupNames = duplicates.toSeq
- .sortBy(_._id)
- .map { duplicate =>
- b.elements.collect { case x if x._2._id == duplicate._id => x }.toSeq
- .sortBy(_._2._id)
- .map(_._1)
- .reverse
- .mkString("(", ",", ")")
- }
- .mkString(",")
- throw new AliasedAggregateFieldException(
- s"${b.className} contains aliased fields named ${dupNames}"
- )
- case _ =>
- throw new AliasedAggregateFieldException(
- s"Aggregate ${this.getClass} contains aliased fields $duplicates ${duplicates.mkString(",")}"
- )
- }
- }
- for (child <- getElements) {
- child.bind(ChildBinding(this), resolvedDirection)
- }
-
- // Check that children obey the directionality rules.
- val childDirections = getElements.map(_.direction).toSet - ActualDirection.Empty
- direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match {
- case Some(dir) => dir
- case None =>
- val childWithDirections = getElements.zip(getElements.map(_.direction))
- throw MixedDirectionAggregateException(
- s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections"
- )
- }
- }
/** Return an Aggregate's literal value if it is a literal, None otherwise.
* If any element of the aggregate is not a literal with a defined width, the result isn't a literal.
@@ -100,7 +54,10 @@ sealed abstract class Aggregate extends Data {
*/
def getElements: Seq[Data]
- private[chisel3] def width: Width = getElements.map(_.width).foldLeft(0.W)(_ + _)
+ /** Similar to [[getElements]] but allows for more optimized use */
+ private[chisel3] def elementsIterator: Iterator[Data]
+
+ private[chisel3] def width: Width = elementsIterator.map(_.width).foldLeft(0.W)(_ + _)
private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = {
// If the source is a DontCare, generate a DefInvalid for the sink,
@@ -221,7 +178,7 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) extend
val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
sample_element.bind(SampleElementBinding(this), resolvedDirection)
- for (child <- getElements) { // assume that all children are the same
+ for (child <- elementsIterator) { // assume that all children are the same
child.bind(ChildBinding(this), resolvedDirection)
}
@@ -342,8 +299,9 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) extend
new Vec(gen.cloneTypeFull, length).asInstanceOf[this.type]
}
- override def getElements: Seq[Data] =
- (0 until length).map(apply(_))
+ override def getElements: Seq[Data] = self
+
+ final override private[chisel3] def elementsIterator: Iterator[Data] = self.iterator
/** Default "pretty-print" implementation
* Analogous to printing a Seq
@@ -923,23 +881,84 @@ trait VecLike[T <: Data] extends IndexedSeq[T] with HasId with SourceInfoDoc {
*/
abstract class Record(private[chisel3] implicit val compileOptions: CompileOptions) extends Aggregate {
+ private[chisel3] def _isOpaqueType: Boolean = this match {
+ case maybe: OpaqueType => maybe.opaqueType
+ case _ => false
+ }
+
// Doing this earlier than onModuleClose allows field names to be available for prefixing the names
// of hardware created when connecting to one of these elements
private def setElementRefs(): Unit = {
+ val opaqueType = this._isOpaqueType
// Since elements is a map, it is impossible for two elements to have the same
// identifier; however, Namespace sanitizes identifiers to make them legal for Firrtl/Verilog
// which can cause collisions
val _namespace = Namespace.empty
- for ((name, elt) <- elements) { elt.setRef(this, _namespace.name(name, leadingDigitOk = true)) }
+ require(
+ !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(", ")}"
+ )
+ for ((name, elt) <- elements) {
+ elt.setRef(this, _namespace.name(name, leadingDigitOk = true), opaque = opaqueType)
+ }
+ }
+
+ /** Checks that there are no duplicate elements (aka aliased fields) in the Record */
+ private def checkForAndReportDuplicates(): Unit = {
+ // Using List to avoid allocation in the common case of no duplicates
+ var duplicates: List[Data] = Nil
+ // Is there a more optimized datastructure we could use with the Int identities? BitSet? Requires benchmarking.
+ val seen = mutable.HashSet.empty[Data]
+ this.elementsIterator.foreach { e =>
+ if (seen(e)) {
+ duplicates = e :: duplicates
+ }
+ seen += e
+ }
+ if (!duplicates.isEmpty) {
+ // show groups of names of fields with duplicate id's
+ // The sorts make the displayed order of fields deterministic and matching the order of occurrence in the Bundle.
+ // It's a bit convoluted but happens rarely and makes the error message easier to understand
+ val dupNames = duplicates.toSeq
+ .sortBy(_._id)
+ .map { duplicate =>
+ this.elements.collect { case x if x._2._id == duplicate._id => x }.toSeq
+ .sortBy(_._2._id)
+ .map(_._1)
+ .reverse
+ .mkString("(", ",", ")")
+ }
+ .mkString(",")
+ throw new AliasedAggregateFieldException(
+ s"${this.className} contains aliased fields named ${dupNames}"
+ )
+ }
}
private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = {
- try {
- super.bind(target, parentDirection)
- } catch { // nasty compatibility mode shim, where anything flies
- case e: MixedDirectionAggregateException if !compileOptions.dontAssumeDirectionality =>
+ _parent.foreach(_.addId(this))
+ binding = target
+
+ val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
+
+ checkForAndReportDuplicates()
+
+ for ((child, sameChild) <- this.elementsIterator.zip(this.elementsIterator)) {
+ if (child != sameChild) {
+ throwException(
+ s"${this.className} does not return the same objects when calling .elements multiple times. Did you make it a def by mistake?"
+ )
+ }
+ child.bind(ChildBinding(this), resolvedDirection)
+ }
+
+ // Check that children obey the directionality rules.
+ val childDirections = elementsIterator.map(_.direction).toSet - ActualDirection.Empty
+ direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match {
+ case Some(dir) => dir
+ case None =>
val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
- direction = resolvedDirection match {
+ resolvedDirection match {
case SpecifiedDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default)
case SpecifiedDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped)
case _ => ActualDirection.Bidirectional(ActualDirection.Default)
@@ -1096,7 +1115,12 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
def elements: SeqMap[String, Data]
/** Name for Pretty Printing */
- def className: String = this.getClass.getSimpleName
+ def className: String = try {
+ this.getClass.getSimpleName
+ } catch {
+ // This happens if your class is defined in an object and is anonymous
+ case e: java.lang.InternalError if e.getMessage == "Malformed class name" => this.getClass.toString
+ }
private[chisel3] override def typeEquivalent(that: Data): Boolean = that match {
case that: Record =>
@@ -1117,9 +1141,11 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
}
}
- private[chisel3] final def allElements: Seq[Element] = elements.toIndexedSeq.flatMap(_._2.allElements)
+ private[chisel3] final def allElements: Seq[Element] = elementsIterator.flatMap(_.allElements).toIndexedSeq
- override def getElements: Seq[Data] = elements.toIndexedSeq.map(_._2)
+ override def getElements: Seq[Data] = elementsIterator.toIndexedSeq
+
+ final override private[chisel3] def elementsIterator: Iterator[Data] = elements.iterator.map(_._2)
// Helper because Bundle elements are reversed before printing
private[chisel3] def toPrintableHelper(elts: Seq[(String, Data)]): Printable = {
@@ -1138,6 +1164,16 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
* Results in "`\$className(elt0.name -> elt0.value, ...)`"
*/
def toPrintable: Printable = toPrintableHelper(elements.toList)
+
+ /** Implementation of cloneType that is [optionally for Record] overridden by the compiler plugin
+ *
+ * @note This should _never_ be overridden or called in user-code
+ */
+ protected def _cloneTypeImpl: Record = {
+ throwException(
+ s"Internal Error! This should have been implemented by the chisel3-plugin. Please file an issue against chisel3"
+ )
+ }
}
/**
@@ -1162,6 +1198,15 @@ package experimental {
class BundleLiteralException(message: String) extends ChiselException(message)
class VecLiteralException(message: String) extends ChiselException(message)
+ /** Indicates that the compiler plugin should generate [[cloneType]] for this type
+ *
+ * All user-defined [[Record]]s should mix this trait in as it will be required for upgrading to Chisel 3.6.
+ */
+ trait AutoCloneType { self: Record =>
+
+ override def cloneType: this.type = _cloneTypeImpl.asInstanceOf[this.type]
+
+ }
}
/** Base class for data types defined as a bundle of other data types.
@@ -1197,17 +1242,22 @@ package experimental {
* }
* }}}
*/
-abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
+abstract class Bundle(implicit compileOptions: CompileOptions) extends Record with experimental.AutoCloneType {
assert(
_usingPlugin,
"The Chisel compiler plugin is now required for compiling Chisel code. " +
"Please see https://github.com/chipsalliance/chisel3#build-your-own-chisel-projects."
)
- override def className: String = this.getClass.getSimpleName match {
- case name if name.startsWith("$anon$") => "AnonymousBundle" // fallback for anonymous Bundle case
- case "" => "AnonymousBundle" // ditto, but on other platforms
- case name => name
+ override def className: String = try {
+ this.getClass.getSimpleName match {
+ case name if name.startsWith("$anon$") => "AnonymousBundle" // fallback for anonymous Bundle case
+ case "" => "AnonymousBundle" // ditto, but on other platforms
+ case name => name
+ }
+ } catch {
+ // This happens if you have nested objects which your class is defined in
+ case e: java.lang.InternalError if e.getMessage == "Malformed class name" => this.getClass.toString
}
/** The collection of [[Data]]
@@ -1365,15 +1415,8 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
clone
}
- /** Implementation of cloneType using runtime reflection. This should _never_ be overridden or called in user-code
- *
- * @note This is overridden by the compiler plugin (this implementation is never called)
- */
- protected def _cloneTypeImpl: Bundle = {
- throwException(
- s"Internal Error! This should have been implemented by the chisel3-plugin. Please file an issue against chisel3"
- )
- }
+ // This is overriden for binary compatibility reasons in 3.5
+ override protected def _cloneTypeImpl: Bundle = super._cloneTypeImpl.asInstanceOf[Bundle]
/** Default "pretty-print" implementation
* Analogous to printing a Map
diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala
index 72094a65..70704e01 100644
--- a/core/src/main/scala/chisel3/Bits.scala
+++ b/core/src/main/scala/chisel3/Bits.scala
@@ -896,7 +896,7 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S
private[chisel3] override def cloneTypeWidth(w: Width): this.type =
new SInt(w).asInstanceOf[this.type]
- /** Unary negation (expanding width)
+ /** Unary negation (constant width)
*
* @return a hardware $coll equal to zero minus this $coll
* $constantWidth
diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala
index f618901f..c3cb3e66 100644
--- a/core/src/main/scala/chisel3/BlackBox.scala
+++ b/core/src/main/scala/chisel3/BlackBox.scala
@@ -92,8 +92,10 @@ package experimental {
private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = {
implicit val sourceInfo = UnlocatableSourceInfo
- for (x <- getModulePorts) {
- pushCommand(DefInvalid(sourceInfo, x.ref))
+ if (!parentCompileOptions.explicitInvalidate) {
+ for (x <- getModulePorts) {
+ pushCommand(DefInvalid(sourceInfo, x.ref))
+ }
}
}
}
@@ -192,8 +194,10 @@ abstract class BlackBox(
}
private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = {
- for ((_, port) <- _io.map(_.elements).getOrElse(Nil)) {
- pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref))
+ if (!parentCompileOptions.explicitInvalidate) {
+ for ((_, port) <- _io.map(_.elements).getOrElse(Nil)) {
+ pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref))
+ }
}
}
}
diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala
index 592ebe25..50093333 100644
--- a/core/src/main/scala/chisel3/Data.scala
+++ b/core/src/main/scala/chisel3/Data.scala
@@ -5,11 +5,11 @@ package chisel3
import chisel3.experimental.dataview.reify
import scala.language.experimental.macros
-import chisel3.experimental.{Analog, BaseModule, DataMirror, FixedPoint, Interval}
+import chisel3.experimental.{Analog, BaseModule, DataMirror, EnumType, FixedPoint, Interval}
import chisel3.internal.Builder.pushCommand
import chisel3.internal._
import chisel3.internal.firrtl._
-import chisel3.internal.sourceinfo.{DeprecatedSourceInfo, SourceInfo, SourceInfoTransform, UnlocatableSourceInfo}
+import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform, UnlocatableSourceInfo}
import scala.collection.immutable.LazyList // Needed for 2.12 alias
import scala.reflect.ClassTag
@@ -265,6 +265,14 @@ package experimental {
}
}
+ /** Returns the parent module within which a module instance is instantiated
+ *
+ * @note Top-level modules in any given elaboration do not have a parent
+ * @param target a module instance
+ * @return the parent of the `target`, if one exists
+ */
+ def getParent(target: BaseModule): Option[BaseModule] = target._parent
+
// Internal reflection-style APIs, subject to change and removal whenever.
object internal {
def isSynthesizable(target: Data): Boolean = target.isSynthesizable
@@ -353,7 +361,7 @@ private[chisel3] object getRecursiveFields {
_ ++ _
}
case data: Vec[_] =>
- data.getElements.zipWithIndex.map {
+ data.elementsIterator.zipWithIndex.map {
case (fieldData, fieldIndex) =>
getRecursiveFields(fieldData, path = s"$path($fieldIndex)")
}.fold(Seq(data -> path)) {
@@ -371,7 +379,7 @@ private[chisel3] object getRecursiveFields {
}
case data: Vec[_] =>
LazyList(data -> path) ++
- data.getElements.view.zipWithIndex.flatMap {
+ data.elementsIterator.zipWithIndex.flatMap {
case (fieldData, fieldIndex) =>
getRecursiveFields(fieldData, path = s"$path($fieldIndex)")
}
@@ -398,8 +406,8 @@ private[chisel3] object getMatchedFields {
_ ++ _
}
case (x: Vec[_], y: Vec[_]) =>
- (x.getElements
- .zip(y.getElements))
+ (x.elementsIterator
+ .zip(y.elementsIterator))
.map {
case (xElt, yElt) =>
getMatchedFields(xElt, yElt)
@@ -456,7 +464,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
@deprecated("pending removal once all instances replaced", "chisel3")
private[chisel3] def flatten: IndexedSeq[Element] = {
this match {
- case elt: Aggregate => elt.getElements.toIndexedSeq.flatMap { _.flatten }
+ case elt: Aggregate => elt.elementsIterator.toIndexedSeq.flatMap { _.flatten }
case elt: Element => IndexedSeq(elt)
case elt => throwException(s"Cannot flatten type ${elt.getClass}")
}
@@ -501,14 +509,15 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
// Binding stores information about this node's position in the hardware graph.
// This information is supplemental (more than is necessary to generate FIRRTL) and is used to
// perform checks in Chisel, where more informative error messages are possible.
- private var _binding: Option[Binding] = None
+ private var _bindingVar: Binding = null // using nullable var for better memory usage
+ private def _binding: Option[Binding] = Option(_bindingVar)
// Only valid after node is bound (synthesizable), crashes otherwise
protected[chisel3] def binding: Option[Binding] = _binding
protected def binding_=(target: Binding) {
if (_binding.isDefined) {
throw RebindingException(s"Attempted reassignment of binding to $this, from: ${target}")
}
- _binding = Some(target)
+ _bindingVar = target
}
// Similar to topBindingOpt except it explicitly excludes SampleElements which are bound but not
@@ -540,14 +549,15 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
// Both are only valid after binding is set.
// Direction of this node, accounting for parents (force Input / Output) and children.
- private var _direction: Option[ActualDirection] = None
+ private var _directionVar: ActualDirection = null // using nullable var for better memory usage
+ private def _direction: Option[ActualDirection] = Option(_directionVar)
private[chisel3] def direction: ActualDirection = _direction.get
private[chisel3] def direction_=(actualDirection: ActualDirection) {
if (_direction.isDefined) {
throw RebindingException(s"Attempted reassignment of resolved direction to $this")
}
- _direction = Some(actualDirection)
+ _directionVar = actualDirection
}
private[chisel3] def stringAccessor(chiselType: String): String = {
@@ -660,7 +670,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
*/
private[chisel3] def typeEquivalent(that: Data): Boolean
- private def requireVisible(): Unit = {
+ private[chisel3] def requireVisible(): Unit = {
val mod = topBindingOpt.flatMap(_.location)
topBindingOpt match {
case Some(tb: TopBinding) if (mod == Builder.currentModule) =>
@@ -682,6 +692,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
topBindingOpt match {
case Some(binding: ReadOnlyBinding) =>
throwException(s"internal error: attempted to generate LHS ref to ReadOnlyBinding $binding")
+ case Some(ViewBinding(target)) => reify(target).lref
case Some(binding: TopBinding) => Node(this)
case opt => throwException(s"internal error: unknown binding $opt in generating LHS ref")
}
@@ -738,7 +749,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
data match {
case _: Element =>
case agg: Aggregate =>
- agg.getElements.foreach(rec)
+ agg.elementsIterator.foreach(rec)
}
}
rec(this)
@@ -883,6 +894,84 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
def toPrintable: Printable
}
+object Data {
+
+ /**
+ * Provides generic, recursive equality for [[Bundle]] and [[Vec]] hardware. This avoids the
+ * need to use workarounds such as `bundle1.asUInt === bundle2.asUInt` by allowing users
+ * to instead write `bundle1 === bundle2`.
+ *
+ * Static type safety of this comparison is guaranteed at compile time as the extension
+ * method requires the same parameterized type for both the left-hand and right-hand
+ * sides. It is, however, possible to get around this type safety using `Bundle` subtypes
+ * that can differ during runtime (e.g. through a generator). These cases are
+ * subsequently raised as elaboration errors.
+ *
+ * @param lhs The [[Data]] hardware on the left-hand side of the equality
+ */
+ implicit class DataEquality[T <: Data](lhs: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) {
+
+ /** Dynamic recursive equality operator for generic [[Data]]
+ *
+ * @param rhs a hardware [[Data]] to compare `lhs` to
+ * @return a hardware [[Bool]] asserted if `lhs` is equal to `rhs`
+ * @throws ChiselException when `lhs` and `rhs` are different types during elaboration time
+ */
+ def ===(rhs: T): Bool = {
+ (lhs, rhs) match {
+ case (thiz: UInt, that: UInt) => thiz === that
+ case (thiz: SInt, that: SInt) => thiz === that
+ case (thiz: AsyncReset, that: AsyncReset) => thiz.asBool === that.asBool
+ case (thiz: Reset, that: Reset) => thiz === that
+ case (thiz: Interval, that: Interval) => thiz === that
+ case (thiz: FixedPoint, that: FixedPoint) => thiz === that
+ case (thiz: EnumType, that: EnumType) => thiz === that
+ case (thiz: Clock, that: Clock) => thiz.asUInt === that.asUInt
+ case (thiz: Vec[_], that: Vec[_]) =>
+ if (thiz.length != that.length) {
+ throwException(s"Cannot compare Vecs $thiz and $that: Vec sizes differ")
+ } else {
+ thiz.elementsIterator
+ .zip(that.elementsIterator)
+ .map { case (thisData, thatData) => thisData === thatData }
+ .reduce(_ && _)
+ }
+ case (thiz: Record, that: Record) =>
+ if (thiz.elements.size != that.elements.size) {
+ throwException(s"Cannot compare Bundles $thiz and $that: Bundle types differ")
+ } else {
+ thiz.elements.map {
+ case (thisName, thisData) =>
+ if (!that.elements.contains(thisName))
+ throwException(
+ s"Cannot compare Bundles $thiz and $that: field $thisName (from $thiz) was not found in $that"
+ )
+
+ val thatData = that.elements(thisName)
+
+ try {
+ thisData === thatData
+ } catch {
+ case e: ChiselException =>
+ throwException(
+ s"Cannot compare field $thisName in Bundles $thiz and $that: ${e.getMessage.split(": ").last}"
+ )
+ }
+ }
+ .reduce(_ && _)
+ }
+ // This should be matching to (DontCare, DontCare) but the compiler wasn't happy with that
+ case (_: DontCare.type, _: DontCare.type) => true.B
+
+ case (thiz: Analog, that: Analog) =>
+ throwException(s"Cannot compare Analog values $thiz and $that: Equality isn't defined for Analog values")
+ // Runtime types are different
+ case (thiz, that) => throwException(s"Cannot compare $thiz and $that: Runtime types differ")
+ }
+ }
+ }
+}
+
trait WireFactory {
/** Construct a [[Wire]] from a type template
@@ -995,7 +1084,6 @@ object WireDefault {
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
): T = {
- implicit val noSourceInfo = UnlocatableSourceInfo
val x = Wire(t)
requireIsHardware(init, "wire initializer")
x := init
diff --git a/core/src/main/scala/chisel3/Mem.scala b/core/src/main/scala/chisel3/Mem.scala
index d6ab9c4b..91872979 100644
--- a/core/src/main/scala/chisel3/Mem.scala
+++ b/core/src/main/scala/chisel3/Mem.scala
@@ -60,7 +60,10 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
// ensure memory ports are created with the same clock unless explicitly specified to use a different clock
private val clockInst: Option[Clock] = Builder.currentClock
- protected def clockWarning(sourceInfo: Option[SourceInfo]): Unit = {
+ // Only kept for binary compatibility reasons, impossible for users to call
+ protected def clockWarning(sourceInfo: Option[SourceInfo]): Unit = clockWarning(sourceInfo, MemPortDirection.INFER)
+
+ protected def clockWarning(sourceInfo: Option[SourceInfo], dir: MemPortDirection): Unit = {
// Turn into pretty String if possible, if not, Builder.deprecated will find one via stack trace
val infoStr = sourceInfo.collect { case SourceLine(file, line, col) => s"$file:$line:$col" }
Builder.deprecated(
@@ -135,7 +138,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
compileOptions: CompileOptions
): T = {
if (warn && clockInst.isDefined && clock != clockInst.get) {
- clockWarning(Some(sourceInfo))
+ clockWarning(Some(sourceInfo), dir)
}
makePort(sourceInfo, idx, dir, clock)
}
@@ -167,7 +170,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
implicit compileOptions: CompileOptions
): Unit = {
if (warn && clockInst.isDefined && clock != clockInst.get) {
- clockWarning(None)
+ clockWarning(None, MemPortDirection.WRITE)
}
implicit val sourceInfo = UnlocatableSourceInfo
makePort(UnlocatableSourceInfo, idx, MemPortDirection.WRITE, clock) := data
@@ -226,7 +229,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
): Unit = {
implicit val sourceInfo = UnlocatableSourceInfo
if (warn && clockInst.isDefined && clock != clockInst.get) {
- clockWarning(None)
+ clockWarning(None, MemPortDirection.WRITE)
}
val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE, clock).asInstanceOf[Vec[Data]]
val dataVec = data.asInstanceOf[Vec[Data]]
@@ -274,7 +277,13 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
* @note when multiple conflicting writes are performed on a Mem element, the
* result is undefined (unlike Vec, where the last assignment wins)
*/
-sealed class Mem[T <: Data] private[chisel3] (t: T, length: BigInt) extends MemBase(t, length)
+sealed class Mem[T <: Data] private[chisel3] (t: T, length: BigInt) extends MemBase(t, length) {
+ override protected def clockWarning(sourceInfo: Option[SourceInfo], dir: MemPortDirection): Unit = {
+ // Do not issue clock warnings on reads, since they are not clocked
+ if (dir != MemPortDirection.READ)
+ super.clockWarning(sourceInfo, dir)
+ }
+}
object SyncReadMem {
diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala
index ba2d2e32..e5c2a848 100644
--- a/core/src/main/scala/chisel3/Module.scala
+++ b/core/src/main/scala/chisel3/Module.scala
@@ -106,6 +106,38 @@ object Module extends SourceInfoDoc {
module
}
+
+ /** Assign directionality on any IOs that are still Unspecified/Flipped
+ *
+ * Chisel2 did not require explicit direction on nodes
+ * (unspecified treated as output, and flip on nothing was input).
+ * As of 3.6, chisel3 is now also using these semantics, so we need to make it work
+ * even for chisel3 code.
+ * This assigns the explicit directions required by both semantics on all Bundles.
+ * This recursively walks the tree, and assigns directions if no explicit
+ * direction given by upper-levels (override Input / Output)
+ */
+ private[chisel3] def assignCompatDir(data: Data): Unit = {
+ data match {
+ case data: Element => data._assignCompatibilityExplicitDirection
+ case data: Aggregate =>
+ data.specifiedDirection match {
+ // Recurse into children to ensure explicit direction set somewhere
+ case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip =>
+ data match {
+ case record: Record =>
+ record.elementsIterator.foreach(assignCompatDir(_))
+ case vec: Vec[_] =>
+ vec.elementsIterator.foreach(assignCompatDir(_))
+ assignCompatDir(vec.sample_element) // This is used in fromChildren computation
+ }
+ case SpecifiedDirection.Input | SpecifiedDirection.Output =>
+ // forced assign, nothing to do
+ // The .bind algorithm will automatically assign the direction here.
+ // Thus, no implicit assignment is necessary.
+ }
+ }
+ }
}
/** Abstract base class for Modules, which behave much like Verilog modules.
@@ -382,6 +414,10 @@ package internal {
new ClonePorts(proto.getChiselPorts :+ ("io" -> b._io.get): _*)
case _ => new ClonePorts(proto.getChiselPorts: _*)
}
+ // getChiselPorts (nor cloneTypeFull in general)
+ // does not recursively copy the right specifiedDirection,
+ // still need to fix it up here.
+ Module.assignCompatDir(clonePorts)
clonePorts.bind(PortBinding(cloneParent))
clonePorts.setAllParents(Some(cloneParent))
cloneParent._portsRecord = clonePorts
@@ -691,34 +727,8 @@ package experimental {
* io, then do operations on it. This binds a Chisel type in-place (mutably) as an IO.
*/
protected def _bindIoInPlace(iodef: Data): Unit = {
- // Compatibility code: Chisel2 did not require explicit direction on nodes
- // (unspecified treated as output, and flip on nothing was input).
- // This sets assigns the explicit directions required by newer semantics on
- // Bundles defined in compatibility mode.
- // This recursively walks the tree, and assigns directions if no explicit
- // direction given by upper-levels (override Input / Output) AND element is
- // directly inside a compatibility Bundle determined by compile options.
- def assignCompatDir(data: Data, insideCompat: Boolean): Unit = {
- data match {
- case data: Element if insideCompat => data._assignCompatibilityExplicitDirection
- case data: Element => // Not inside a compatibility Bundle, nothing to be done
- case data: Aggregate =>
- data.specifiedDirection match {
- // Recurse into children to ensure explicit direction set somewhere
- case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip =>
- data match {
- case record: Record =>
- val compatRecord = !record.compileOptions.dontAssumeDirectionality
- record.getElements.foreach(assignCompatDir(_, compatRecord))
- case vec: Vec[_] =>
- vec.getElements.foreach(assignCompatDir(_, insideCompat))
- }
- case SpecifiedDirection.Input | SpecifiedDirection.Output => // forced assign, nothing to do
- }
- }
- }
-
- assignCompatDir(iodef, false)
+ // Assign any signals (Chisel or chisel3) with Unspecified/Flipped directions to Output/Input
+ Module.assignCompatDir(iodef)
iodef.bind(PortBinding(this))
_ports += iodef
diff --git a/core/src/main/scala/chisel3/Printable.scala b/core/src/main/scala/chisel3/Printable.scala
index 78655517..82054ee1 100644
--- a/core/src/main/scala/chisel3/Printable.scala
+++ b/core/src/main/scala/chisel3/Printable.scala
@@ -134,6 +134,19 @@ object Printable {
val bufEscapeBackSlash = buf.map(_.replace("\\", "\\\\"))
StringContext(bufEscapeBackSlash.toSeq: _*).cf(data: _*)
}
+
+ private[chisel3] def checkScope(message: Printable): Unit = {
+ def getData(x: Printable): Seq[Data] = {
+ x match {
+ case y: FirrtlFormat => Seq(y.bits)
+ case Name(d) => Seq(d)
+ case FullName(d) => Seq(d)
+ case Printables(p) => p.flatMap(getData(_)).toSeq
+ case _ => Seq() // Handles subtypes PString and Percent
+ }
+ }
+ getData(message).foreach(_.requireVisible())
+ }
}
case class Printables(pables: Iterable[Printable]) extends Printable {
diff --git a/core/src/main/scala/chisel3/Printf.scala b/core/src/main/scala/chisel3/Printf.scala
index bdcca8e1..a7338072 100644
--- a/core/src/main/scala/chisel3/Printf.scala
+++ b/core/src/main/scala/chisel3/Printf.scala
@@ -2,10 +2,11 @@
package chisel3
-import scala.language.experimental.macros
import chisel3.internal._
import chisel3.internal.Builder.pushCommand
import chisel3.internal.sourceinfo.SourceInfo
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox
/** Prints a message in simulation
*
@@ -76,7 +77,44 @@ object printf {
* @param data format string varargs containing data to print
*/
def apply(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf =
- apply(Printable.pack(fmt, data: _*))
+ macro _applyMacroWithInterpolatorCheck
+
+ def _applyMacroWithInterpolatorCheck(
+ c: blackbox.Context
+ )(fmt: c.Tree,
+ data: c.Tree*
+ )(sourceInfo: c.Tree,
+ compileOptions: c.Tree
+ ): c.Tree = {
+ import c.universe._
+ fmt match {
+ case q"scala.StringContext.apply(..$_).s(..$_)" =>
+ c.warning(
+ c.enclosingPosition,
+ "The s-interpolator prints the Scala .toString of Data objects rather than the value " +
+ "of the hardware wire during simulation. Use the cf-interpolator instead. If you want " +
+ "an elaboration time print, use println."
+ )
+ case _ =>
+ }
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("printfWithReset"))
+ q"$apply_impl_do(_root_.chisel3.Printable.pack($fmt, ..$data))($sourceInfo, $compileOptions)"
+ }
+
+ // Private internal methods that serve to maintain binary
+ // compatibility after interpolator check updates
+ @deprecated("This Printf.apply method has been deprecated and will be removed in Chisel 3.6")
+ def apply(fmt: String, sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf =
+ apply(fmt, Nil, sourceInfo, compileOptions)
+
+ @deprecated("This Printf.apply method has been deprecated and will be removed in Chisel 3.6")
+ def apply(
+ fmt: String,
+ data: Seq[Bits],
+ sourceInfo: SourceInfo,
+ compileOptions: CompileOptions
+ ): Printf =
+ apply(Printable.pack(fmt, data: _*))(sourceInfo, compileOptions)
/** Prints a message in simulation
*
@@ -92,7 +130,15 @@ object printf {
* @see [[Printable]] documentation
* @param pable [[Printable]] to print
*/
- def apply(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf = {
+ def apply(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf =
+ printfWithReset(pable)(sourceInfo, compileOptions)
+
+ private[chisel3] def printfWithReset(
+ pable: Printable
+ )(
+ implicit sourceInfo: SourceInfo,
+ compileOptions: CompileOptions
+ ): Printf = {
var printfId: Printf = null
when(!Module.reset.asBool) {
printfId = printfWithoutReset(pable)
@@ -108,6 +154,9 @@ object printf {
): Printf = {
val clock = Builder.forcedClock
val printfId = new Printf(pable)
+
+ Printable.checkScope(pable)
+
pushCommand(chisel3.internal.firrtl.Printf(printfId, sourceInfo, clock.ref, pable))
printfId
}
diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala
index f2ce4c70..9668313a 100644
--- a/core/src/main/scala/chisel3/RawModule.scala
+++ b/core/src/main/scala/chisel3/RawModule.scala
@@ -2,7 +2,6 @@
package chisel3
-import scala.collection.mutable.{ArrayBuffer, HashMap}
import scala.util.Try
import scala.language.experimental.macros
import scala.annotation.nowarn
@@ -13,6 +12,7 @@ import chisel3.internal.Builder._
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.UnlocatableSourceInfo
import _root_.firrtl.annotations.{IsModule, ModuleTarget}
+import scala.collection.immutable.VectorBuilder
/** 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
@@ -23,14 +23,18 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions) extends
//
// RTL construction internals
//
- private val _commands = ArrayBuffer[Command]()
+ // 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) {
require(!_closed, "Can't write to module after module close")
_commands += c
}
- protected def getCommands = {
+ protected def getCommands: Seq[Command] = {
require(_closed, "Can't get commands before module close")
- _commands.toSeq
+ // Unsafe cast but we know that any RawModule uses a DefModule
+ // _component is defined as a var on BaseModule and we cannot override mutable vars
+ _component.get.asInstanceOf[DefModule].commands
}
//
@@ -153,7 +157,7 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions) extends
Seq()
}
}
- val component = DefModule(this, name, firrtlPorts, invalidateCommands ++ getCommands)
+ val component = DefModule(this, name, firrtlPorts, invalidateCommands ++: _commands.result())
_component = Some(component)
_component
}
diff --git a/core/src/main/scala/chisel3/StrongEnum.scala b/core/src/main/scala/chisel3/StrongEnum.scala
index cd6f11ee..3c9f4105 100644
--- a/core/src/main/scala/chisel3/StrongEnum.scala
+++ b/core/src/main/scala/chisel3/StrongEnum.scala
@@ -251,7 +251,28 @@ abstract class EnumType(private[chisel3] val factory: EnumFactory, selfAnnotatin
protected def enumTypeName: String = factory.enumTypeName
- def toPrintable: Printable = FullName(this) // TODO: Find a better pretty printer
+ def toPrintable: Printable = {
+ implicit val sourceInfo = UnlocatableSourceInfo
+ implicit val compileOptions = ExplicitCompileOptions.Strict
+ val allNames = factory.allNames.zip(factory.all)
+ val nameSize = allNames.map(_._1.length).max
+ def leftPad(str: String): String = {
+ str.reverse.padTo(nameSize, ' ').reverse
+ }
+ val allNamesPadded = allNames.map { case (name, value) => leftPad(name) -> value }
+
+ val result = Wire(Vec(nameSize, UInt(8.W))).suggestName(s"_${enumTypeName}Printable")
+ result.foreach(_ := '?'.U)
+
+ for ((name, value) <- allNamesPadded) {
+ when(this === value) {
+ for ((r, c) <- result.zip(name)) {
+ r := c.toChar.U
+ }
+ }
+ }
+ result.map(Character(_)).foldLeft(p"")(_ + _)
+ }
}
abstract class EnumFactory {
@@ -284,6 +305,8 @@ abstract class EnumFactory {
def getWidth: Int = width.get
def all: Seq[Type] = enumInstances
+ /* Accessor for Seq of names in enumRecords */
+ def allNames: Seq[String] = enumNames
private[chisel3] def nameOfValue(id: BigInt): Option[String] = {
enumRecords.find(_.inst.litValue == id).map(_.name)
@@ -339,7 +362,7 @@ abstract class EnumFactory {
} else if (n.getWidth > this.getWidth) {
throwException(s"The UInt being cast to $enumTypeName is wider than $enumTypeName's width ($getWidth)")
} else {
- if (warn && !this.isTotal) {
+ if (!Builder.suppressEnumCastWarning && warn && !this.isTotal) {
Builder.warning(
s"Casting non-literal UInt to $enumTypeName. You can use $enumTypeName.safe to cast without this warning."
)
@@ -409,3 +432,33 @@ private[chisel3] class UnsafeEnum(override val width: Width) extends EnumType(Un
override def cloneType: this.type = new UnsafeEnum(width).asInstanceOf[this.type]
}
private object UnsafeEnum extends EnumFactory
+
+/** Suppress enum cast warnings
+ *
+ * Users should use [[EnumFactory.safe <EnumType>.safe]] when possible.
+ *
+ * This is primarily used for casting from [[UInt]] to a Bundle type that contains an Enum.
+ * {{{
+ * class MyBundle extends Bundle {
+ * val addr = UInt(8.W)
+ * val op = OpEnum()
+ * }
+ *
+ * // Since this is a cast to a Bundle, cannot use OpCode.safe
+ * val bundle = suppressEnumCastWarning {
+ * someUInt.asTypeOf(new MyBundle)
+ * }
+ * }}}
+ */
+object suppressEnumCastWarning {
+ def apply[T](block: => T): T = {
+ val parentWarn = Builder.suppressEnumCastWarning
+
+ Builder.suppressEnumCastWarning = true
+
+ val res = block // execute block
+
+ Builder.suppressEnumCastWarning = parentWarn
+ res
+ }
+}
diff --git a/core/src/main/scala/chisel3/VerificationStatement.scala b/core/src/main/scala/chisel3/VerificationStatement.scala
index 7229c412..a0040d78 100644
--- a/core/src/main/scala/chisel3/VerificationStatement.scala
+++ b/core/src/main/scala/chisel3/VerificationStatement.scala
@@ -11,40 +11,100 @@ import chisel3.internal.sourceinfo.SourceInfo
import scala.reflect.macros.blackbox
-object assert {
+/** Scaladoc information for internal verification statement macros
+ * that are used in objects assert, assume and cover.
+ *
+ * @groupdesc VerifPrintMacros
+ *
+ * <p>
+ * '''These internal methods are not part of the public-facing API!'''
+ * </p>
+ * <br>
+ *
+ * @groupprio VerifPrintMacros 1001
+ */
+trait VerifPrintMacrosDoc
+
+object assert extends VerifPrintMacrosDoc {
+
+ /** Checks for a condition to be valid in the circuit at rising clock edge
+ * when not in reset. If the condition evaluates to false, the circuit
+ * simulation stops with an error.
+ *
+ * @param cond condition, assertion fires (simulation fails) when false
+ * @param message optional format string to print when the assertion fires
+ * @param data optional bits to print in the message formatting
+ *
+ * @note See [[printf.apply(fmt:String* printf]] for format string documentation
+ * @note currently cannot be used in core Chisel / libraries because macro
+ * defs need to be compiled first and the SBT project is not set up to do
+ * that
+ */
+ // Macros currently can't take default arguments, so we need two functions to emulate defaults.
+ def apply(
+ cond: Bool,
+ message: String,
+ data: Bits*
+ )(
+ implicit sourceInfo: SourceInfo,
+ compileOptions: CompileOptions
+ ): Assert = macro _applyMacroWithInterpolatorCheck
/** Checks for a condition to be valid in the circuit at all times. If the
* condition evaluates to false, the circuit simulation stops with an error.
*
- * Does not fire when in reset (defined as the encapsulating Module's
- * reset). If your definition of reset is not the encapsulating Module's
- * reset, you will need to gate this externally.
+ * Does not fire when in reset (defined as the current implicit reset, e.g. as set by
+ * the enclosing `withReset` or Module.reset.
*
* May be called outside of a Module (like defined in a function), so
* functions using assert make the standard Module assumptions (single clock
* and single reset).
*
- * @param cond condition, assertion fires (simulation fails) when false
- * @param message optional format string to print when the assertion fires
- * @param data optional bits to print in the message formatting
+ * @param cond condition, assertion fires (simulation fails) on a rising clock edge when false and reset is not asserted
+ * @param message optional chisel Printable type message
*
- * @note See [[printf.apply(fmt:String* printf]] for format string documentation
+ * @note See [[printf.apply(fmt:Printable)]] for documentation on printf using Printables
* @note currently cannot be used in core Chisel / libraries because macro
* defs need to be compiled first and the SBT project is not set up to do
* that
*/
- // Macros currently can't take default arguments, so we need two functions to emulate defaults.
def apply(
cond: Bool,
- message: String,
- data: Bits*
+ message: Printable
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
- ): Assert = macro _applyMacroWithMessage
+ ): Assert = macro _applyMacroWithPrintableMessage
+
def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assert =
macro _applyMacroWithNoMessage
+ import VerificationStatement._
+
+ /** @group VerifPrintMacros */
+ def _applyMacroWithInterpolatorCheck(
+ c: blackbox.Context
+ )(cond: c.Tree,
+ message: c.Tree,
+ data: c.Tree*
+ )(sourceInfo: c.Tree,
+ compileOptions: c.Tree
+ ): c.Tree = {
+ import c.universe._
+ message match {
+ case q"scala.StringContext.apply(..$_).s(..$_)" =>
+ c.warning(
+ c.enclosingPosition,
+ "The s-interpolator prints the Scala .toString of Data objects rather than the value " +
+ "of the hardware wire during simulation. Use the cf-interpolator instead. If you want " +
+ "an elaboration time check, call assert with a Boolean condition."
+ )
+ case _ =>
+ }
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
+ q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo, $compileOptions)"
+ }
+
/** An elaboration-time assertion. Calls the built-in Scala assert function. */
def apply(cond: Boolean, message: => String): Unit = Predef.assert(cond, message)
@@ -54,8 +114,11 @@ object assert {
/** Named class for assertions. */
final class Assert private[chisel3] () extends VerificationStatement
- import VerificationStatement._
-
+ /** @group VerifPrintMacros */
+ @deprecated(
+ "This method has been deprecated in favor of _applyMacroWithStringMessage. Please use the same.",
+ "Chisel 3.5"
+ )
def _applyMacroWithMessage(
c: blackbox.Context
)(cond: c.Tree,
@@ -65,10 +128,38 @@ object assert {
compileOptions: c.Tree
): c.Tree = {
import c.universe._
- val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine"))
- q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message), ..$data)($sourceInfo, $compileOptions)"
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
+ q"$apply_impl_do($cond, ${getLine(c)},_root_.scala.Some(_root_.chisel3.Printable.pack($message,..$data)))($sourceInfo, $compileOptions)"
+ }
+
+ /** @group VerifPrintMacros */
+ def _applyMacroWithStringMessage(
+ c: blackbox.Context
+ )(cond: c.Tree,
+ message: c.Tree,
+ data: c.Tree*
+ )(sourceInfo: c.Tree,
+ compileOptions: c.Tree
+ ): c.Tree = {
+ import c.universe._
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
+ q"$apply_impl_do($cond, ${getLine(c)},_root_.scala.Some(_root_.chisel3.Printable.pack($message,..$data)))($sourceInfo, $compileOptions)"
}
+ /** @group VerifPrintMacros */
+ def _applyMacroWithPrintableMessage(
+ c: blackbox.Context
+ )(cond: c.Tree,
+ message: c.Tree
+ )(sourceInfo: c.Tree,
+ compileOptions: c.Tree
+ ): c.Tree = {
+ import c.universe._
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
+ q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo, $compileOptions)"
+ }
+
+ /** @group VerifPrintMacros */
def _applyMacroWithNoMessage(
c: blackbox.Context
)(cond: c.Tree
@@ -76,11 +167,14 @@ object assert {
compileOptions: c.Tree
): c.Tree = {
import c.universe._
- val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine"))
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)"
}
- /** Used by our macros. Do not call directly! */
+ /** This will be removed in Chisel 3.6 in favor of the Printable version
+ *
+ * @group VerifPrintMacros
+ */
def _applyWithSourceLine(
cond: Bool,
line: SourceLineInfo,
@@ -92,14 +186,32 @@ object assert {
): Assert = {
val id = new Assert()
when(!Module.reset.asBool()) {
- failureMessage("Assertion", line, cond, message, data)
+ failureMessage("Assertion", line, cond, message.map(Printable.pack(_, data: _*)))
+ Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, ""))
+ }
+ id
+ }
+
+ /** @group VerifPrintMacros */
+ def _applyWithSourceLinePrintable(
+ cond: Bool,
+ line: SourceLineInfo,
+ message: Option[Printable]
+ )(
+ implicit sourceInfo: SourceInfo,
+ compileOptions: CompileOptions
+ ): Assert = {
+ val id = new Assert()
+ message.foreach(Printable.checkScope(_))
+ when(!Module.reset.asBool()) {
+ failureMessage("Assertion", line, cond, message)
Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, ""))
}
id
}
}
-object assume {
+object assume extends VerifPrintMacrosDoc {
/** Assumes a condition to be valid in the circuit at all times.
* Acts like an assertion in simulation and imposes a declarative
@@ -127,7 +239,33 @@ object assume {
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
- ): Assume = macro _applyMacroWithMessage
+ ): Assume = macro _applyMacroWithInterpolatorCheck
+
+ /** Assumes a condition to be valid in the circuit at all times.
+ * Acts like an assertion in simulation and imposes a declarative
+ * assumption on the state explored by formal tools.
+ *
+ * Does not fire when in reset (defined as the encapsulating Module's
+ * reset). If your definition of reset is not the encapsulating Module's
+ * reset, you will need to gate this externally.
+ *
+ * May be called outside of a Module (like defined in a function), so
+ * functions using assert make the standard Module assumptions (single clock
+ * and single reset).
+ *
+ * @param cond condition, assertion fires (simulation fails) when false
+ * @param message optional Printable type message when the assertion fires
+ *
+ * @note See [[printf.apply(fmt:Printable]] for documentation on printf using Printables
+ */
+ def apply(
+ cond: Bool,
+ message: Printable
+ )(
+ implicit sourceInfo: SourceInfo,
+ compileOptions: CompileOptions
+ ): Assume = macro _applyMacroWithPrintableMessage
+
def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assume =
macro _applyMacroWithNoMessage
@@ -142,6 +280,35 @@ object assume {
import VerificationStatement._
+ /** @group VerifPrintMacros */
+ def _applyMacroWithInterpolatorCheck(
+ c: blackbox.Context
+ )(cond: c.Tree,
+ message: c.Tree,
+ data: c.Tree*
+ )(sourceInfo: c.Tree,
+ compileOptions: c.Tree
+ ): c.Tree = {
+ import c.universe._
+ message match {
+ case q"scala.StringContext.apply(..$_).s(..$_)" =>
+ c.warning(
+ c.enclosingPosition,
+ "The s-interpolator prints the Scala .toString of Data objects rather than the value " +
+ "of the hardware wire during simulation. Use the cf-interpolator instead. If you want " +
+ "an elaboration time check, call assert with a Boolean condition."
+ )
+ case _ =>
+ }
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
+ q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo, $compileOptions)"
+ }
+
+ /** @group VerifPrintMacros */
+ @deprecated(
+ "This method has been deprecated in favor of _applyMacroWithStringMessage. Please use the same.",
+ "Chisel 3.5"
+ )
def _applyMacroWithMessage(
c: blackbox.Context
)(cond: c.Tree,
@@ -151,10 +318,38 @@ object assume {
compileOptions: c.Tree
): c.Tree = {
import c.universe._
- val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine"))
- q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message), ..$data)($sourceInfo, $compileOptions)"
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
+ q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo, $compileOptions)"
+ }
+
+ /** @group VerifPrintMacros */
+ def _applyMacroWithStringMessage(
+ c: blackbox.Context
+ )(cond: c.Tree,
+ message: c.Tree,
+ data: c.Tree*
+ )(sourceInfo: c.Tree,
+ compileOptions: c.Tree
+ ): c.Tree = {
+ import c.universe._
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
+ q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo, $compileOptions)"
}
+ /** @group VerifPrintMacros */
+ def _applyMacroWithPrintableMessage(
+ c: blackbox.Context
+ )(cond: c.Tree,
+ message: c.Tree
+ )(sourceInfo: c.Tree,
+ compileOptions: c.Tree
+ ): c.Tree = {
+ import c.universe._
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
+ q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo, $compileOptions)"
+ }
+
+ /** @group VerifPrintMacros */
def _applyMacroWithNoMessage(
c: blackbox.Context
)(cond: c.Tree
@@ -162,11 +357,14 @@ object assume {
compileOptions: c.Tree
): c.Tree = {
import c.universe._
- val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine"))
+ val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)"
}
- /** Used by our macros. Do not call directly! */
+ /** This will be removed in Chisel 3.6 in favor of the Printable version
+ *
+ * @group VerifPrintMacros
+ */
def _applyWithSourceLine(
cond: Bool,
line: SourceLineInfo,
@@ -178,14 +376,32 @@ object assume {
): Assume = {
val id = new Assume()
when(!Module.reset.asBool()) {
- failureMessage("Assumption", line, cond, message, data)
+ failureMessage("Assumption", line, cond, message.map(Printable.pack(_, data: _*)))
+ Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, ""))
+ }
+ id
+ }
+
+ /** @group VerifPrintMacros */
+ def _applyWithSourceLinePrintable(
+ cond: Bool,
+ line: SourceLineInfo,
+ message: Option[Printable]
+ )(
+ implicit sourceInfo: SourceInfo,
+ compileOptions: CompileOptions
+ ): Assume = {
+ val id = new Assume()
+ message.foreach(Printable.checkScope(_))
+ when(!Module.reset.asBool()) {
+ failureMessage("Assumption", line, cond, message)
Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, ""))
}
id
}
}
-object cover {
+object cover extends VerifPrintMacrosDoc {
/** Declares a condition to be covered.
* At ever clock event, a counter is incremented iff the condition is active
@@ -213,6 +429,7 @@ object cover {
import VerificationStatement._
+ /** @group VerifPrintMacros */
def _applyMacroWithNoMessage(
c: blackbox.Context
)(cond: c.Tree
@@ -224,6 +441,7 @@ object cover {
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)"
}
+ /** @group VerifPrintMacros */
def _applyMacroWithMessage(
c: blackbox.Context
)(cond: c.Tree,
@@ -236,7 +454,7 @@ object cover {
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo, $compileOptions)"
}
- /** Used by our macros. Do not call directly! */
+ /** @group VerifPrintMacros */
def _applyWithSourceLine(
cond: Bool,
line: SourceLineInfo,
@@ -299,13 +517,11 @@ private object VerificationStatement {
(p.source.file.name, p.line, p.lineContent.trim)
}
- // creates a printf to inform the user of a failed assertion or assumption
def failureMessage(
kind: String,
lineInfo: SourceLineInfo,
cond: Bool,
- message: Option[String],
- data: Seq[Bits]
+ message: Option[Printable]
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
@@ -314,11 +530,11 @@ private object VerificationStatement {
val lineMsg = s"$filename:$line $content".replaceAll("%", "%%")
val fmt = message match {
case Some(msg) =>
- s"$kind failed: $msg\n at $lineMsg\n"
- case None => s"$kind failed\n at $lineMsg\n"
+ p"$kind failed: $msg\n at $lineMsg\n"
+ case None => p"$kind failed\n at $lineMsg\n"
}
when(!cond) {
- printf.printfWithoutReset(fmt, data: _*)
+ printf.printfWithoutReset(fmt)
}
}
}
diff --git a/core/src/main/scala/chisel3/experimental/Analog.scala b/core/src/main/scala/chisel3/experimental/Analog.scala
index a366f0c3..7d89025c 100644
--- a/core/src/main/scala/chisel3/experimental/Analog.scala
+++ b/core/src/main/scala/chisel3/experimental/Analog.scala
@@ -69,7 +69,8 @@ final class Analog private (private[chisel3] val width: Width) extends Element {
}
targetTopBinding match {
- case _: WireBinding | _: PortBinding => direction = ActualDirection.Bidirectional(ActualDirection.Default)
+ case _: WireBinding | _: PortBinding | _: ViewBinding | _: AggregateViewBinding =>
+ direction = ActualDirection.Bidirectional(ActualDirection.Default)
case x => throwException(s"Analog can only be Ports and Wires, not '$x'")
}
binding = target
diff --git a/core/src/main/scala/chisel3/Attach.scala b/core/src/main/scala/chisel3/experimental/Attach.scala
index 5c9cfe53..5c9cfe53 100644
--- a/core/src/main/scala/chisel3/Attach.scala
+++ b/core/src/main/scala/chisel3/experimental/Attach.scala
diff --git a/core/src/main/scala/chisel3/experimental/OpaqueType.scala b/core/src/main/scala/chisel3/experimental/OpaqueType.scala
new file mode 100644
index 00000000..e7a2a15d
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/OpaqueType.scala
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental
+
+import chisel3._
+
+/** Indicates if this Record represents an "Opaque Type"
+ *
+ * Opaque types provide a mechanism for user-defined types
+ * that do not impose any "boxing" overhead in the emitted FIRRTL and Verilog.
+ * You can think about an opaque type Record as a box around
+ * a single element that only exists at Chisel elaboration time.
+ * Put another way, if this trait is mixed into a Record,
+ * the Record may only contain a single element with an empty name
+ * and there will be no `_` in the name for that element in the emitted Verilog.
+ *
+ * @see RecordSpec in Chisel's tests for example usage and expected output
+ */
+trait OpaqueType { self: Record =>
+
+ /** If set to true, indicates that this Record is an OpaqueType
+ *
+ * Users can override this if they need more dynamic control over the behavior for when
+ * instances of this type are considered opaque
+ */
+ def opaqueType: Boolean = true
+}
diff --git a/core/src/main/scala/chisel3/experimental/Trace.scala b/core/src/main/scala/chisel3/experimental/Trace.scala
index 4ab615a5..eb2ed46a 100644
--- a/core/src/main/scala/chisel3/experimental/Trace.scala
+++ b/core/src/main/scala/chisel3/experimental/Trace.scala
@@ -1,7 +1,7 @@
package chisel3.experimental
import chisel3.internal.HasId
-import chisel3.{Aggregate, Data, Element, Module}
+import chisel3.{Aggregate, Data, Element, Module, RawModule}
import firrtl.AnnotationSeq
import firrtl.annotations.{Annotation, CompleteTarget, SingleTargetAnnotation}
import firrtl.transforms.DontTouchAllTargets
@@ -22,13 +22,22 @@ import firrtl.transforms.DontTouchAllTargets
object Trace {
/** Trace a Instance name. */
- def traceName(x: Module): Unit = {
+ @deprecated("switch to traceNameV2 (until Chisel 3.6)", "3.5.5")
+ def traceName(x: Module): Unit = traceName(x: RawModule)
+
+ /** Trace a Instance name. */
+ @deprecated("switch to traceNameV2 (until Chisel 3.6)", "3.5.5")
+ def traceName(x: RawModule): Unit = {
annotate(new ChiselAnnotation {
def toFirrtl: Annotation = TraceNameAnnotation(x.toAbsoluteTarget, x.toAbsoluteTarget)
})
}
- /** Trace a Data name. */
+ /** Trace a Data name. This adds "don't touch" semantics to anything traced. */
+ @deprecated(
+ "switch to traceNameV2 (until Chisel 3.6) and add dontTouch if you want \"don't touch\" behavior",
+ "3.5.5"
+ )
def traceName(x: Data): Unit = {
x match {
case aggregate: Aggregate =>
@@ -43,7 +52,29 @@ object Trace {
}
}
- /** An Annotation that records the original target annotate from Chisel.
+ /** Trace an Instance name. */
+ def traceNameV2(x: RawModule): Unit = {
+ annotate(new ChiselAnnotation {
+ def toFirrtl: Annotation = TraceAnnotation(x.toAbsoluteTarget, x.toAbsoluteTarget)
+ })
+ }
+
+ /** Trace a Data name. This does NOT add "don't touch" semantics to the traced data. If you want this behavior, use an explicit [[chisel3.dontTouch]]. */
+ def traceNameV2(x: Data): Unit = {
+ x match {
+ case aggregate: Aggregate =>
+ annotate(new ChiselAnnotation {
+ def toFirrtl: Annotation = TraceAnnotation(aggregate.toAbsoluteTarget, aggregate.toAbsoluteTarget)
+ })
+ aggregate.elementsIterator.foreach(traceNameV2)
+ case element: Element =>
+ annotate(new ChiselAnnotation {
+ def toFirrtl: Annotation = TraceAnnotation(element.toAbsoluteTarget, element.toAbsoluteTarget)
+ })
+ }
+ }
+
+ /** An Annotation that records the original target annotate from Chisel. This adds don't touch behavior.
*
* @param target target that should be renamed by [[firrtl.RenameMap]] in the firrtl transforms.
* @param chiselTarget original annotated target in Chisel, which should not be changed or renamed in FIRRTL.
@@ -54,6 +85,16 @@ object Trace {
def duplicate(n: T): Annotation = this.copy(target = n)
}
+ /** An Annotation that records the original target annotate from Chisel. This does NOT add don't touch behavior.
+ *
+ * @param target target that should be renamed by [[firrtl.RenameMap]] in the firrtl transforms.
+ * @param chiselTarget original annotated target in Chisel, which should not be changed or renamed in FIRRTL.
+ */
+ private case class TraceAnnotation[T <: CompleteTarget](target: T, chiselTarget: T)
+ extends SingleTargetAnnotation[T] {
+ def duplicate(n: T): Annotation = this.copy(target = n)
+ }
+
/** Get [[CompleteTarget]] of the target `x` for `annos`.
* This API can be used to find the final reference to a signal or module which is marked by `traceName`
*/
@@ -65,5 +106,6 @@ object Trace {
*/
def finalTargetMap(annos: AnnotationSeq): Map[CompleteTarget, Seq[CompleteTarget]] = annos.collect {
case TraceNameAnnotation(t, chiselTarget) => chiselTarget -> t
+ case TraceAnnotation(t, chiselTarget) => chiselTarget -> t
}.groupBy(_._1).map { case (k, v) => k -> v.map(_._2) }
}
diff --git a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala
index 7f20964d..cc555b11 100644
--- a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala
+++ b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala
@@ -592,4 +592,27 @@ object PartialDataView {
implicit sourceInfo: SourceInfo
): DataView[T, V] =
new DataView[T, V](mkView, mapping, _total = false)
+
+ /** Constructs a non-total [[DataView]] mapping from a [[Bundle]] type to a parent [[Bundle]] type
+ *
+ * @param mkView a function constructing an instance `V` from an instance of `T`
+ * @return the [[DataView]] that enables viewing instances of a [[Bundle]] as instances of a parent type
+ */
+ def supertype[T <: Bundle, V <: Bundle](
+ mkView: T => V
+ )(
+ implicit ev: SubTypeOf[T, V],
+ sourceInfo: SourceInfo
+ ): DataView[T, V] =
+ mapping[T, V](
+ mkView,
+ {
+ case (a, b) =>
+ val aElts = a.elements
+ val bElts = b.elements
+ val bKeys = bElts.keySet
+ val keys = aElts.keysIterator.filter(bKeys.contains)
+ keys.map(k => aElts(k) -> bElts(k)).toSeq
+ }
+ )
}
diff --git a/core/src/main/scala/chisel3/experimental/dataview/package.scala b/core/src/main/scala/chisel3/experimental/dataview/package.scala
index 71ae2d8f..a52e88cf 100644
--- a/core/src/main/scala/chisel3/experimental/dataview/package.scala
+++ b/core/src/main/scala/chisel3/experimental/dataview/package.scala
@@ -43,24 +43,14 @@ package object dataview {
"${A} is not a subtype of ${B}! Did you mean .viewAs[${B}]? " +
"Please see https://www.chisel-lang.org/chisel3/docs/cookbooks/dataview"
)
- private type SubTypeOf[A, B] = A <:< B
+ private[dataview] type SubTypeOf[A, B] = A <:< B
/** Provides `viewAsSupertype` for subclasses of [[Bundle]] */
implicit class BundleUpcastable[T <: Bundle](target: T) {
/** View a [[Bundle]] or [[Record]] as a parent type (upcast) */
def viewAsSupertype[V <: Bundle](proto: V)(implicit ev: SubTypeOf[T, V], sourceInfo: SourceInfo): V = {
- implicit val dataView = PartialDataView.mapping[T, V](
- _ => proto,
- {
- case (a, b) =>
- val aElts = a.elements
- val bElts = b.elements
- val bKeys = bElts.keySet
- val keys = aElts.keysIterator.filter(bKeys.contains)
- keys.map(k => aElts(k) -> bElts(k)).toSeq
- }
- )
+ implicit val dataView = PartialDataView.supertype[T, V](_ => proto)
target.viewAs[V]
}
}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
index 36bf6f87..99eacc7d 100644
--- a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
@@ -103,7 +103,7 @@ object Definition extends SourceInfoDoc {
): Definition[T] = {
val dynamicContext = {
val context = Builder.captureContext()
- new DynamicContext(Nil, context.throwOnFirstError, context.warnReflectiveNaming)
+ new DynamicContext(Nil, context.throwOnFirstError, context.warnReflectiveNaming, context.warningsAsErrors)
}
Builder.globalNamespace.copyTo(dynamicContext.globalNamespace)
dynamicContext.inDefinition = true
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
index c83479b0..aa35455d 100644
--- a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
@@ -335,11 +335,20 @@ object Lookupable {
}
def instanceLookup[A](that: A => B, instance: Instance[A]): C = {
val ret = that(instance.proto)
- val ioMap: Option[Map[Data, Data]] = instance.underlying match {
- case Clone(x: ModuleClone[_]) => Some(x.ioMap)
- case Proto(x: BaseModule) => Some(x.getChiselPorts.map { case (_, data) => data -> data }.toMap)
- case _ => None
+
+ def getIoMap(hierarchy: Hierarchy[_]): Option[Map[Data, Data]] = {
+ hierarchy.underlying match {
+ case Clone(x: ModuleClone[_]) => Some(x.ioMap)
+ case Proto(x: BaseModule) => Some(x.getChiselPorts.map { case (_, data) => data -> data }.toMap)
+ case Clone(x: InstantiableClone[_]) => getIoMap(x._innerContext)
+ case Clone(x: InstanceClone[_]) => None
+ case other => {
+ Builder.exception(s"Internal Error! Unexpected case where we can't get IO Map: $other")
+ }
+ }
}
+ val ioMap = getIoMap(instance)
+
if (isView(ret)) {
cloneViewToContext(ret, instance.cache, ioMap, instance.getInnerDataContext)
} else {
diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala
index b1d9cae4..39131943 100644
--- a/core/src/main/scala/chisel3/experimental/package.scala
+++ b/core/src/main/scala/chisel3/experimental/package.scala
@@ -235,9 +235,33 @@ package object experimental {
}
}
- // Use to add a prefix to any component generated in input scope
+ /** Use to add a prefix to any components generated in the provided scope.
+ *
+ * @example {{{
+ *
+ * val x1 = prefix("first") {
+ * // Anything generated here will be prefixed with "first"
+ * }
+ *
+ * val x2 = prefix(mysignal) {
+ * // Anything generated here will be prefixed with the name of mysignal
+ * }
+ *
+ * }}}
+ */
val prefix = chisel3.internal.prefix
- // Use to remove prefixes not in provided scope
+
+ /** Use to clear existing prefixes so no signals within the scope are prefixed
+ * by signals/names outside the scope
+ *
+ * @example {{{
+ *
+ * val x1 = prefix("first") {
+ * // Anything generated here will have no prefix.
+ * // The result returned from this would *still* be called `x1` however.
+ * }
+ * }}}
+ */
val noPrefix = chisel3.internal.noPrefix
// ****************************** Hardware equivalents of Scala Tuples ******************************
diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala
index e8fb2361..74376598 100644
--- a/core/src/main/scala/chisel3/internal/BiConnect.scala
+++ b/core/src/main/scala/chisel3/internal/BiConnect.scala
@@ -227,9 +227,12 @@ private[chisel3] object BiConnect {
context_mod: RawModule
): Unit = {
// Verify right has no extra fields that left doesn't have
- for ((field, right_sub) <- right_r.elements) {
- if (!left_r.elements.isDefinedAt(field)) {
- if (connectCompileOptions.connectFieldsMustMatch) {
+
+ // For each field in left, descend with right.
+ // Don't bother doing this check if we don't expect it to necessarily pass.
+ if (connectCompileOptions.connectFieldsMustMatch) {
+ for ((field, right_sub) <- right_r.elements) {
+ if (!left_r.elements.isDefinedAt(field)) {
throw MissingLeftFieldException(field)
}
}
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index 61f94f8f..e3dfff09 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -88,10 +88,19 @@ trait InstanceId {
private[chisel3] trait HasId extends InstanceId {
private[chisel3] def _onModuleClose: Unit = {}
- private[chisel3] var _parent: Option[BaseModule] = Builder.currentModule
+ // using nullable var for better memory usage
+ private var _parentVar: BaseModule = Builder.currentModule.getOrElse(null)
+ private[chisel3] def _parent: Option[BaseModule] = Option(_parentVar)
+ private[chisel3] def _parent_=(target: Option[BaseModule]): Unit = {
+ _parentVar = target.getOrElse(null)
+ }
// 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 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
@@ -100,10 +109,12 @@ private[chisel3] trait HasId extends InstanceId {
override def equals(that: Any): Boolean = super.equals(that)
// Contains suggested seed (user-decided seed)
- private var suggested_seed: Option[String] = None
+ 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_seed: Option[String] = None
+ 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
@@ -131,7 +142,7 @@ 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_seed = Some(seed)
+ auto_seedVar = seed
for (hook <- auto_postseed_hooks.reverse) { hook(seed) }
naming_prefix = Builder.getPrefix
this
@@ -159,9 +170,12 @@ private[chisel3] trait HasId extends InstanceId {
* @return this object
*/
def suggestName(seed: => String): this.type = {
- if (suggested_seed.isEmpty) suggested_seed = Some(seed)
- naming_prefix = Builder.getPrefix
- for (hook <- suggest_postseed_hooks.reverse) { hook(seed) }
+ if (suggested_seed.isEmpty) {
+ 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) }
+ }
this
}
@@ -171,7 +185,7 @@ private[chisel3] trait HasId extends InstanceId {
private[chisel3] def forceFinalName(seed: String): this.type = {
// This could be called with user prefixes, ignore them
noPrefix {
- suggested_seed = Some(seed)
+ suggested_seedVar = seed
this.suggestName(seed)
}
}
@@ -212,16 +226,21 @@ private[chisel3] trait HasId extends InstanceId {
naming_prefix = Nil
}
- private var _ref: Option[Arg] = None
+ private var _refVar: Arg = null // using nullable var for better memory usage
+ private def _ref: Option[Arg] = Option(_refVar)
private[chisel3] def setRef(imm: Arg): Unit = setRef(imm, false)
private[chisel3] def setRef(imm: Arg, force: Boolean): Unit = {
if (_ref.isEmpty || force) {
- _ref = Some(imm)
+ _refVar = imm
}
}
- private[chisel3] def setRef(parent: HasId, name: String): Unit = setRef(Slot(Node(parent), name))
- 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: HasId, name: String, opaque: Boolean = false): Unit = {
+ if (!opaque) setRef(Slot(Node(parent), name))
+ else setRef(OpaqueSlot(Node(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 getRef: Arg = _ref.get
private[chisel3] def getOptionRef: Option[Arg] = _ref
@@ -232,10 +251,21 @@ private[chisel3] trait HasId extends InstanceId {
// 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.
- val errors = new ErrorLog
+ // It's especially bad because --warnings-as-errors does not work with these warnings
+ val nameGuess = _computeName(None) match {
+ case Some(name) => s": '$name'"
+ case None => ""
+ }
+ val parentGuess = _parent match {
+ case Some(ViewParent) => s", in module '${reifyParent.pathName}'"
+ case Some(p) => s", in module '${p.pathName}'"
+ case None => ""
+ }
+ 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. " +
- "This will become an error in Chisel 3.6."
+ val msg =
+ "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated" + nameGuess + parentGuess + ". " +
+ "This will become an error in Chisel 3.6."
errors.deprecated(msg, None)
errors.checkpoint(logger)
_computeName(None).get
@@ -364,7 +394,8 @@ private[chisel3] class ChiselContext() {
private[chisel3] class DynamicContext(
val annotationSeq: AnnotationSeq,
val throwOnFirstError: Boolean,
- val warnReflectiveNaming: Boolean) {
+ val warnReflectiveNaming: Boolean,
+ val warningsAsErrors: Boolean) {
val importDefinitionAnnos = annotationSeq.collect { case a: ImportDefinitionAnnotation[_] => a }
// Map holding the actual names of extModules
@@ -421,8 +452,9 @@ private[chisel3] class DynamicContext(
var whenStack: List[WhenContext] = Nil
var currentClock: Option[Clock] = None
var currentReset: Option[Reset] = None
- val errors = new ErrorLog
+ val errors = new ErrorLog(warningsAsErrors)
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
}
@@ -439,6 +471,9 @@ private[chisel3] object Builder extends LazyLogging {
dynamicContextVar.value.get
}
+ // Used to suppress warnings when casting from a UInt to an Enum
+ var suppressEnumCastWarning: Boolean = false
+
// Returns the current dynamic context
def captureContext(): DynamicContext = dynamicContext
// Sets the current dynamic contents
@@ -496,6 +531,7 @@ private[chisel3] object Builder extends LazyLogging {
def buildAggName(id: HasId): Option[String] = {
def getSubName(field: Data): Option[String] = field.getOptionRef.flatMap {
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 Index(_, ULit(n, _)) => Some(n.toString) // Vec lit indexing
case Index(_, _: Node) => None // Vec dynamic indexing
diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala
index 3b0846eb..da968d2a 100644
--- a/core/src/main/scala/chisel3/internal/Error.scala
+++ b/core/src/main/scala/chisel3/internal/Error.scala
@@ -176,21 +176,35 @@ private[chisel3] object ErrorLog {
val errTag = s"[${Console.RED}error${Console.RESET}]"
}
-private[chisel3] class ErrorLog {
+private[chisel3] class ErrorLog(warningsAsErrors: Boolean) {
+ def getLoc(loc: Option[StackTraceElement]): String = {
+ loc match {
+ case Some(elt: StackTraceElement) => s"${elt.getFileName}:${elt.getLineNumber}"
+ case None => "(unknown)"
+ }
+ }
/** Log an error message */
- def error(m: => String): Unit =
- errors += new Error(m, getUserLineNumber)
+ def error(m: => String): Unit = {
+ val loc = getUserLineNumber
+ errors += (((m, getLoc(loc)), new Error(m, loc)))
+ }
- /** Log a warning message */
- def warning(m: => String): Unit =
- errors += new Warning(m, getUserLineNumber)
+ private def warn(m: => String, loc: Option[StackTraceElement]): LogEntry =
+ if (warningsAsErrors) new Error(m, loc) else new Warning(m, loc)
- /** Log a warning message without a source locator */
- def warningNoLoc(m: => String): Unit = {
- errors += new Warning(m, None)
+ /** Log a warning message */
+ def warning(m: => String): Unit = {
+ val loc = getUserLineNumber
+ errors += (((m, getLoc(loc)), warn(m, loc)))
}
+ /** Log a warning message without a source locator. This is used when the
+ * locator wouldn't be helpful (e.g., due to lazy values).
+ */
+ def warningNoLoc(m: => String): Unit =
+ errors += (((m, ""), warn(m, None)))
+
/** Emit an informational message */
@deprecated("This method will be removed in 3.5", "3.4")
def info(m: String): Unit =
@@ -200,11 +214,7 @@ private[chisel3] class ErrorLog {
def deprecated(m: => String, location: Option[String]): Unit = {
val sourceLoc = location match {
case Some(loc) => loc
- case None =>
- getUserLineNumber match {
- case Some(elt: StackTraceElement) => s"${elt.getFileName}:${elt.getLineNumber}"
- case None => "(unknown)"
- }
+ case None => getLoc(getUserLineNumber)
}
val thisEntry = (m, sourceLoc)
@@ -217,7 +227,7 @@ private[chisel3] class ErrorLog {
case ((message, sourceLoc), count) =>
logger.warn(s"${ErrorLog.depTag} $sourceLoc ($count calls): $message")
}
- errors.foreach(e => logger.error(e.toString))
+ errors.foreach(e => logger.error(e._2.toString))
if (!deprecations.isEmpty) {
logger.warn(
@@ -233,8 +243,8 @@ private[chisel3] class ErrorLog {
logger.warn(s"""${ErrorLog.warnTag} scalacOptions := Seq("-unchecked", "-deprecation")""")
}
- val allErrors = errors.filter(_.isFatal)
- val allWarnings = errors.filter(!_.isFatal)
+ val allErrors = errors.filter(_._2.isFatal)
+ val allWarnings = errors.filter(!_._2.isFatal)
if (!allWarnings.isEmpty && !allErrors.isEmpty) {
logger.warn(
@@ -289,7 +299,7 @@ private[chisel3] class ErrorLog {
.headOption
}
- private val errors = ArrayBuffer[LogEntry]()
+ private val errors = LinkedHashMap[(String, String), LogEntry]()
private val deprecations = LinkedHashMap[(String, String), Int]()
private val startTime = System.currentTimeMillis
diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala
index 31364804..4e762a7c 100644
--- a/core/src/main/scala/chisel3/internal/MonoConnect.scala
+++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala
@@ -322,21 +322,35 @@ private[chisel3] object MonoConnect {
else false
}
- /** Trace flow from child Data to its parent. */
- @tailrec private[chisel3] def traceFlow(currentlyFlipped: Boolean, data: Data, context_mod: RawModule): Boolean = {
- import SpecifiedDirection.{Input => SInput, Flip => SFlip}
+ /** Trace flow from child Data to its parent.
+ *
+ * Returns true if, given the context,
+ * this signal can be a sink when wantsToBeSink = true,
+ * or if it can be a source when wantsToBeSink = false.
+ * Always returns true if the Data does not actually correspond
+ * to a Port.
+ */
+ @tailrec private[chisel3] def traceFlow(
+ wantToBeSink: Boolean,
+ currentlyFlipped: Boolean,
+ data: Data,
+ context_mod: RawModule
+ ): Boolean = {
val sdir = data.specifiedDirection
- val flipped = sdir == SInput || sdir == SFlip
+ val coercedFlip = sdir == SpecifiedDirection.Input
+ val coercedAlign = sdir == SpecifiedDirection.Output
+ val flipped = sdir == SpecifiedDirection.Flip
+ val traceFlipped = ((flipped ^ currentlyFlipped) || coercedFlip) && (!coercedAlign)
data.binding.get match {
- case ChildBinding(parent) => traceFlow(flipped ^ currentlyFlipped, parent, context_mod)
+ case ChildBinding(parent) => traceFlow(wantToBeSink, traceFlipped, parent, context_mod)
case PortBinding(enclosure) =>
val childPort = enclosure != context_mod
- childPort ^ flipped ^ currentlyFlipped
+ wantToBeSink ^ childPort ^ traceFlipped
case _ => true
}
}
- def canBeSink(data: Data, context_mod: RawModule): Boolean = traceFlow(true, data, context_mod)
- def canBeSource(data: Data, context_mod: RawModule): Boolean = traceFlow(false, data, context_mod)
+ def canBeSink(data: Data, context_mod: RawModule): Boolean = traceFlow(true, false, data, context_mod)
+ def canBeSource(data: Data, context_mod: RawModule): Boolean = traceFlow(false, false, data, context_mod)
/** Check whether two aggregates can be bulk connected (<=) in FIRRTL. (MonoConnect case)
*
diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala
index 2d21a7cb..ca39608f 100644
--- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala
+++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala
@@ -8,7 +8,7 @@ import firrtl.{ir => fir}
import chisel3.internal.{castToInt, throwException, HasId}
import scala.annotation.{nowarn, tailrec}
-import scala.collection.immutable.Queue
+import scala.collection.immutable.{Queue, VectorBuilder}
import scala.collection.immutable.LazyList // Needed for 2.12 alias
@nowarn("msg=class Port") // delete when Port becomes private
@@ -68,6 +68,8 @@ private[chisel3] object Converter {
fir.Reference(name, fir.UnknownType)
case Slot(imm, name) =>
fir.SubField(convert(imm, ctx, info), name, fir.UnknownType)
+ case OpaqueSlot(imm) =>
+ convert(imm, ctx, info)
case Index(imm, ILit(idx)) =>
fir.SubIndex(convert(imm, ctx, info), castToInt(idx, "Index"), fir.UnknownType)
case Index(imm, value) =>
@@ -213,7 +215,7 @@ private[chisel3] object Converter {
* @param alt Indicates if currently processing commands in the alternate (else) of the when scope
*/
// TODO we should probably have a different structure in the IR to close elses
- private case class WhenFrame(when: fir.Conditionally, outer: Queue[fir.Statement], alt: Boolean)
+ private case class WhenFrame(when: fir.Conditionally, outer: VectorBuilder[fir.Statement], alt: Boolean)
/** Convert Chisel IR Commands into FIRRTL Statements
*
@@ -224,52 +226,72 @@ private[chisel3] object Converter {
* @return FIRRTL Statement that is equivalent to the input cmds
*/
def convert(cmds: Seq[Command], ctx: Component): fir.Statement = {
- @tailrec
- def rec(acc: Queue[fir.Statement], scope: List[WhenFrame])(cmds: Seq[Command]): Seq[fir.Statement] = {
- if (cmds.isEmpty) {
- assert(scope.isEmpty)
- acc
- } else
- convertSimpleCommand(cmds.head, ctx) match {
- // Most Commands map 1:1
- case Some(stmt) =>
- rec(acc :+ stmt, scope)(cmds.tail)
- // When scoping logic does not map 1:1 and requires pushing/popping WhenFrames
- // Please see WhenFrame for more details
- case None =>
- cmds.head match {
- case WhenBegin(info, pred) =>
- 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, _) =>
- val frame = scope.head
- val when =
- if (frame.alt) frame.when.copy(alt = fir.Block(acc))
- else frame.when.copy(conseq = fir.Block(acc))
- // Check if this when has an else
- cmds.tail.headOption match {
- case Some(AltBegin(_)) =>
- assert(!frame.alt, "Internal Error! Unexpected when structure!") // Only 1 else per when
- rec(Queue.empty, frame.copy(when = when, alt = true) +: scope.tail)(cmds.drop(2))
- case _ => // Not followed by otherwise
- // If depth > 0 then we need to close multiple When scopes so we add a new WhenEnd
- // If we're nested we need to add more WhenEnds to ensure each When scope gets
- // properly closed
- val cmdsx = if (depth > 0) WhenEnd(info, depth - 1, false) +: cmds.tail else cmds.tail
- rec(frame.outer :+ when, scope.tail)(cmdsx)
- }
- case OtherwiseEnd(info, depth) =>
- val frame = scope.head
- val when = frame.when.copy(alt = fir.Block(acc))
- // TODO For some reason depth == 1 indicates the last closing otherwise whereas
- // depth == 0 indicates last closing when
- val cmdsx = if (depth > 1) OtherwiseEnd(info, depth - 1) +: cmds.tail else cmds.tail
- rec(scope.head.outer :+ when, scope.tail)(cmdsx)
- }
- }
+ var stmts = new VectorBuilder[fir.Statement]()
+ var scope: List[WhenFrame] = Nil
+ var cmdsIt = cmds.iterator.buffered
+ // Extra var because sometimes we want to push a Command to the head of cmdsIt
+ // This is more efficient than changing the iterator
+ var nextCmd: Command = null
+ while (nextCmd != null || cmdsIt.hasNext) {
+ val cmd = if (nextCmd != null) {
+ val _nextCmd = nextCmd
+ nextCmd = null
+ _nextCmd
+ } else {
+ cmdsIt.next()
+ }
+ convertSimpleCommand(cmd, ctx) match {
+ // Most Commands map 1:1
+ case Some(stmt) =>
+ stmts += stmt
+ // When scoping logic does not map 1:1 and requires pushing/popping WhenFrames
+ // Please see WhenFrame for more details
+ case None =>
+ cmd match {
+ case WhenBegin(info, pred) =>
+ val when = fir.Conditionally(convert(info), convert(pred, ctx, info), fir.EmptyStmt, fir.EmptyStmt)
+ val frame = WhenFrame(when, stmts, false)
+ stmts = new VectorBuilder[fir.Statement]
+ scope = frame :: scope
+ case WhenEnd(info, depth, _) =>
+ val frame = scope.head
+ val when =
+ if (frame.alt) frame.when.copy(alt = fir.Block(stmts.result()))
+ else frame.when.copy(conseq = fir.Block(stmts.result()))
+ // Check if this when has an else
+ cmdsIt.headOption match {
+ case Some(AltBegin(_)) =>
+ assert(!frame.alt, "Internal Error! Unexpected when structure!") // Only 1 else per when
+ scope = frame.copy(when = when, alt = true) :: scope.tail
+ cmdsIt.next() // Consume the AltBegin
+ stmts = new VectorBuilder[fir.Statement]
+ case _ => // Not followed by otherwise
+ // If depth > 0 then we need to close multiple When scopes so we add a new WhenEnd
+ // If we're nested we need to add more WhenEnds to ensure each When scope gets
+ // properly closed
+ if (depth > 0) {
+ nextCmd = WhenEnd(info, depth - 1, false)
+ }
+ stmts = frame.outer
+ stmts += when
+ scope = scope.tail
+ }
+ case OtherwiseEnd(info, depth) =>
+ val frame = scope.head
+ val when = frame.when.copy(alt = fir.Block(stmts.result()))
+ // TODO For some reason depth == 1 indicates the last closing otherwise whereas
+ // depth == 0 indicates last closing when
+ if (depth > 1) {
+ nextCmd = OtherwiseEnd(info, depth - 1)
+ }
+ stmts = frame.outer
+ stmts += when
+ scope = scope.tail
+ }
+ }
}
- fir.Block(rec(Queue.empty, List.empty)(cmds))
+ assert(scope.isEmpty)
+ fir.Block(stmts.result())
}
def convert(width: Width): fir.Width = width match {
@@ -299,9 +321,12 @@ private[chisel3] object Converter {
case d: SInt => fir.SIntType(convert(d.width))
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, info), d.length)
- case d: Record =>
+ case d: Analog => fir.AnalogType(convert(d.width))
+ case d: Vec[_] =>
+ val childClearDir = clearDir ||
+ d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output
+ fir.VectorType(extractType(d.sample_element, childClearDir, 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 {
@@ -311,7 +336,11 @@ private[chisel3] object Converter {
case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) =>
fir.Field(getRef(elt, info).name, fir.Flip, extractType(elt, false, info))
}
- fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) })
+ if (!d._isOpaqueType)
+ fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) })
+ else
+ extractType(d.elements.head._2, childClearDir, info)
+ }
}
def convert(name: String, param: Param): fir.Param = param match {
@@ -338,7 +367,7 @@ private[chisel3] object Converter {
def convert(component: Component): fir.DefModule = component match {
case ctx @ DefModule(_, name, ports, cmds) =>
- fir.Module(fir.NoInfo, name, ports.map(p => convert(p)), convert(cmds.toList, ctx))
+ fir.Module(fir.NoInfo, name, ports.map(p => convert(p)), convert(cmds, ctx))
case ctx @ DefBlackBox(id, name, ports, topDir, params) =>
fir.ExtModule(
fir.NoInfo,
diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
index dc9ab027..ddad6b10 100644
--- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -91,6 +91,7 @@ object Arg {
case Some(Index(Node(imm), Node(value))) => s"${earlyLocalName(imm)}[${earlyLocalName(imm)}]"
case Some(Index(Node(imm), arg)) => s"${earlyLocalName(imm)}[${arg.localName}]"
case Some(Slot(Node(imm), name)) => s"${earlyLocalName(imm)}.$name"
+ case Some(OpaqueSlot(Node(imm))) => s"${earlyLocalName(imm)}"
case Some(arg) => arg.name
case None =>
id match {
@@ -218,6 +219,11 @@ case class Slot(imm: Node, name: String) extends Arg {
}
}
+case class OpaqueSlot(imm: Node) extends Arg {
+ override def contextualName(ctx: Component): String = imm.contextualName(ctx)
+ override def name: String = imm.name
+}
+
case class Index(imm: Arg, value: Arg) extends Arg {
def name: String = s"[$value]"
override def contextualName(ctx: Component): String = s"${imm.contextualName(ctx)}[${value.contextualName(ctx)}]"