summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/scala-2.12/scala/collection/immutable/package.scala16
-rw-r--r--core/src/main/scala/chisel3/Aggregate.scala481
-rw-r--r--core/src/main/scala/chisel3/Bits.scala43
-rw-r--r--core/src/main/scala/chisel3/BlackBox.scala13
-rw-r--r--core/src/main/scala/chisel3/Data.scala151
-rw-r--r--core/src/main/scala/chisel3/Element.scala11
-rw-r--r--core/src/main/scala/chisel3/Mem.scala12
-rw-r--r--core/src/main/scala/chisel3/Module.scala228
-rw-r--r--core/src/main/scala/chisel3/ModuleAspect.scala4
-rw-r--r--core/src/main/scala/chisel3/Num.scala8
-rw-r--r--core/src/main/scala/chisel3/Printf.scala22
-rw-r--r--core/src/main/scala/chisel3/RawModule.scala52
-rw-r--r--core/src/main/scala/chisel3/Reg.scala2
-rw-r--r--core/src/main/scala/chisel3/SeqUtils.scala10
-rw-r--r--core/src/main/scala/chisel3/StrongEnum.scala70
-rw-r--r--core/src/main/scala/chisel3/aop/Aspect.scala7
-rw-r--r--core/src/main/scala/chisel3/core/package.scala288
-rw-r--r--core/src/main/scala/chisel3/dontTouch.scala7
-rw-r--r--core/src/main/scala/chisel3/experimental/Analog.scala5
-rw-r--r--core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala72
-rw-r--r--core/src/main/scala/chisel3/experimental/dataview/DataView.scala154
-rw-r--r--core/src/main/scala/chisel3/experimental/dataview/package.scala251
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala100
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala111
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala17
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/IsLookupable.scala25
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala368
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/package.scala48
-rw-r--r--core/src/main/scala/chisel3/experimental/package.scala46
-rw-r--r--core/src/main/scala/chisel3/experimental/verification/package.scala47
-rw-r--r--core/src/main/scala/chisel3/internal/BiConnect.scala43
-rw-r--r--core/src/main/scala/chisel3/internal/Binding.scala22
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala133
-rw-r--r--core/src/main/scala/chisel3/internal/Error.scala113
-rw-r--r--core/src/main/scala/chisel3/internal/MonoConnect.scala8
-rw-r--r--core/src/main/scala/chisel3/internal/Namer.scala14
-rw-r--r--core/src/main/scala/chisel3/internal/firrtl/Converter.scala109
-rw-r--r--core/src/main/scala/chisel3/internal/firrtl/IR.scala83
-rw-r--r--core/src/main/scala/chisel3/package.scala3
39 files changed, 2554 insertions, 643 deletions
diff --git a/core/src/main/scala-2.12/scala/collection/immutable/package.scala b/core/src/main/scala-2.12/scala/collection/immutable/package.scala
new file mode 100644
index 00000000..c06935d0
--- /dev/null
+++ b/core/src/main/scala-2.12/scala/collection/immutable/package.scala
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package scala.collection
+
+import scala.collection.immutable.ListMap
+
+package object immutable {
+ val SeqMap = ListMap
+ type SeqMap[K, +V] = ListMap[K, V]
+
+ val VectorMap = ListMap
+ type VectorMap[K, +V] = ListMap[K, V]
+
+ val LazyList = Stream
+ type LazyList[+A] = Stream[A]
+}
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala
index 403fcdba..17e46cb3 100644
--- a/core/src/main/scala/chisel3/Aggregate.scala
+++ b/core/src/main/scala/chisel3/Aggregate.scala
@@ -2,25 +2,28 @@
package chisel3
-import scala.collection.immutable.ListMap
+import chisel3.experimental.VecLiterals.AddVecLiteralConstructor
+import chisel3.experimental.dataview.{InvalidViewException, isView}
+
+import scala.collection.immutable.{SeqMap, VectorMap}
import scala.collection.mutable.{HashSet, LinkedHashMap}
import scala.language.experimental.macros
-
-import chisel3.experimental.BaseModule
-import chisel3.experimental.BundleLiteralException
-import chisel3.experimental.EnumType
+import chisel3.experimental.{BaseModule, BundleLiteralException, ChiselEnum, EnumType, VecLiteralException}
import chisel3.internal._
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo._
+import scala.collection.mutable
+
class AliasedAggregateFieldException(message: String) extends ChiselException(message)
/** An abstract class for data types that solely consist of (are an aggregate
* of) other Data objects.
*/
sealed abstract class Aggregate extends Data {
- private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) {
+ private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = {
+ _parent.foreach(_.addId(this))
binding = target
val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
@@ -50,16 +53,19 @@ sealed abstract class Aggregate extends Data {
*/
override def litOption: Option[BigInt] = {
// Shift the accumulated value by our width and add in our component, masked by our width.
- def shiftAdd(accumulator: Option[BigInt], elt: Data): Option[BigInt] = (accumulator, elt.litOption()) match {
- case (Some(accumulator), Some(eltLit)) =>
- val width = elt.width.get
- val masked = ((BigInt(1) << width) - 1) & eltLit // also handles the negative case with two's complement
- Some((accumulator << width) + masked)
- case (None, _) => None
- case (_, None) => None
+ def shiftAdd(accumulator: Option[BigInt], elt: Data): Option[BigInt] = {
+ (accumulator, elt.litOption()) match {
+ case (Some(accumulator), Some(eltLit)) =>
+ val width = elt.width.get
+ val masked = ((BigInt(1) << width) - 1) & eltLit // also handles the negative case with two's complement
+ Some((accumulator << width) + masked)
+ case (None, _) => None
+ case (_, None) => None
+ }
}
+
topBindingOpt match {
- case Some(BundleLitBinding(_)) =>
+ case Some(BundleLitBinding(_)) | Some(VecLitBinding(_)) =>
getElements
.reverse
.foldLeft[Option[BigInt]](Some(BigInt(0)))(shiftAdd)
@@ -72,6 +78,7 @@ sealed abstract class Aggregate extends Data {
def getElements: Seq[Data]
private[chisel3] def width: Width = getElements.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,
// otherwise, issue a Connect.
@@ -85,8 +92,9 @@ sealed abstract class Aggregate extends Data {
override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = {
SeqUtils.do_asUInt(flatten.map(_.asUInt()))
}
+
private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo,
- compileOptions: CompileOptions): Unit = {
+ compileOptions: CompileOptions): Unit = {
var i = 0
val bits = if (that.isLit) that else WireDefault(UInt(this.width), that) // handles width padding
for (x <- flatten) {
@@ -154,9 +162,18 @@ trait VecFactory extends SourceInfoDoc {
*/
sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int)
extends Aggregate with VecLike[T] {
+
override def toString: String = {
+ val bindingString = topBindingOpt match {
+ case Some(VecLitBinding(vecLitBinding)) =>
+ val contents = vecLitBinding.zipWithIndex.map { case ((data, lit), index) =>
+ s"$index=$lit"
+ }.mkString(", ")
+ s"($contents)"
+ case _ => bindingToString
+ }
val elementType = sample_element.cloneType
- s"$elementType[$length]$bindingToString"
+ s"$elementType[$length]$bindingString"
}
private[chisel3] override def typeEquivalent(that: Data): Boolean = that match {
@@ -166,7 +183,8 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int)
case _ => false
}
- private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) {
+ private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = {
+ _parent.foreach(_.addId(this))
binding = target
val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
@@ -242,6 +260,9 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int)
def do_apply(p: UInt)(implicit compileOptions: CompileOptions): T = {
requireIsHardware(this, "vec")
requireIsHardware(p, "vec index")
+ if (isView(this)) {
+ throw InvalidViewException("Dynamic indexing of Views is not yet supported")
+ }
val port = gen
// Reconstruct the resolvedDirection (in Aggregate.bind), since it's not stored.
@@ -316,9 +337,172 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int)
}
curLayer(0)
}
+
+ /** Creates a Vec literal of this type with specified values. this must be a chisel type.
+ *
+ * @param elementInitializers literal values, specified as a pair of the Vec field to the literal value.
+ * The Vec field is specified as a function from an object of this type to the field.
+ * Fields that aren't initialized to DontCare, and assignment to a wire will overwrite any
+ * existing value with DontCare.
+ * @return a Vec literal of this type with subelement values specified
+ *
+ * Vec(2, UInt(8.W)).Lit(
+ * 1 -> 0x0A.U,
+ * 2 -> 0x0B.U
+ * )
+ * }}}
+ */
+ private[chisel3] def _makeLit(elementInitializers: (Int, T)*)(implicit sourceInfo: SourceInfo,
+ compileOptions: CompileOptions): this.type = {
+
+ def checkLiteralConstruction(): Unit = {
+ val dupKeys = elementInitializers.map { x => x._1 }.groupBy(x => x).flatMap { case (k, v) =>
+ if (v.length > 1) {
+ Some(k, v.length)
+ } else {
+ None
+ }
+ }
+ if (dupKeys.nonEmpty) {
+ throw new VecLiteralException(
+ s"VecLiteral: has duplicated indices ${dupKeys.map { case (k, n) => s"$k($n times)" }.mkString(",")}"
+ )
+ }
+
+ val outOfRangeIndices = elementInitializers.map(_._1).filter { case index => index < 0 || index >= length }
+ if (outOfRangeIndices.nonEmpty) {
+ throw new VecLiteralException(
+ s"VecLiteral: The following indices (${outOfRangeIndices.mkString(",")}) " +
+ s"are less than zero or greater or equal to than Vec length"
+ )
+ }
+ cloneSupertype(elementInitializers.map(_._2), s"Vec.Lit(...)")
+
+ // look for literals of this vec that are wider than the vec's type
+ val badLits = elementInitializers.flatMap {
+ case (index, lit) =>
+ (sample_element.width, lit.width) match {
+ case (KnownWidth(m), KnownWidth(n)) =>
+ if (m < n) Some(index -> lit) else None
+ case (KnownWidth(_), _) =>
+ None
+ case (UnknownWidth(), _) =>
+ None
+ case _ =>
+ Some(index -> lit)
+ }
+ case _ => None
+ }
+ if (badLits.nonEmpty) {
+ throw new VecLiteralException(
+ s"VecLiteral: Vec[$gen] has the following incorrectly typed or sized initializers: " +
+ badLits.map { case (a, b) => s"$a -> $b" }.mkString(",")
+ )
+ }
+
+ }
+
+ requireIsChiselType(this, "vec literal constructor model")
+ checkLiteralConstruction()
+
+ val clone = cloneType
+ val cloneFields = getRecursiveFields(clone, "(vec root)").toMap
+
+ // Create the Vec literal binding from litArgs of arguments
+ val vecLitLinkedMap = new mutable.LinkedHashMap[Data, LitArg]()
+ elementInitializers.sortBy { case (a, _) => a }.foreach { case (fieldIndex, value) =>
+ val field = clone.apply(fieldIndex)
+ val fieldName = cloneFields.getOrElse(field,
+ throw new VecLiteralException(s"field $field (with value $value) is not a field," +
+ s" ensure the field is specified as a function returning a field on an object of class ${this.getClass}," +
+ s" eg '_.a' to select hypothetical bundle field 'a'")
+ )
+
+ val valueBinding = value.topBindingOpt match {
+ case Some(litBinding: LitBinding) => litBinding
+ case _ => throw new VecLiteralException(s"field $fieldIndex specified with non-literal value $value")
+ }
+
+ field match { // Get the litArg(s) for this field
+ case bitField: Bits =>
+ if (!field.typeEquivalent(bitField)) {
+ throw new VecLiteralException(
+ s"VecLit: Literal specified at index $fieldIndex ($value) does not match Vec type $sample_element"
+ )
+ }
+ if (bitField.getWidth > field.getWidth) {
+ throw new VecLiteralException(
+ s"VecLit: Literal specified at index $fieldIndex ($value) is too wide for Vec type $sample_element"
+ )
+ }
+ val litArg = valueBinding match {
+ case ElementLitBinding(litArg) => litArg
+ case BundleLitBinding(litMap) => litMap.getOrElse(value,
+ throw new BundleLiteralException(s"Field $fieldName specified with unspecified value")
+ )
+ case VecLitBinding(litMap) => litMap.getOrElse(value,
+ throw new VecLiteralException(s"Field $fieldIndex specified with unspecified value"))
+ }
+ val adjustedLitArg = litArg.cloneWithWidth(sample_element.width)
+ vecLitLinkedMap(bitField) = adjustedLitArg
+
+ case recordField: Record =>
+ if (!(recordField.typeEquivalent(value))) {
+ throw new VecLiteralException(s"field $fieldIndex $recordField specified with non-type-equivalent value $value")
+ }
+ // Copy the source BundleLitBinding with fields (keys) remapped to the clone
+ val remap = getMatchedFields(value, recordField).toMap
+ valueBinding.asInstanceOf[BundleLitBinding].litMap.map { case (valueField, valueValue) =>
+ vecLitLinkedMap(remap(valueField)) = valueValue
+ }
+
+ case vecField: Vec[_] =>
+ if (!(vecField typeEquivalent value)) {
+ throw new VecLiteralException(s"field $fieldIndex $vecField specified with non-type-equivalent value $value")
+ }
+ // Copy the source VecLitBinding with vecFields (keys) remapped to the clone
+ val remap = getMatchedFields(value, vecField).toMap
+ value.topBinding.asInstanceOf[VecLitBinding].litMap.map { case (valueField, valueValue) =>
+ vecLitLinkedMap(remap(valueField)) = valueValue
+ }
+
+ case enumField: EnumType => {
+ if (!(enumField typeEquivalent value)) {
+ throw new VecLiteralException(s"field $fieldIndex $enumField specified with non-type-equivalent enum value $value")
+ }
+ val litArg = valueBinding match {
+ case ElementLitBinding(litArg) => litArg
+ case _ =>
+ throw new VecLiteralException(s"field $fieldIndex $enumField could not bematched with $valueBinding")
+ }
+ vecLitLinkedMap(field) = litArg
+ }
+
+ case _ => throw new VecLiteralException(s"unsupported field $fieldIndex of type $field")
+ }
+ }
+
+ clone.bind(VecLitBinding(VectorMap(vecLitLinkedMap.toSeq:_*)))
+ clone
+ }
}
object VecInit extends SourceInfoDoc {
+
+ /** Gets the correct connect operation (directed hardware assign or bulk connect) for element in Vec.
+ */
+ private def getConnectOpFromDirectionality[T <: Data](proto: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): (T, T) => Unit = proto.direction match {
+ case ActualDirection.Input | ActualDirection.Output | ActualDirection.Unspecified =>
+ // When internal wires are involved, driver / sink must be specified explicitly, otherwise
+ // the system is unable to infer which is driver / sink
+ (x, y) => x := y
+ case ActualDirection.Bidirectional(_) =>
+ // For bidirectional, must issue a bulk connect so subelements are resolved correctly.
+ // Bulk connecting two wires may not succeed because Chisel frontend does not infer
+ // directions.
+ (x, y) => x <> y
+ }
+
/** Creates a new [[Vec]] composed of elements of the input Seq of [[Data]]
* nodes.
*
@@ -340,22 +524,14 @@ object VecInit extends SourceInfoDoc {
// DummyImplicit or additional type parameter will break some code.
// Check that types are homogeneous. Width mismatch for Elements is safe.
- require(!elts.isEmpty)
+ require(elts.nonEmpty, "Vec hardware values are not allowed to be empty")
elts.foreach(requireIsHardware(_, "vec element"))
val vec = Wire(Vec(elts.length, cloneSupertype(elts, "Vec")))
-
- // TODO: try to remove the logic for this mess
- elts.head.direction match {
- case ActualDirection.Input | ActualDirection.Output | ActualDirection.Unspecified =>
- // When internal wires are involved, driver / sink must be specified explicitly, otherwise
- // the system is unable to infer which is driver / sink
- (vec zip elts).foreach(x => x._1 := x._2)
- case ActualDirection.Bidirectional(_) =>
- // For bidirectional, must issue a bulk connect so subelements are resolved correctly.
- // Bulk connecting two wires may not succeed because Chisel frontend does not infer
- // directions.
- (vec zip elts).foreach(x => x._1 <> x._2)
+ val op = getConnectOpFromDirectionality(vec.head)
+
+ (vec zip elts).foreach{ x =>
+ op(x._1, x._2)
}
vec
}
@@ -387,12 +563,137 @@ object VecInit extends SourceInfoDoc {
/** @group SourceInfoTransformMacro */
def do_tabulate[T <: Data](n: Int)(gen: (Int) => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] =
apply((0 until n).map(i => gen(i)))
+
+ /** Creates a new 2D [[Vec]] of length `n by m` composed of the results of the given
+ * function applied over a range of integer values starting from 0.
+ *
+ * @param n number of 1D vectors inside outer vector
+ * @param m number of elements in each 1D vector (the function is applied from
+ * 0 to `n-1`)
+ * @param gen function that takes in an Int (the index) and returns a
+ * [[Data]] that becomes the output element
+ */
+ def tabulate[T <: Data](n: Int, m: Int)(gen: (Int, Int) => T): Vec[Vec[T]] = macro VecTransform.tabulate2D
+
+ /** @group SourceInfoTransformMacro */
+ def do_tabulate[T <: Data](n: Int, m: Int)(gen: (Int, Int) => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[Vec[T]] = {
+ // TODO make this lazy (requires LazyList and cross compilation, beyond the scope of this PR)
+ val elts = Seq.tabulate(n, m)(gen)
+ val flatElts = elts.flatten
+
+ require(flatElts.nonEmpty, "Vec hardware values are not allowed to be empty")
+ flatElts.foreach(requireIsHardware(_, "vec element"))
+
+ val tpe = cloneSupertype(flatElts, "Vec.tabulate")
+ val myVec = Wire(Vec(n, Vec(m, tpe)))
+ val op = getConnectOpFromDirectionality(myVec.head.head)
+ for (
+ (xs1D, ys1D) <- myVec zip elts;
+ (x, y) <- xs1D zip ys1D
+ ) {
+ op(x, y)
+ }
+ myVec
+ }
+
+ /** Creates a new 3D [[Vec]] of length `n by m by p` composed of the results of the given
+ * function applied over a range of integer values starting from 0.
+ *
+ * @param n number of 2D vectors inside outer vector
+ * @param m number of 1D vectors in each 2D vector
+ * @param p number of elements in each 1D vector
+ * @param gen function that takes in an Int (the index) and returns a
+ * [[Data]] that becomes the output element
+ */
+ def tabulate[T <: Data](n: Int, m: Int, p: Int)(gen: (Int, Int, Int) => T): Vec[Vec[Vec[T]]] = macro VecTransform.tabulate3D
+
+ /** @group SourceInfoTransformMacro */
+ def do_tabulate[T <: Data](n: Int, m: Int, p: Int)(gen: (Int, Int, Int) => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[Vec[Vec[T]]] = {
+ // TODO make this lazy (requires LazyList and cross compilation, beyond the scope of this PR)
+ val elts = Seq.tabulate(n, m, p)(gen)
+ val flatElts = elts.flatten.flatten
+
+ require(flatElts.nonEmpty, "Vec hardware values are not allowed to be empty")
+ flatElts.foreach(requireIsHardware(_, "vec element"))
+
+ val tpe = cloneSupertype(flatElts, "Vec.tabulate")
+ val myVec = Wire(Vec(n, Vec(m, Vec(p, tpe))))
+ val op = getConnectOpFromDirectionality(myVec.head.head.head)
+
+ for (
+ (xs2D, ys2D) <- myVec zip elts;
+ (xs1D, ys1D) <- xs2D zip ys2D;
+ (x, y) <- xs1D zip ys1D
+ ) {
+ op(x, y)
+ }
+
+ myVec
+ }
+
+ /** Creates a new [[Vec]] of length `n` composed of the result of the given
+ * function applied to an element of data type T.
+ *
+ * @param n number of elements in the vector
+ * @param gen function that takes in an element T and returns an output
+ * element of the same type
+ */
+ def fill[T <: Data](n: Int)(gen: => T): Vec[T] = macro VecTransform.fill
+
+ /** @group SourceInfoTransformMacro */
+ def do_fill[T <: Data](n: Int)(gen: => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] =
+ apply(Seq.fill(n)(gen))
+
+ /** Creates a new 2D [[Vec]] of length `n by m` composed of the result of the given
+ * function applied to an element of data type T.
+ *
+ * @param n number of inner vectors (rows) in the outer vector
+ * @param m number of elements in each inner vector (column)
+ * @param gen function that takes in an element T and returns an output
+ * element of the same type
+ */
+ def fill[T <: Data](n: Int, m: Int)(gen: => T): Vec[Vec[T]] = macro VecTransform.fill2D
+
+ /** @group SourceInfoTransformMacro */
+ def do_fill[T <: Data](n: Int, m: Int)(gen: => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[Vec[T]] = {
+ do_tabulate(n, m)((_, _) => gen)
+ }
+
+ /** Creates a new 3D [[Vec]] of length `n by m by p` composed of the result of the given
+ * function applied to an element of data type T.
+ *
+ * @param n number of 2D vectors inside outer vector
+ * @param m number of 1D vectors in each 2D vector
+ * @param p number of elements in each 1D vector
+ * @param gen function that takes in an element T and returns an output
+ * element of the same type
+ */
+ def fill[T <: Data](n: Int, m: Int, p: Int)(gen: => T): Vec[Vec[Vec[T]]] = macro VecTransform.fill3D
+
+ /** @group SourceInfoTransformMacro */
+ def do_fill[T <: Data](n: Int, m: Int, p: Int)(gen: => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[Vec[Vec[T]]] = {
+ do_tabulate(n, m, p)((_, _, _) => gen)
+ }
+
+ /** Creates a new [[Vec]] of length `n` composed of the result of the given
+ * function applied to an element of data type T.
+ *
+ * @param start First element in the Vec
+ * @param len Lenth of elements in the Vec
+ * @param f Function that applies the element T from previous index and returns the output
+ * element to the next index
+ */
+ def iterate[T <: Data](start: T, len: Int)(f: (T) => T): Vec[T] = macro VecTransform.iterate
+
+ /** @group SourceInfoTransformMacro */
+ def do_iterate[T <: Data](start: T, len: Int)(f: (T) => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] =
+ apply(Seq.iterate(start, len)(f))
}
/** A trait for [[Vec]]s containing common hardware generators for collection
* operations.
*/
-trait VecLike[T <: Data] extends collection.IndexedSeq[T] with HasId with SourceInfoDoc {
+trait VecLike[T <: Data] extends IndexedSeq[T] with HasId with SourceInfoDoc {
def apply(p: UInt): T = macro CompileOptionsTransform.pArg
/** @group SourceInfoTransformMacro */
@@ -519,32 +820,13 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
* val b = Bool()
* }
*
- * (mew MyBundle).Lit(
+ * (new MyBundle).Lit(
* _.a -> 42.U,
* _.b -> true.B
* )
* }}}
*/
private[chisel3] def _makeLit(elems: (this.type => (Data, Data))*): this.type = {
- // Returns pairs of all fields, element-level and containers, in a Record and their path names
- def getRecursiveFields(data: Data, path: String): Seq[(Data, String)] = data match {
- case data: Record => data.elements.map { case (fieldName, fieldData) =>
- getRecursiveFields(fieldData, s"$path.$fieldName")
- }.fold(Seq(data -> path)) { _ ++ _ }
- case data => Seq(data -> path) // we don't support or recurse into other Aggregate types here
- }
-
- // Returns pairs of corresponding fields between two Records of the same type
- def getMatchedFields(x: Data, y: Data): Seq[(Data, Data)] = (x, y) match {
- case (x: Element, y: Element) =>
- require(x typeEquivalent y)
- Seq(x -> y)
- case (x: Record, y: Record) =>
- (x.elements zip y.elements).map { case ((xName, xElt), (yName, yElt)) =>
- require(xName == yName) // assume fields returned in same, deterministic order
- getMatchedFields(xElt, yElt)
- }.fold(Seq(x -> y)) { _ ++ _ }
- }
requireIsChiselType(this, "bundle literal constructor model")
val clone = cloneType
@@ -570,9 +852,15 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
val litArg = valueBinding match {
case ElementLitBinding(litArg) => litArg
case BundleLitBinding(litMap) => litMap.getOrElse(value,
- throw new BundleLiteralException(s"Field $fieldName specified with unspecified value"))
+ throw new BundleLiteralException(s"Field $fieldName specified with unspecified value")
+ )
+ case VecLitBinding(litMap) => litMap.getOrElse(value,
+ throw new VecLiteralException(s"Vec literal $fieldName specified with out literal values")
+ )
+
}
Seq(field -> litArg)
+
case field: Record =>
if (!(field typeEquivalent value)) {
throw new BundleLiteralException(s"field $fieldName $field specified with non-type-equivalent value $value")
@@ -582,18 +870,33 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
value.topBinding.asInstanceOf[BundleLitBinding].litMap.map { case (valueField, valueValue) =>
remap(valueField) -> valueValue
}
+
+ case vecField: Vec[_] =>
+ if (!(vecField typeEquivalent value)) {
+ throw new BundleLiteralException(s"field $fieldName $vecField specified with non-type-equivalent value $value")
+ }
+ // Copy the source BundleLitBinding with fields (keys) remapped to the clone
+ val remap = getMatchedFields(value, vecField).toMap
+ value.topBinding.asInstanceOf[VecLitBinding].litMap.map { case (valueField, valueValue) =>
+ remap(valueField) -> valueValue
+ }
+
case field: EnumType => {
if (!(field typeEquivalent value)) {
throw new BundleLiteralException(s"field $fieldName $field specified with non-type-equivalent enum value $value")
}
val litArg = valueBinding match {
case ElementLitBinding(litArg) => litArg
+ case _ =>
+ throw new BundleLiteralException(s"field $fieldName $field could not be matched with $valueBinding")
}
Seq(field -> litArg)
}
case _ => throw new BundleLiteralException(s"unsupported field $fieldName of type $field")
}
- } // don't convert to a Map yet to preserve duplicate keys
+ }
+
+ // don't convert to a Map yet to preserve duplicate keys
val duplicates = bundleLitMap.map(_._1).groupBy(identity).collect { case (x, elts) if elts.size > 1 => x }
if (!duplicates.isEmpty) {
val duplicateNames = duplicates.map(cloneFields(_)).mkString(", ")
@@ -630,7 +933,7 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
s"$className$bindingString"
}
- val elements: ListMap[String, Data]
+ def elements: SeqMap[String, Data]
/** Name for Pretty Printing */
def className: String = this.getClass.getSimpleName
@@ -692,6 +995,7 @@ class AutoClonetypeException(message: String) extends ChiselException(message)
package experimental {
class BundleLiteralException(message: String) extends ChiselException(message)
+ class VecLiteralException(message: String) extends ChiselException(message)
}
@@ -752,11 +1056,13 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
* assert(uint === "h12345678".U) // This will pass
* }}}
*/
- final lazy val elements: ListMap[String, Data] = {
+ final lazy val elements: SeqMap[String, Data] = {
val nameMap = LinkedHashMap[String, Data]()
for (m <- getPublicFields(classOf[Bundle])) {
getBundleField(m) match {
case Some(d: Data) =>
+ requireIsChiselType(d)
+
if (nameMap contains m.getName) {
require(nameMap(m.getName) eq d)
} else {
@@ -779,7 +1085,7 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
}
}
}
- ListMap(nameMap.toSeq sortWith { case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn)) }: _*)
+ VectorMap(nameMap.toSeq sortWith { case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn)) }: _*)
}
/**
@@ -796,17 +1102,50 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
case _ => None
}
+ /** Indicates if a concrete Bundle class was compiled using the compiler plugin
+ *
+ * Used for optimizing Chisel's performance and testing Chisel itself
+ * @note This should not be used in user code!
+ */
+ protected def _usingPlugin: Boolean = false
+
// Memoize the outer instance for autoclonetype, especially where this is context-dependent
// (like the outer module or enclosing Bundles).
private var _outerInst: Option[Object] = None
- // For autoclonetype, record possible candidates for outer instance.
+ // For reflective autoclonetype, record possible candidates for outer instance.
// _outerInst should always take precedence, since it should be propagated from the original
// object which has the most accurate context.
- private val _containingModule: Option[BaseModule] = Builder.currentModule
- private val _containingBundles: Seq[Bundle] = Builder.updateBundleStack(this)
+ private val _containingModule: Option[BaseModule] = if (_usingPlugin) None else Builder.currentModule
+ private val _containingBundles: Seq[Bundle] = if (_usingPlugin) Nil else Builder.updateBundleStack(this)
+
+ private def checkClone(clone: Bundle): Unit = {
+ for ((name, field) <- elements) {
+ if (clone.elements(name) eq field) {
+ throw new AutoClonetypeException(
+ s"Automatically cloned $clone has field '$name' aliased with base $this." +
+ " In the future, this will be solved automatically by the compiler plugin." +
+ " For now, ensure Chisel types used in the Bundle definition are passed through constructor arguments," +
+ " or wrapped in Input(...), Output(...), or Flipped(...) if appropriate." +
+ " As a last resort, you can override cloneType manually."
+ )
+ }
+ }
+
+ }
+
+ override def cloneType: this.type = {
+ val clone = _cloneTypeImpl.asInstanceOf[this.type]
+ checkClone(clone)
+ clone
+ }
- override def cloneType : this.type = {
+ /** Implementation of cloneType using runtime reflection. This should _never_ be overridden or called in user-code
+ *
+ * @note This is overridden by the compiler plugin (it is never called when using the plugin)
+ */
+ protected def _cloneTypeImpl: Bundle = {
+ assert(Builder.allowReflectiveAutoCloneType, "reflective autoclonetype is disallowed, this should only happen in testing")
// This attempts to infer constructor and arguments to clone this Bundle subtype without
// requiring the user explicitly overriding cloneType.
import scala.language.existentials
@@ -814,24 +1153,19 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
val clazz = this.getClass
- def autoClonetypeError(desc: String): Nothing = {
- throw new AutoClonetypeException(s"Unable to automatically infer cloneType on $clazz: $desc")
- }
+ def autoClonetypeError(desc: String): Nothing =
+ throw new AutoClonetypeException(
+ s"Unable to automatically infer cloneType on $clazz. " +
+ "cloneType is now implemented by the Chisel compiler plugin so please ensure you are using it in your build. " +
+ "If you cannot use the compiler plugin or you are using it and you still see this message, please file an issue and let us know. " +
+ s"For those not using the plugin, here is the 'runtime reflection' cloneType error message: $desc"
+ )
def validateClone(clone: Bundle, equivDiagnostic: String): Unit = {
if (!clone.typeEquivalent(this)) {
autoClonetypeError(s"Automatically cloned $clone not type-equivalent to base $this. " + equivDiagnostic)
}
-
- for ((name, field) <- elements) {
- if (clone.elements(name) eq field) {
- autoClonetypeError(s"Automatically cloned $clone has field $name aliased with base $this." +
- " In the future, this can be solved by wrapping the field in Field(...)," +
- " see https://github.com/freechipsproject/chisel3/pull/909." +
- " For now, ensure Chisel types used in the Bundle definition are passed through constructor arguments," +
- " or wrapped in Input(...), Output(...), or Flipped(...) if appropriate.")
- }
- }
+ checkClone(clone)
}
val mirror = runtimeMirror(clazz.getClassLoader)
@@ -1032,3 +1366,4 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
*/
override def toPrintable: Printable = toPrintableHelper(elements.toList.reverse)
}
+
diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala
index 6bd5a07c..670f6e7a 100644
--- a/core/src/main/scala/chisel3/Bits.scala
+++ b/core/src/main/scala/chisel3/Bits.scala
@@ -29,15 +29,6 @@ private[chisel3] sealed trait ToBoolable extends Element {
/** @group SourceInfoTransformMacro */
def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool
-
- /** Casts this $coll to a [[Bool]]
- *
- * @note The width must be known and equal to 1
- */
- final def toBool(): Bool = macro SourceInfoWhiteboxTransform.noArg
-
- /** @group SourceInfoTransformMacro */
- def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool
}
/** A data type for values represented by a single bitvector. This provides basic bitwise operations.
@@ -315,11 +306,6 @@ sealed abstract class Bits(private[chisel3] val width: Width) extends Element wi
/** Returns the contents of this wire as a [[scala.collection.Seq]] of [[Bool]]. */
final def toBools(): Seq[Bool] = macro SourceInfoTransform.noArg
- /** @group SourceInfoTransformMacro */
- @chiselRuntimeDeprecated
- @deprecated("Use asBools instead", "3.2")
- def do_toBools(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Seq[Bool] = do_asBools
-
/** Returns the contents of this wire as a [[scala.collection.Seq]] of [[Bool]]. */
final def asBools(): Seq[Bool] = macro SourceInfoTransform.noArg
@@ -369,10 +355,6 @@ sealed abstract class Bits(private[chisel3] val width: Width) extends Element wi
}
}
- @chiselRuntimeDeprecated
- @deprecated("Use asBool instead", "3.2")
- final def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool
-
/** Concatenation operator
*
* @param that a hardware component
@@ -448,7 +430,7 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U
override def do_/ (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt =
binop(sourceInfo, UInt(this.width), DivideOp, that)
override def do_% (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt =
- binop(sourceInfo, UInt(this.width), RemOp, that)
+ binop(sourceInfo, UInt(this.width min that.width), RemOp, that)
override def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt =
binop(sourceInfo, UInt(this.width + that.width), TimesOp, that)
@@ -591,10 +573,6 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U
override def do_<= (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that)
override def do_>= (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that)
- @chiselRuntimeDeprecated
- @deprecated("Use '=/=', which avoids potential precedence problems", "3.0")
- final def != (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this =/= that
-
/** Dynamic not equals operator
*
* @param that a hardware $coll
@@ -762,9 +740,9 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S
override def do_* (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt =
binop(sourceInfo, SInt(this.width + that.width), TimesOp, that)
override def do_/ (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt =
- binop(sourceInfo, SInt(this.width), DivideOp, that)
+ binop(sourceInfo, SInt(this.width + 1), DivideOp, that)
override def do_% (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt =
- binop(sourceInfo, SInt(this.width), RemOp, that)
+ binop(sourceInfo, SInt(this.width min that.width), RemOp, that)
/** Multiplication operator
*
@@ -877,10 +855,6 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S
override def do_<= (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that)
override def do_>= (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that)
- @chiselRuntimeDeprecated
- @deprecated("Use '=/=', which avoids potential precedence problems", "3.0")
- final def != (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this =/= that
-
/** Dynamic not equals operator
*
* @param that a hardware $coll
@@ -987,11 +961,6 @@ final class ResetType(private[chisel3] val width: Width = Width(1)) extends Elem
private[chisel3] def typeEquivalent(that: Data): Boolean =
this.getClass == that.getClass
- override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match {
- case _: Reset | DontCare => super.connect(that)(sourceInfo, connectCompileOptions)
- case _ => super.badConnect(that)(sourceInfo)
- }
-
override def litOption = None
/** Not really supported */
@@ -1034,11 +1003,6 @@ sealed class AsyncReset(private[chisel3] val width: Width = Width(1)) extends El
private[chisel3] def typeEquivalent(that: Data): Boolean =
this.getClass == that.getClass
- override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match {
- case _: AsyncReset | DontCare => super.connect(that)(sourceInfo, connectCompileOptions)
- case _ => super.badConnect(that)(sourceInfo)
- }
-
override def litOption = None
/** Not really supported */
@@ -1173,6 +1137,7 @@ sealed class Bool() extends UInt(1.W) with Reset {
package experimental {
import chisel3.internal.firrtl.BinaryPoint
+ import chisel3.internal.requireIsHardware // Fix ambiguous import
/** Chisel types that have binary points support retrieving
* literal values as `Double` or `BigDecimal`
diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala
index 03543790..38b08193 100644
--- a/core/src/main/scala/chisel3/BlackBox.scala
+++ b/core/src/main/scala/chisel3/BlackBox.scala
@@ -62,7 +62,7 @@ package experimental {
* @note The parameters API is experimental and may change
*/
abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Param]) extends BaseBlackBox {
- private[chisel3] override def generateComponent(): Component = {
+ private[chisel3] override def generateComponent(): Option[Component] = {
require(!_closed, "Can't generate module more than once")
_closed = true
@@ -81,10 +81,12 @@ package experimental {
id._onModuleClose
}
+ closeUnboundIds(names)
+
val firrtlPorts = getModulePorts map {port => Port(port, port.specifiedDirection)}
val component = DefBlackBox(this, name, firrtlPorts, SpecifiedDirection.Unspecified, params)
_component = Some(component)
- component
+ _component
}
private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = {
@@ -143,7 +145,7 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param
// Allow access to bindings from the compatibility package
protected def _compatIoPortBound() = portsContains(_io)
- private[chisel3] override def generateComponent(): Component = {
+ private[chisel3] override def generateComponent(): Option[Component] = {
_compatAutoWrapPorts() // pre-IO(...) compatibility hack
// Restrict IO to just io, clock, and reset
@@ -156,11 +158,12 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param
val namedPorts = _io.elements.toSeq.reverse // ListMaps are stored in reverse order
- // setRef is not called on the actual io.
// There is a risk of user improperly attempting to connect directly with io
// Long term solution will be to define BlackBox IO differently as part of
// it not descending from the (current) Module
for ((name, port) <- namedPorts) {
+ // We are setting a 'fake' ref for io, so that cloneType works but if a user connects to io, it still fails.
+ this.findPort("io").get.setRef(ModuleIO(internal.ViewParent, ""), force = true)
// We have to force override the _ref because it was set during IO binding
port.setRef(ModuleIO(this, _namespace.name(name)), force = true)
}
@@ -176,7 +179,7 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param
val firrtlPorts = namedPorts map {namedPort => Port(namedPort._2, namedPort._2.specifiedDirection)}
val component = DefBlackBox(this, name, firrtlPorts, _io.specifiedDirection, params)
_component = Some(component)
- component
+ _component
}
private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = {
diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala
index a1f6abf8..32d83008 100644
--- a/core/src/main/scala/chisel3/Data.scala
+++ b/core/src/main/scala/chisel3/Data.scala
@@ -2,13 +2,16 @@
package chisel3
+import chisel3.experimental.dataview.reify
+
import scala.language.experimental.macros
-import chisel3.experimental.{Analog, DataMirror, FixedPoint, Interval}
+import chisel3.experimental.{Analog, BaseModule, DataMirror, FixedPoint, Interval}
import chisel3.internal.Builder.pushCommand
import chisel3.internal._
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.{DeprecatedSourceInfo, SourceInfo, SourceInfoTransform, UnlocatableSourceInfo}
+import scala.collection.immutable.LazyList // Needed for 2.12 alias
import scala.util.Try
/** User-specified directions.
@@ -122,6 +125,7 @@ object ActualDirection {
}
package experimental {
+ import chisel3.internal.requireIsHardware // Fix ambiguous import
/** Experimental hardware construction reflection API
*/
@@ -155,7 +159,9 @@ package experimental {
// with compiled artifacts (vs. elaboration-time reflection)?
def modulePorts(target: BaseModule): Seq[(String, Data)] = target.getChiselPorts
- // Returns all module ports with underscore-qualified names
+ /** Returns all module ports with underscore-qualified names
+ * return includes [[Module.clock]] and [[Module.reset]]
+ */
def fullModulePorts(target: BaseModule): Seq[(String, Data)] = {
def getPortNames(name: String, data: Data): Seq[(String, Data)] = Seq(name -> data) ++ (data match {
case _: Element => Seq()
@@ -233,6 +239,62 @@ private[chisel3] object cloneSupertype {
}
}
+// Returns pairs of all fields, element-level and containers, in a Record and their path names
+private[chisel3] object getRecursiveFields {
+ def apply(data: Data, path: String): Seq[(Data, String)] = data match {
+ case data: Record =>
+ data.elements.map { case (fieldName, fieldData) =>
+ getRecursiveFields(fieldData, s"$path.$fieldName")
+ }.fold(Seq(data -> path)) {
+ _ ++ _
+ }
+ case data: Vec[_] =>
+ data.getElements.zipWithIndex.map { case (fieldData, fieldIndex) =>
+ getRecursiveFields(fieldData, path = s"$path($fieldIndex)")
+ }.fold(Seq(data -> path)) {
+ _ ++ _
+ }
+ case data: Element => Seq(data -> path)
+ }
+
+ def lazily(data: Data, path: String): Seq[(Data, String)] = data match {
+ case data: Record =>
+ LazyList(data -> path) ++
+ data.elements.view.flatMap { case (fieldName, fieldData) =>
+ getRecursiveFields(fieldData, s"$path.$fieldName")
+ }
+ case data: Vec[_] =>
+ LazyList(data -> path) ++
+ data.getElements.view.zipWithIndex.flatMap { case (fieldData, fieldIndex) =>
+ getRecursiveFields(fieldData, path = s"$path($fieldIndex)")
+ }
+ case data: Element => LazyList(data -> path)
+ }
+}
+
+// Returns pairs of corresponding fields between two Records of the same type
+// TODO it seems wrong that Elements are checked for typeEquivalence in Bundle and Vec lit creation
+private[chisel3] object getMatchedFields {
+ def apply(x: Data, y: Data): Seq[(Data, Data)] = (x, y) match {
+ case (x: Element, y: Element) =>
+ require(x typeEquivalent y)
+ Seq(x -> y)
+ case (x: Record, y: Record) =>
+ (x.elements zip y.elements).map { case ((xName, xElt), (yName, yElt)) =>
+ require(xName == yName) // assume fields returned in same, deterministic order
+ getMatchedFields(xElt, yElt)
+ }.fold(Seq(x -> y)) {
+ _ ++ _
+ }
+ case (x: Vec[_], y: Vec[_]) =>
+ (x.getElements zip y.getElements).map { case (xElt, yElt) =>
+ getMatchedFields(xElt, yElt)
+ }.fold(Seq(x -> y)) {
+ _ ++ _
+ }
+ }
+}
+
/** Returns the chisel type of a hardware object, allowing other hardware to be constructed from it.
*/
object chiselTypeOf {
@@ -339,13 +401,14 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
private[chisel3] final def isSynthesizable: Boolean = _binding.map {
case ChildBinding(parent) => parent.isSynthesizable
case _: TopBinding => true
- case _: SampleElementBinding[_] => false
+ case (_: SampleElementBinding[_] | _: MemTypeBinding[_]) => false
}.getOrElse(false)
private[chisel3] def topBindingOpt: Option[TopBinding] = _binding.flatMap {
case ChildBinding(parent) => parent.topBindingOpt
case bindingVal: TopBinding => Some(bindingVal)
case SampleElementBinding(parent) => parent.topBindingOpt
+ case _: MemTypeBinding[_] => None
}
private[chisel3] def topBinding: TopBinding = topBindingOpt.get
@@ -355,7 +418,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
* node is the top-level.
* binding and direction are valid after this call completes.
*/
- private[chisel3] def bind(target: Binding, parentDirection: SpecifiedDirection = SpecifiedDirection.Unspecified)
+ private[chisel3] def bind(target: Binding, parentDirection: SpecifiedDirection = SpecifiedDirection.Unspecified): Unit
// Both _direction and _resolvedUserDirection are saved versions of computed variables (for
// efficiency, avoid expensive recomputation of frequent operations).
@@ -391,6 +454,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
case Some(DontCareBinding()) => s"(DontCare)"
case Some(ElementLitBinding(litArg)) => s"(unhandled literal)"
case Some(BundleLitBinding(litMap)) => s"(unhandled bundle literal)"
+ case Some(VecLitBinding(litMap)) => s"(unhandled vec literal)"
}).getOrElse("")
// Return ALL elements at root of this type.
@@ -483,6 +547,14 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
}
requireIsHardware(this)
topBindingOpt match {
+ // DataView
+ case Some(ViewBinding(target)) => reify(target).ref
+ case Some(AggregateViewBinding(viewMap, _)) =>
+ viewMap.get(this) match {
+ case None => materializeWire() // FIXME FIRRTL doesn't have Aggregate Init expressions
+ // This should not be possible because Element does the lookup in .topBindingOpt
+ case x: Some[_] => throwException(s"Internal Error: In .ref for $this got '$topBindingOpt' and '$x'")
+ }
// Literals
case Some(ElementLitBinding(litArg)) => litArg
case Some(BundleLitBinding(litMap)) =>
@@ -490,6 +562,11 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
case Some(litArg) => litArg
case _ => materializeWire() // FIXME FIRRTL doesn't have Bundle literal expressions
}
+ case Some(VecLitBinding(litMap)) =>
+ litMap.get(this) match {
+ case Some(litArg) => litArg
+ case _ => materializeWire() // FIXME FIRRTL doesn't have Vec literal expressions
+ }
case Some(DontCareBinding()) =>
materializeWire() // FIXME FIRRTL doesn't have a DontCare expression so materialize a Wire
// Non-literals
@@ -504,6 +581,20 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
}
}
+
+ // Recursively set the parent of the start Data and any children (eg. in an Aggregate)
+ private[chisel3] def setAllParents(parent: Option[BaseModule]): Unit = {
+ def rec(data: Data): Unit = {
+ data._parent = parent
+ data match {
+ case _: Element =>
+ case agg: Aggregate =>
+ agg.getElements.foreach(rec)
+ }
+ }
+ rec(this)
+ }
+
private[chisel3] def width: Width
private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit
@@ -554,14 +645,6 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
}
}
- @chiselRuntimeDeprecated
- @deprecated("litArg is deprecated, use litOption or litTo*Option", "3.2")
- def litArg(): Option[LitArg] = topBindingOpt match {
- case Some(ElementLitBinding(litArg)) => Some(litArg)
- case Some(BundleLitBinding(litMap)) => None // this API does not support Bundle literals
- case _ => None
- }
-
def isLit(): Boolean = litOption.isDefined
/**
@@ -764,35 +847,33 @@ object WireDefault {
}
}
-package internal {
- /** RHS (source) for Invalidate API.
- * Causes connection logic to emit a DefInvalid when connected to an output port (or wire).
- */
- private[chisel3] object InternalDontCare extends Element {
- // This object should be initialized before we execute any user code that refers to it,
- // otherwise this "Chisel" object will end up on the UserModule's id list.
- // We make it private to chisel3 so it has to be accessed through the package object.
+/** RHS (source) for Invalidate API.
+ * Causes connection logic to emit a DefInvalid when connected to an output port (or wire).
+ */
+final case object DontCare extends Element {
+ // This object should be initialized before we execute any user code that refers to it,
+ // otherwise this "Chisel" object will end up on the UserModule's id list.
+ // We make it private to chisel3 so it has to be accessed through the package object.
- private[chisel3] override val width: Width = UnknownWidth()
+ private[chisel3] override val width: Width = UnknownWidth()
- bind(DontCareBinding(), SpecifiedDirection.Output)
- override def cloneType: this.type = DontCare
+ bind(DontCareBinding(), SpecifiedDirection.Output)
+ override def cloneType: this.type = DontCare
- override def toString: String = "DontCare()"
+ override def toString: String = "DontCare()"
- override def litOption: Option[BigInt] = None
+ override def litOption: Option[BigInt] = None
- def toPrintable: Printable = PString("DONTCARE")
+ def toPrintable: Printable = PString("DONTCARE")
- private[chisel3] def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = {
- Builder.error("connectFromBits: DontCare cannot be a connection sink (LHS)")
- }
+ private[chisel3] def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = {
+ Builder.error("connectFromBits: DontCare cannot be a connection sink (LHS)")
+ }
- def do_asUInt(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): UInt = {
- Builder.error("DontCare does not have a UInt representation")
- 0.U
- }
- // DontCare's only match themselves.
- private[chisel3] def typeEquivalent(that: Data): Boolean = that == DontCare
+ def do_asUInt(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): UInt = {
+ Builder.error("DontCare does not have a UInt representation")
+ 0.U
}
+ // DontCare's only match themselves.
+ private[chisel3] def typeEquivalent(that: Data): Boolean = that == DontCare
}
diff --git a/core/src/main/scala/chisel3/Element.scala b/core/src/main/scala/chisel3/Element.scala
index 55415f3d..bc006922 100644
--- a/core/src/main/scala/chisel3/Element.scala
+++ b/core/src/main/scala/chisel3/Element.scala
@@ -17,7 +17,8 @@ abstract class Element extends Data {
def widthKnown: Boolean = width.known
def name: String = getRef.name
- private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) {
+ private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = {
+ _parent.foreach(_.addId(this))
binding = target
val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
direction = ActualDirection.fromSpecified(resolvedDirection)
@@ -29,6 +30,14 @@ abstract class Element extends Data {
case Some(litArg) => Some(ElementLitBinding(litArg))
case _ => Some(DontCareBinding())
}
+ case Some(VecLitBinding(litMap)) => litMap.get(this) match {
+ case Some(litArg) => Some(ElementLitBinding(litArg))
+ case _ => Some(DontCareBinding())
+ }
+ case Some(b @ AggregateViewBinding(viewMap, _)) => viewMap.get(this) match {
+ case Some(elt) => Some(ViewBinding(elt))
+ case _ => throwException(s"Internal Error! $this missing from topBinding $b")
+ }
case topBindingOpt => topBindingOpt
}
diff --git a/core/src/main/scala/chisel3/Mem.scala b/core/src/main/scala/chisel3/Mem.scala
index a60b31ac..183620b6 100644
--- a/core/src/main/scala/chisel3/Mem.scala
+++ b/core/src/main/scala/chisel3/Mem.scala
@@ -35,6 +35,7 @@ object Mem {
}
val mt = t.cloneTypeFull
val mem = new Mem(mt, size)
+ mt.bind(MemTypeBinding(mem))
pushCommand(DefMemory(sourceInfo, mem, mt, size))
mem
}
@@ -45,6 +46,8 @@ object Mem {
}
sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) extends HasId with NamedComponent with SourceInfoDoc {
+ _parent.foreach(_.addId(this))
+
// REVIEW TODO: make accessors (static/dynamic, read/write) combinations consistent.
/** Creates a read accessor into the memory with static addressing. See the
@@ -174,6 +177,7 @@ object SyncReadMem {
}
val mt = t.cloneTypeFull
val mem = new SyncReadMem(mt, size, ruw)
+ mt.bind(MemTypeBinding(mem))
pushCommand(DefSeqMemory(sourceInfo, mem, mt, size, ruw))
mem
}
@@ -209,8 +213,14 @@ sealed class SyncReadMem[T <: Data] private (t: T, n: BigInt, val readUnderWrite
var port: Option[T] = None
when (enable) {
a := addr
- port = Some(read(a))
+ port = Some(super.do_read(a))
}
port.get
}
+
+ /** @group SourceInfoTransformMacro*/
+ override def do_read(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) =
+ do_read(addr = idx, enable = true.B)
+ // note: we implement do_read(addr) for SyncReadMem in terms of do_read(addr, en) in order to ensure that
+ // `mem.read(addr)` will always behave the same as `mem.read(addr, true.B)`
}
diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala
index d34211f1..3ae48821 100644
--- a/core/src/main/scala/chisel3/Module.scala
+++ b/core/src/main/scala/chisel3/Module.scala
@@ -4,9 +4,7 @@ package chisel3
import scala.collection.immutable.ListMap
import scala.collection.mutable.{ArrayBuffer, HashMap}
-import scala.collection.JavaConversions._
import scala.language.experimental.macros
-import java.util.IdentityHashMap
import chisel3.internal._
import chisel3.internal.Builder._
@@ -14,6 +12,7 @@ import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.{InstTransform, SourceInfo, UnlocatableSourceInfo}
import chisel3.experimental.BaseModule
import _root_.firrtl.annotations.{IsModule, ModuleName, ModuleTarget}
+import _root_.firrtl.AnnotationSeq
object Module extends SourceInfoDoc {
/** A wrapper method that all Module instantiations must be wrapped in
@@ -65,13 +64,18 @@ object Module extends SourceInfoDoc {
Builder.currentClock = saveClock // Back to clock and reset scope
Builder.currentReset = saveReset
- val component = module.generateComponent()
- Builder.components += component
+ // Only add the component if the module generates one
+ val componentOpt = module.generateComponent()
+ for (component <- componentOpt) {
+ Builder.components += component
+ }
Builder.setPrefix(savePrefix)
// Handle connections at enclosing scope
- if(!Builder.currentModule.isEmpty) {
+ // We use _component because Modules that don't generate them may still have one
+ if (Builder.currentModule.isDefined && module._component.isDefined) {
+ val component = module._component.get
pushCommand(DefInstance(sourceInfo, module, component.ports))
module.initializeInParent(compileOptions)
}
@@ -117,7 +121,8 @@ abstract class Module(implicit moduleCompileOptions: CompileOptions) extends Raw
private[chisel3] def mkReset: Reset = {
// Top module and compatibility mode use Bool for reset
- val inferReset = _parent.isDefined && moduleCompileOptions.inferModuleReset
+ // Note that a Definition elaboration will lack a parent, but still not be a Top module
+ val inferReset = (_parent.isDefined || Builder.inDefinition) && moduleCompileOptions.inferModuleReset
if (inferReset) Reset() else Bool()
}
@@ -138,6 +143,8 @@ abstract class Module(implicit moduleCompileOptions: CompileOptions) extends Raw
package experimental {
+ import chisel3.internal.requireIsChiselType // Fix ambiguous import
+
object IO {
/** Constructs a port for the current Module
*
@@ -175,8 +182,116 @@ package experimental {
package internal {
import chisel3.experimental.BaseModule
+ import chisel3.experimental.hierarchy.IsInstantiable
object BaseModule {
+ /** Represents a clone of an underlying object. This is used to support CloneModuleAsRecord and Instance/Definition.
+ *
+ * @note We don't actually "clone" anything in the traditional sense but is a placeholder so we lazily clone internal state
+ */
+ private [chisel3] trait IsClone[+T] {
+ // Underlying object of which this is a clone of
+ val _proto: T
+ def getProto: T = _proto
+ def isACloneOf(a: Any): Boolean = this == a || _proto == a
+ }
+
+ // Private internal class to serve as a _parent for Data in cloned ports
+ private[chisel3] class ModuleClone[T <: BaseModule] (val _proto: T) extends PseudoModule with IsClone[T] {
+ override def toString = s"ModuleClone(${_proto})"
+ def getPorts = _portsRecord
+ // ClonePorts that hold the bound ports for this module
+ // Used for setting the refs of both this module and the Record
+ private[BaseModule] var _portsRecord: Record = _
+ // This is necessary for correctly supporting .toTarget on a Module Clone. If it is made from the
+ // Instance/Definition API, it should return an instanceTarget. If made from CMAR, it should return a
+ // ModuleTarget.
+ private[chisel3] var _madeFromDefinition: Boolean = false
+ // Don't generate a component, but point to the one for the cloned Module
+ private[chisel3] def generateComponent(): Option[Component] = {
+ require(!_closed, "Can't generate module more than once")
+ _closed = true
+ _component = _proto._component
+ None
+ }
+ // Maps proto ports to module clone's ports
+ private[chisel3] lazy val ioMap: Map[Data, Data] = {
+ val name2Port = getPorts.elements
+ _proto.getChiselPorts.map { case (name, data) => data -> name2Port(name) }.toMap
+ }
+ // This module doesn't actually exist in the FIRRTL so no initialization to do
+ private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = ()
+
+ // Name of this instance's module is the same as the proto's name
+ override def desiredName: String = _proto.name
+
+ private[chisel3] def setRefAndPortsRef(namespace: Namespace): Unit = {
+ val record = _portsRecord
+ // Use .forceName to re-use default name resolving behavior
+ record.forceName(None, default=this.desiredName, namespace)
+ // Now take the Ref that forceName set and convert it to the correct Arg
+ val instName = record.getRef match {
+ case Ref(name) => name
+ case bad => throwException(s"Internal Error! Cloned-module Record $record has unexpected ref $bad")
+ }
+ // Set both the record and the module to have the same instance name
+ record.setRef(ModuleCloneIO(_proto, instName), force=true) // force because we did .forceName first
+ this.setRef(Ref(instName))
+ }
+ }
+
+ /** Represents a module viewed from a different instance context.
+ *
+ * @note Why do we need both ModuleClone and InstanceClone? If we are annotating a reference in a module-clone,
+ * all submodules must be also be 'cloned' so the toTarget can be computed properly. However, we don't need separate
+ * connectable ports for this instance; all that's different from the proto is the parent.
+ *
+ * @note In addition, the instance name of an InstanceClone is going to be the SAME as the proto, but this is not true
+ * for ModuleClone.
+ */
+ private[chisel3] final class InstanceClone[T <: BaseModule] (val _proto: T, val instName: () => String) extends PseudoModule with IsClone[T] {
+ override def toString = s"InstanceClone(${_proto})"
+ // No addition components are generated
+ private[chisel3] def generateComponent(): Option[Component] = None
+ // Necessary for toTarget to work
+ private[chisel3] def setAsInstanceRef(): Unit = { this.setRef(Ref(instName())) }
+ // This module doesn't acutally exist in the FIRRTL so no initialization to do
+ private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = ()
+ // Instance name is the same as proto's instance name
+ override def instanceName = instName()
+ // Module name is the same as proto's module name
+ override def desiredName: String = _proto.name
+ }
+
+ /** Represents a Definition root module, when accessing something from a definition
+ *
+ * @note This is necessary to distinguish between the toTarget behavior for a Module returned from a Definition,
+ * versus a normal Module. A normal Module.toTarget will always return a local target. If calling toTarget
+ * on a Module returned from a Definition (and thus wrapped in an Instance), we need to return the non-local
+ * target whose root is the Definition. This DefinitionClone is used to represent the root parent of the
+ * InstanceClone (which represents the returned module).
+ */
+ private[chisel3] class DefinitionClone[T <: BaseModule] (val _proto: T) extends PseudoModule with IsClone[T] {
+ override def toString = s"DefinitionClone(${_proto})"
+ // No addition components are generated
+ private[chisel3] def generateComponent(): Option[Component] = None
+ // Necessary for toTarget to work
+ private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = ()
+ // Module name is the same as proto's module name
+ override def desiredName: String = _proto.name
+ }
+
+ /** @note If we are cloning a non-module, we need another object which has the proper _parent set!
+ */
+ private[chisel3] final class InstantiableClone[T <: IsInstantiable] (val _proto: T) extends IsClone[T] {
+ private[chisel3] var _parent: Option[BaseModule] = internal.Builder.currentModule
+ }
+
+ /** Record type returned by CloneModuleAsRecord
+ *
+ * @note These are not true Data (the Record doesn't correspond to anything in the emitted
+ * FIRRTL yet its elements *do*) so have some very specialized behavior.
+ */
private[chisel3] class ClonePorts (elts: Data*)(implicit compileOptions: CompileOptions) extends Record {
val elements = ListMap(elts.map(d => d.instanceName -> d.cloneTypeFull): _*)
def apply(field: String) = elements(field)
@@ -185,12 +300,20 @@ package internal {
private[chisel3] def cloneIORecord(proto: BaseModule)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): ClonePorts = {
require(proto.isClosed, "Can't clone a module before module close")
+ // Fake Module to serve as the _parent of the cloned ports
+ // We make this before clonePorts because we want it to come up first in naming in
+ // currentModule
+ val cloneParent = Module(new ModuleClone(proto))
+ require(proto.isClosed, "Can't clone a module before module close")
+ require(cloneParent.getOptionRef.isEmpty, "Can't have ref set already!")
+ // Fake Module to serve as the _parent of the cloned ports
+ // We don't create this inside the ModuleClone because we need the ref to be set by the
+ // currentModule (and not clonePorts)
val clonePorts = new ClonePorts(proto.getModulePorts: _*)
- clonePorts.bind(WireBinding(Builder.forcedUserModule, Builder.currentWhen()))
- val cloneInstance = new DefInstance(sourceInfo, proto, proto._component.get.ports) {
- override def name = clonePorts.getRef.name
- }
- pushCommand(cloneInstance)
+ clonePorts.bind(PortBinding(cloneParent))
+ clonePorts.setAllParents(Some(cloneParent))
+ cloneParent._portsRecord = clonePorts
+ // Normally handled during Module construction but ClonePorts really lives in its parent's parent
if (!compileOptions.explicitInvalidate) {
pushCommand(DefInvalid(sourceInfo, clonePorts.ref))
}
@@ -205,10 +328,21 @@ package internal {
package experimental {
+ import chisel3.experimental.hierarchy.IsInstantiable
+
+ object BaseModule {
+ implicit class BaseModuleExtensions[T <: BaseModule](b: T) {
+ import chisel3.experimental.hierarchy.{Instance, Definition}
+ def toInstance: Instance[T] = new Instance(Left(b))
+ def toDefinition: Definition[T] = new Definition(Left(b))
+ }
+ }
/** Abstract base class for Modules, an instantiable organizational unit for RTL.
*/
// TODO: seal this?
- abstract class BaseModule extends HasId {
+ abstract class BaseModule extends HasId with IsInstantiable {
+ _parent.foreach(_.addId(this))
+
//
// Builder Internals - this tracks which Module RTL construction belongs to.
//
@@ -240,7 +374,15 @@ package experimental {
}
}
- protected def getIds = {
+ // Returns the last id contained within a Module
+ private[chisel3] def _lastId: Long = _ids.last match {
+ case mod: BaseModule => mod._lastId
+ case _ =>
+ // Ideally we could just take last._id, but Records store and thus bind their Data in reverse order
+ _ids.maxBy(_._id)._id
+ }
+
+ private[chisel3] def getIds = {
require(_closed, "Can't get ids before module close")
_ids.toSeq
}
@@ -267,7 +409,7 @@ package experimental {
/** Generates the FIRRTL Component (Module or Blackbox) of this Module.
* Also closes the module so no more construction can happen inside.
*/
- private[chisel3] def generateComponent(): Component
+ private[chisel3] def generateComponent(): Option[Component]
/** Sets up this module in the parent context
*/
@@ -305,9 +447,12 @@ package experimental {
/** Legalized name of this module. */
final lazy val name = try {
- // If this is a module aspect, it should share the same name as the original module
- // Thus, the desired name should be returned without uniquification
- if(this.isInstanceOf[ModuleAspect]) desiredName else Builder.globalNamespace.name(desiredName)
+ // PseudoModules are not "true modules" and thus should share
+ // their original modules names without uniquification
+ this match {
+ case _: PseudoModule => desiredName
+ case _ => Builder.globalNamespace.name(desiredName)
+ }
} catch {
case e: NullPointerException => throwException(
s"Error: desiredName of ${this.getClass.getName} is null. Did you evaluate 'name' before all values needed by desiredName were available?", e)
@@ -318,13 +463,36 @@ package experimental {
*
* @note Should not be called until circuit elaboration is complete
*/
- final def toNamed: ModuleName = toTarget.toNamed
+ final def toNamed: ModuleName = ModuleTarget(this.circuitName, this.name).toNamed
/** Returns a FIRRTL ModuleTarget that references this object
*
* @note Should not be called until circuit elaboration is complete
*/
- final def toTarget: ModuleTarget = ModuleTarget(this.circuitName, this.name)
+ final def toTarget: ModuleTarget = this match {
+ case m: internal.BaseModule.InstanceClone[_] => throwException(s"Internal Error! It's not legal to call .toTarget on an InstanceClone. $m")
+ case m: internal.BaseModule.DefinitionClone[_] => throwException(s"Internal Error! It's not legal to call .toTarget on an DefinitionClone. $m")
+ case _ => ModuleTarget(this.circuitName, this.name)
+ }
+
+ /** Returns the real target of a Module which may be an [[InstanceTarget]]
+ *
+ * BaseModule.toTarget returns a ModuleTarget because the classic Module(new MyModule) API elaborates
+ * Modules in a way that there is a 1:1 relationship between instances and elaborated definitions
+ *
+ * Instance/Definition introduced special internal modules [[InstanceClone]] and [[ModuleClone]] that
+ * do not have this 1:1 relationship so need the ability to return [[InstanceTarget]]s.
+ * Because users can never actually get references to these underlying objects, we can maintain
+ * BaseModule.toTarget's API returning [[ModuleTarget]] while providing an internal API for getting
+ * the correct [[InstanceTarget]]s whenever using the Definition/Instance API.
+ */
+ private[chisel3] def getTarget: IsModule = this match {
+ case m: internal.BaseModule.InstanceClone[_] if m._parent.nonEmpty => m._parent.get.getTarget.instOf(instanceName, name)
+ case m: internal.BaseModule.ModuleClone[_] if m._madeFromDefinition => m._parent.get.getTarget.instOf(instanceName, name)
+ // Without this, we get the wrong CircuitName for the Definition
+ case m: internal.BaseModule.DefinitionClone[_] if m._circuit.nonEmpty => ModuleTarget(this._circuit.get.circuitName, this.name)
+ case _ => this.toTarget
+ }
/** Returns a FIRRTL ModuleTarget that references this object
*
@@ -332,8 +500,15 @@ package experimental {
*/
final def toAbsoluteTarget: IsModule = {
_parent match {
- case Some(parent) => parent.toAbsoluteTarget.instOf(this.instanceName, toTarget.module)
- case None => toTarget
+ case Some(parent) => parent.toAbsoluteTarget.instOf(this.instanceName, name)
+ case None =>
+ // FIXME Special handling for Views - evidence of "weirdness" of .toAbsoluteTarget
+ // In theory, .toAbsoluteTarget should not be necessary, .toTarget combined with the
+ // target disambiguation in FIRRTL's deduplication transform should ensure that .toTarget
+ // is always unambigous. However, legacy workarounds for Chisel's lack of an instance API
+ // have lead some to use .toAbsoluteTarget as a workaround. A proper instance API will make
+ // it possible to deprecate and remove .toAbsoluteTarget
+ if (this == ViewParent) ViewParent.absoluteTarget else getTarget
}
}
@@ -382,6 +557,17 @@ package experimental {
names
}
+ /** Invokes _onModuleClose on HasIds found via reflection but not bound to hardware
+ * (thus not part of _ids)
+ * This maintains old naming behavior for non-hardware Data
+ */
+ private[chisel3] def closeUnboundIds(names: HashMap[HasId, String]): Unit = {
+ val idLookup = _ids.toSet
+ for ((id, _) <- names if !idLookup(id)) {
+ id._onModuleClose
+ }
+ }
+
/** Compatibility function. Allows Chisel2 code which had ports without the IO wrapper to
* compile under Bindings checks. Does nothing in non-compatibility mode.
*
diff --git a/core/src/main/scala/chisel3/ModuleAspect.scala b/core/src/main/scala/chisel3/ModuleAspect.scala
index 4e37ab35..a528fa72 100644
--- a/core/src/main/scala/chisel3/ModuleAspect.scala
+++ b/core/src/main/scala/chisel3/ModuleAspect.scala
@@ -2,7 +2,7 @@
package chisel3
-import chisel3.internal.Builder
+import chisel3.internal.{Builder, PseudoModule}
/** Used by Chisel Aspects to inject Chisel code into modules, after they have been elaborated.
* This is an internal API - don't use!
@@ -13,7 +13,7 @@ import chisel3.internal.Builder
* @param moduleCompileOptions
*/
abstract class ModuleAspect private[chisel3] (module: RawModule)
- (implicit moduleCompileOptions: CompileOptions) extends RawModule {
+ (implicit moduleCompileOptions: CompileOptions) extends RawModule with PseudoModule {
Builder.addAspect(module, this)
diff --git a/core/src/main/scala/chisel3/Num.scala b/core/src/main/scala/chisel3/Num.scala
index e1af9bdb..6dd299f4 100644
--- a/core/src/main/scala/chisel3/Num.scala
+++ b/core/src/main/scala/chisel3/Num.scala
@@ -156,7 +156,7 @@ trait Num[T <: Data] {
/** Minimum operator
*
* @param that a hardware $coll
- * @return a $numType with a value equal to the mimimum value of this $coll and `that`
+ * @return a $numType with a value equal to the minimum value of this $coll and `that`
* $maxWidth
* @group Arithmetic
*/
@@ -169,7 +169,7 @@ trait Num[T <: Data] {
/** Maximum operator
*
* @param that a $numType
- * @return a $numType with a value equal to the mimimum value of this $coll and `that`
+ * @return a $numType with a value equal to the minimum value of this $coll and `that`
* $maxWidth
* @group Arithmetic
*/
@@ -226,7 +226,7 @@ trait NumObject {
*/
def toBigInt(x: BigDecimal, binaryPoint: Int): BigInt = {
val multiplier = math.pow(2, binaryPoint)
- val result = (x * multiplier).rounded.toBigInt()
+ val result = (x * multiplier).rounded.toBigInt
result
}
@@ -304,4 +304,4 @@ trait NumObject {
throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x")
}
}
-} \ No newline at end of file
+}
diff --git a/core/src/main/scala/chisel3/Printf.scala b/core/src/main/scala/chisel3/Printf.scala
index 7cbd1918..cf7821b8 100644
--- a/core/src/main/scala/chisel3/Printf.scala
+++ b/core/src/main/scala/chisel3/Printf.scala
@@ -3,11 +3,10 @@
package chisel3
import scala.language.experimental.macros
-
import chisel3.internal._
import chisel3.internal.Builder.pushCommand
-import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.SourceInfo
+import chisel3.experimental.BaseSim
/** Prints a message in simulation
*
@@ -34,6 +33,9 @@ object printf {
formatIn map escaped mkString ""
}
+ /** Named class for [[printf]]s. */
+ final class Printf(val pable: Printable) extends BaseSim
+
/** Prints a message in simulation
*
* Prints a message every cycle. If defined within the scope of a [[when]] block, the message
@@ -71,7 +73,7 @@ object printf {
* @param fmt printf format string
* @param data format string varargs containing data to print
*/
- def apply(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit =
+ def apply(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf =
apply(Printable.pack(fmt, data:_*))
/** Prints a message in simulation
*
@@ -87,16 +89,20 @@ object printf {
* @see [[Printable]] documentation
* @param pable [[Printable]] to print
*/
- def apply(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = {
+ def apply(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf = {
+ var printfId: Printf = null
when (!Module.reset.asBool) {
- printfWithoutReset(pable)
+ printfId = printfWithoutReset(pable)
}
+ printfId
}
- private[chisel3] def printfWithoutReset(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = {
+ private[chisel3] def printfWithoutReset(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf = {
val clock = Builder.forcedClock
- pushCommand(Printf(sourceInfo, clock.ref, pable))
+ val printfId = new Printf(pable)
+ pushCommand(chisel3.internal.firrtl.Printf(printfId, sourceInfo, clock.ref, pable))
+ printfId
}
- private[chisel3] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit =
+ private[chisel3] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf =
printfWithoutReset(Printable.pack(fmt, data:_*))
}
diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala
index 0adacedb..27f16ad4 100644
--- a/core/src/main/scala/chisel3/RawModule.scala
+++ b/core/src/main/scala/chisel3/RawModule.scala
@@ -4,14 +4,14 @@ package chisel3
import scala.collection.mutable.{ArrayBuffer, HashMap}
import scala.util.Try
-import scala.collection.JavaConversions._
import scala.language.experimental.macros
-
-import chisel3.experimental.BaseModule
+import chisel3.experimental.{BaseModule, BaseSim}
import chisel3.internal._
+import chisel3.internal.BaseModule.{ModuleClone, InstanceClone}
import chisel3.internal.Builder._
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.UnlocatableSourceInfo
+import _root_.firrtl.annotations.{IsModule, ModuleTarget}
/** 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
@@ -37,6 +37,7 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions)
//
// For debuggers/testers, TODO: refactor out into proper public API
private var _firrtlPorts: Option[Seq[firrtl.Port]] = None
+ @deprecated("Use DataMirror.fullModulePorts instead. this API will be removed in Chisel 3.6", "Chisel 3.5")
lazy val getPorts = _firrtlPorts.get
val compileOptions = moduleCompileOptions
@@ -59,7 +60,7 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions)
}
- private[chisel3] override def generateComponent(): Component = {
+ private[chisel3] override def generateComponent(): Option[Component] = {
require(!_closed, "Can't generate module more than once")
_closed = true
@@ -76,8 +77,11 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions)
// All suggestions are in, force names to every node.
for (id <- getIds) {
id match {
+ case id: ModuleClone[_] => id.setRefAndPortsRef(_namespace) // special handling
+ case id: InstanceClone[_] => id.setAsInstanceRef()
case id: BaseModule => id.forceName(None, default=id.desiredName, _namespace)
case id: MemBase[_] => id.forceName(None, default="MEM", _namespace)
+ case id: BaseSim => id.forceName(None, default="SIM", _namespace)
case id: Data =>
if (id.isSynthesizable) {
id.topBinding match {
@@ -98,6 +102,8 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions)
id._onModuleClose
}
+ closeUnboundIds(names)
+
val firrtlPorts = getModulePorts map { port: Data =>
// Special case Vec to make FIRRTL emit the direction of its
// element.
@@ -129,7 +135,7 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions)
}
val component = DefModule(this, name, firrtlPorts, invalidateCommands ++ getCommands)
_component = Some(component)
- component
+ _component
}
private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = {
@@ -153,6 +159,13 @@ trait RequireSyncReset extends Module {
package object internal {
+ import scala.annotation.implicitNotFound
+ @implicitNotFound("You are trying to access a macro-only API. Please use the @public annotation instead.")
+ trait MacroGenerated
+
+ /** Marker trait for modules that are not true modules */
+ private[chisel3] trait PseudoModule extends BaseModule
+
// Private reflective version of "val io" to maintain Chisel.Module semantics without having
// io as a virtual method. See https://github.com/freechipsproject/chisel3/pull/1550 for more
// information about the removal of "val io"
@@ -220,7 +233,7 @@ package object internal {
// Allow access to bindings from the compatibility package
protected def _compatIoPortBound() = portsContains(_io)
- private[chisel3] override def generateComponent(): Component = {
+ private[chisel3] override def generateComponent(): Option[Component] = {
_compatAutoWrapPorts() // pre-IO(...) compatibility hack
// Restrict IO to just io, clock, and reset
@@ -260,4 +273,31 @@ package object internal {
}
}
}
+
+ /** Internal API for [[ViewParent]] */
+ sealed private[chisel3] class ViewParentAPI extends RawModule()(ExplicitCompileOptions.Strict) with PseudoModule {
+ // We must provide `absoluteTarget` but not `toTarget` because otherwise they would be exactly
+ // the same and we'd have no way to distinguish the kind of target when renaming view targets in
+ // the Converter
+ // Note that this is not overriding .toAbsoluteTarget, that is a final def in BaseModule that delegates
+ // to this method
+ private[chisel3] val absoluteTarget: IsModule = ModuleTarget(this.circuitName, "_$$AbsoluteView$$_")
+
+ // This module is not instantiable
+ override private[chisel3] def generateComponent(): Option[Component] = None
+ override private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = ()
+ // This module is not really part of the circuit
+ _parent = None
+
+ // Sigil to mark views, starts with '_' to make it a legal FIRRTL target
+ override def desiredName = "_$$View$$_"
+
+ private[chisel3] val fakeComponent: Component = DefModule(this, desiredName, Nil, Nil)
+ }
+
+ /** Special internal object representing the parent of all views
+ *
+ * @note this is a val instead of an object because of the need to wrap in Module(...)
+ */
+ private[chisel3] val ViewParent = Module.do_apply(new ViewParentAPI)(UnlocatableSourceInfo, ExplicitCompileOptions.Strict)
}
diff --git a/core/src/main/scala/chisel3/Reg.scala b/core/src/main/scala/chisel3/Reg.scala
index b2b99cc1..bd9e5311 100644
--- a/core/src/main/scala/chisel3/Reg.scala
+++ b/core/src/main/scala/chisel3/Reg.scala
@@ -127,7 +127,7 @@ object RegNext {
* val x = Wire(UInt())
* val y = Wire(UInt(8.W))
* val r1 = RegInit(x) // width will be inferred
- * val r2 = RegInit(y) // width is set to 8
+ * val r2 = RegInit(y) // width will be inferred
* }}}
*
* 3. [[Aggregate]] initializer - width will be set to match the aggregate
diff --git a/core/src/main/scala/chisel3/SeqUtils.scala b/core/src/main/scala/chisel3/SeqUtils.scala
index f6642bcb..da6fc802 100644
--- a/core/src/main/scala/chisel3/SeqUtils.scala
+++ b/core/src/main/scala/chisel3/SeqUtils.scala
@@ -7,7 +7,7 @@ import chisel3.internal.{prefix, throwException}
import scala.language.experimental.macros
import chisel3.internal.sourceinfo._
-
+import chisel3.internal.plugin.autoNameRecursively
private[chisel3] object SeqUtils {
/** Concatenates the data elements of the input sequence, in sequence order, together.
@@ -26,12 +26,12 @@ private[chisel3] object SeqUtils {
} else if (in.tail.isEmpty) {
in.head.asUInt
} else {
- val lo = prefix("lo") {
+ val lo = autoNameRecursively("lo")(prefix("lo") {
asUInt(in.slice(0, in.length/2))
- }.autoSeed("lo")
- val hi = prefix("hi") {
+ })
+ val hi = autoNameRecursively("hi")(prefix("hi") {
asUInt(in.slice(in.length/2, in.length))
- }.autoSeed("hi")
+ })
hi ## lo
}
}
diff --git a/core/src/main/scala/chisel3/StrongEnum.scala b/core/src/main/scala/chisel3/StrongEnum.scala
index 2d372a94..b3d7cf7d 100644
--- a/core/src/main/scala/chisel3/StrongEnum.scala
+++ b/core/src/main/scala/chisel3/StrongEnum.scala
@@ -18,7 +18,7 @@ object EnumAnnotations {
/** An annotation for strong enum instances that are ''not'' inside of Vecs
*
* @param target the enum instance being annotated
- * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'')
+ * @param enumTypeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'')
*/
case class EnumComponentAnnotation(target: Named, enumTypeName: String) extends SingleTargetAnnotation[Named] {
def duplicate(n: Named): EnumComponentAnnotation = this.copy(target = n)
@@ -50,11 +50,11 @@ object EnumAnnotations {
*
*/
case class EnumVecAnnotation(target: Named, typeName: String, fields: Seq[Seq[String]]) extends SingleTargetAnnotation[Named] {
- def duplicate(n: Named) = this.copy(target = n)
+ def duplicate(n: Named): EnumVecAnnotation = this.copy(target = n)
}
case class EnumVecChiselAnnotation(target: InstanceId, typeName: String, fields: Seq[Seq[String]]) extends ChiselAnnotation {
- override def toFirrtl = EnumVecAnnotation(target.toNamed, typeName, fields)
+ override def toFirrtl: EnumVecAnnotation = EnumVecAnnotation(target.toNamed, typeName, fields)
}
/** An annotation for enum types (rather than enum ''instances'').
@@ -131,10 +131,28 @@ abstract class EnumType(private val factory: EnumFactory, selfAnnotating: Boolea
if (litOption.isDefined) {
true.B
} else {
- factory.all.map(this === _).reduce(_ || _)
+ if (factory.isTotal) true.B else factory.all.map(this === _).reduce(_ || _)
}
}
+ /** Test if this enumeration is equal to any of the values in a given sequence
+ *
+ * @param s a [[scala.collection.Seq$ Seq]] of enumeration values to look for
+ * @return a hardware [[Bool]] that indicates if this value matches any of the given values
+ */
+ final def isOneOf(s: Seq[EnumType])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = {
+ VecInit(s.map(this === _)).asUInt().orR()
+ }
+
+ /** Test if this enumeration is equal to any of the values given as arguments
+ *
+ * @param u1 the first value to look for
+ * @param u2 zero or more additional values to look for
+ * @return a hardware [[Bool]] that indicates if this value matches any of the given values
+ */
+ final def isOneOf(u1: EnumType, u2: EnumType*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool
+ = isOneOf(u1 +: u2.toSeq)
+
def next(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = {
if (litOption.isDefined) {
val index = factory.all.indexOf(this)
@@ -225,14 +243,20 @@ abstract class EnumFactory {
private[chisel3] var width: Width = 0.W
private case class EnumRecord(inst: Type, name: String)
- private val enum_records = mutable.ArrayBuffer.empty[EnumRecord]
+ private val enumRecords = mutable.ArrayBuffer.empty[EnumRecord]
- private def enumNames = enum_records.map(_.name).toSeq
- private def enumValues = enum_records.map(_.inst.litValue()).toSeq
- private def enumInstances = enum_records.map(_.inst).toSeq
+ private def enumNames = enumRecords.map(_.name).toSeq
+ private def enumValues = enumRecords.map(_.inst.litValue()).toSeq
+ private def enumInstances = enumRecords.map(_.inst).toSeq
private[chisel3] val enumTypeName = getClass.getName.init
+ // Do all bitvectors of this Enum's width represent legal states?
+ private[chisel3] def isTotal: Boolean = {
+ (this.getWidth < 31) && // guard against Integer overflow
+ (enumRecords.size == (1 << this.getWidth))
+ }
+
private[chisel3] def globalAnnotation: EnumDefChiselAnnotation =
EnumDefChiselAnnotation(enumTypeName, (enumNames, enumValues).zipped.toMap)
@@ -241,7 +265,7 @@ abstract class EnumFactory {
def all: Seq[Type] = enumInstances
private[chisel3] def nameOfValue(id: BigInt): Option[String] = {
- enum_records.find(_.inst.litValue() == id).map(_.name)
+ enumRecords.find(_.inst.litValue() == id).map(_.name)
}
protected def Value: Type = macro EnumMacros.ValImpl
@@ -253,7 +277,7 @@ abstract class EnumFactory {
// We have to use UnknownWidth here, because we don't actually know what the final width will be
result.bindToLiteral(id, UnknownWidth())
- enum_records.append(EnumRecord(result, name))
+ enumRecords.append(EnumRecord(result, name))
width = (1 max id.bitLength).W
id += 1
@@ -277,7 +301,7 @@ abstract class EnumFactory {
def apply(): Type = new Type
- def apply(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Type = {
+ private def castImpl(n: UInt, warn: Boolean)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Type = {
if (n.litOption.isDefined) {
enumInstances.find(_.litValue == n.litValue) match {
case Some(result) => result
@@ -288,8 +312,9 @@ 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 {
- Builder.warning(s"Casting non-literal UInt to $enumTypeName. You can check that its value is legal by calling isValid")
-
+ if (warn && !this.isTotal) {
+ Builder.warning(s"Casting non-literal UInt to $enumTypeName. You can use $enumTypeName.safe to cast without this warning.")
+ }
val glue = Wire(new UnsafeEnum(width))
glue := n
val result = Wire(new Type)
@@ -297,6 +322,25 @@ abstract class EnumFactory {
result
}
}
+
+ /** Cast an [[UInt]] to the type of this Enum
+ *
+ * @note will give a Chisel elaboration time warning if the argument could hit invalid states
+ * @param n the UInt to cast
+ * @return the equivalent Enum to the value of the cast UInt
+ */
+ def apply(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Type = castImpl(n, warn = true)
+
+ /** Safely cast an [[UInt]] to the type of this Enum
+ *
+ * @param n the UInt to cast
+ * @return the equivalent Enum to the value of the cast UInt and a Bool indicating if the
+ * Enum is valid
+ */
+ def safe(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): (Type, Bool) = {
+ val t = castImpl(n, warn = false)
+ (t, t.isValid)
+ }
}
diff --git a/core/src/main/scala/chisel3/aop/Aspect.scala b/core/src/main/scala/chisel3/aop/Aspect.scala
index 59add417..be9b8975 100644
--- a/core/src/main/scala/chisel3/aop/Aspect.scala
+++ b/core/src/main/scala/chisel3/aop/Aspect.scala
@@ -12,6 +12,10 @@ import firrtl.AnnotationSeq
* @tparam T Type of top-level module
*/
abstract class Aspect[T <: RawModule] extends Annotation with Unserializable with NoTargetAnnotation {
+ /** variable to save [[AnnotationSeq]] from [[chisel3.stage.phases.AspectPhase]]
+ * to be used at [[chisel3.aop.injecting.InjectorAspect]], exposes annotations to [[chisel3.internal.DynamicContext]]
+ */
+ private[aop] var annotationsInAspect: AnnotationSeq = Seq()
/** Convert this Aspect to a seq of FIRRTL annotation
* @param top
* @return
@@ -22,7 +26,8 @@ abstract class Aspect[T <: RawModule] extends Annotation with Unserializable wit
* @param top
* @return
*/
- private[chisel3] def resolveAspect(top: RawModule): AnnotationSeq = {
+ private[chisel3] def resolveAspect(top: RawModule, remainingAnnotations: AnnotationSeq): AnnotationSeq = {
+ annotationsInAspect = remainingAnnotations
toAnnotation(top.asInstanceOf[T])
}
}
diff --git a/core/src/main/scala/chisel3/core/package.scala b/core/src/main/scala/chisel3/core/package.scala
deleted file mode 100644
index fa29f244..00000000
--- a/core/src/main/scala/chisel3/core/package.scala
+++ /dev/null
@@ -1,288 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-package chisel3
-
-/**
- * These definitions exist to deal with those clients that relied on chisel3.core.
- * They are deprecated and will be removed in the future.
- */
-package object core {
-
- @deprecated("Use the version in chisel3._", "3.2")
- val CompileOptions = chisel3.CompileOptions
-
- @deprecated("Use the version in chisel3._", "3.2")
- val Input = chisel3.Input
- @deprecated("Use the version in chisel3._", "3.2")
- val Output = chisel3.Output
- @deprecated("Use the version in chisel3._", "3.2")
- val Flipped = chisel3.Flipped
- @deprecated("Use the version in chisel3._", "3.2")
- val chiselTypeOf = chisel3.chiselTypeOf
-
- @deprecated("Use the version in chisel3._", "3.2")
- type Data = chisel3.Data
-
- @deprecated("Use the version in chisel3._", "3.2")
- val WireDefault = chisel3.WireDefault
-
- @deprecated("Use the version in chisel3._", "3.2")
- val Clock = chisel3.Clock
- @deprecated("Use the version in chisel3._", "3.2")
- type Clock = chisel3.Clock
-
- @deprecated("Use the version in chisel3._", "3.2")
- type Reset = chisel3.Reset
-
- @deprecated("Use the version in chisel3._", "3.2")
- type Aggregate = chisel3.Aggregate
-
- @deprecated("Use the version in chisel3._", "3.2")
- val Vec = chisel3.Vec
- @deprecated("Use the version in chisel3._", "3.2")
- val VecInit = chisel3.VecInit
- @deprecated("Use the version in chisel3._", "3.2")
- type Vec[T <: Data] = chisel3.Vec[T]
- @deprecated("Use the version in chisel3._", "3.2")
- type VecLike[T <: Data] = chisel3.VecLike[T]
- @deprecated("Use the version in chisel3._", "3.2")
- type Bundle = chisel3.Bundle
- @deprecated("Use the version in chisel3._", "3.2")
- type IgnoreSeqInBundle = chisel3.IgnoreSeqInBundle
- @deprecated("Use the version in chisel3._", "3.2")
- type Record = chisel3.Record
-
- @deprecated("Use the version in chisel3._", "3.2")
- val assert = chisel3.assert
-
- @deprecated("Use the version in chisel3._", "3.2")
- type Element = chisel3.Element
- @deprecated("Use the version in chisel3._", "3.2")
- type Bits = chisel3.Bits
-
- // These provide temporary compatibility for those who foolishly imported from chisel3.core
- @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " +
- " Use chisel3.RawModule instead. This alias will be removed in 3.4.", "since the beginning of time")
- type RawModule = chisel3.RawModule
- @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " +
- "Use chisel3.MultiIOModule instead. This alias will be removed in 3.4.", "since the beginning of time")
- type MultiIOModule = chisel3.Module
- @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " +
- " Use chisel3.RawModule instead. This alias will be removed in 3.4.", "since the beginning of time")
- type UserModule = chisel3.RawModule
- @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " +
- "Use chisel3.MultiIOModule instead. This alias will be removed in 3.4.", "since the beginning of time")
- type ImplicitModule = chisel3.Module
-
- @deprecated("Use the version in chisel3._", "3.2")
- val Bits = chisel3.Bits
- @deprecated("Use the version in chisel3._", "3.2")
- type Num[T <: chisel3.Data] = chisel3.Num[T]
- @deprecated("Use the version in chisel3._", "3.2")
- type UInt = chisel3.UInt
- @deprecated("Use the version in chisel3._", "3.2")
- val UInt = chisel3.UInt
- @deprecated("Use the version in chisel3._", "3.2")
- type SInt = chisel3.SInt
- @deprecated("Use the version in chisel3._", "3.2")
- val SInt = chisel3.SInt
- @deprecated("Use the version in chisel3._", "3.2")
- type Bool = chisel3.Bool
- @deprecated("Use the version in chisel3._", "3.2")
- val Bool = chisel3.Bool
- @deprecated("Use the version in chisel3._", "3.2")
- val Mux = chisel3.Mux
-
- @deprecated("Use the version in chisel3._", "3.2")
- type BlackBox = chisel3.BlackBox
-
- @deprecated("Use the version in chisel3._", "3.2")
- val Mem = chisel3.Mem
- @deprecated("Use the version in chisel3._", "3.2")
- type MemBase[T <: chisel3.Data] = chisel3.MemBase[T]
- @deprecated("Use the version in chisel3._", "3.2")
- type Mem[T <: chisel3.Data] = chisel3.Mem[T]
- @deprecated("Use the version in chisel3._", "3.2")
- val SyncReadMem = chisel3.SyncReadMem
- @deprecated("Use the version in chisel3._", "3.2")
- type SyncReadMem[T <: chisel3.Data] = chisel3.SyncReadMem[T]
-
- @deprecated("Use the version in chisel3._", "3.2")
- val Module = chisel3.Module
- @deprecated("Use the version in chisel3._", "3.2")
- type Module = chisel3.Module
-
- @deprecated("Use the version in chisel3._", "3.2")
- val printf = chisel3.printf
-
- @deprecated("Use the version in chisel3._", "3.2")
- val RegNext = chisel3.RegNext
- @deprecated("Use the version in chisel3._", "3.2")
- val RegInit = chisel3.RegInit
- @deprecated("Use the version in chisel3._", "3.2")
- val Reg = chisel3.Reg
-
- @deprecated("Use the version in chisel3._", "3.2")
- val when = chisel3.when
- @deprecated("Use the version in chisel3._", "3.2")
- type WhenContext = chisel3.WhenContext
-
- @deprecated("Use the version in chisel3._", "3.2")
- type Printable = chisel3.Printable
- @deprecated("Use the version in chisel3._", "3.2")
- val Printable = chisel3.Printable
- @deprecated("Use the version in chisel3._", "3.2")
- type Printables = chisel3.Printables
- @deprecated("Use the version in chisel3._", "3.2")
- val Printables = chisel3.Printables
- @deprecated("Use the version in chisel3._", "3.2")
- type PString = chisel3.PString
- @deprecated("Use the version in chisel3._", "3.2")
- val PString = chisel3.PString
- @deprecated("Use the version in chisel3._", "3.2")
- type FirrtlFormat = chisel3.FirrtlFormat
- @deprecated("Use the version in chisel3._", "3.2")
- val FirrtlFormat = chisel3.FirrtlFormat
- @deprecated("Use the version in chisel3._", "3.2")
- type Decimal = chisel3.Decimal
- @deprecated("Use the version in chisel3._", "3.2")
- val Decimal = chisel3.Decimal
- @deprecated("Use the version in chisel3._", "3.2")
- type Hexadecimal = chisel3.Hexadecimal
- val Hexadecimal = chisel3.Hexadecimal
- @deprecated("Use the version in chisel3._", "3.2")
- type Binary = chisel3.Binary
- @deprecated("Use the version in chisel3._", "3.2")
- val Binary = chisel3.Binary
- @deprecated("Use the version in chisel3._", "3.2")
- type Character = chisel3.Character
- @deprecated("Use the version in chisel3._", "3.2")
- val Character = chisel3.Character
- @deprecated("Use the version in chisel3._", "3.2")
- type Name = chisel3.Name
- @deprecated("Use the version in chisel3._", "3.2")
- val Name = chisel3.Name
- @deprecated("Use the version in chisel3._", "3.2")
- type FullName = chisel3.FullName
- @deprecated("Use the version in chisel3._", "3.2")
- val FullName = chisel3.FullName
- @deprecated("Use the version in chisel3._", "3.2")
- val Percent = chisel3.Percent
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type Param = chisel3.experimental.Param
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type IntParam = chisel3.experimental.IntParam
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val IntParam = chisel3.experimental.IntParam
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type DoubleParam = chisel3.experimental.DoubleParam
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val DoubleParam = chisel3.experimental.DoubleParam
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type StringParam = chisel3.experimental.StringParam
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val StringParam = chisel3.experimental.StringParam
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type RawParam = chisel3.experimental.RawParam
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val RawParam = chisel3.experimental.RawParam
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type Analog = chisel3.experimental.Analog
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val Analog = chisel3.experimental.Analog
-
- @deprecated("Use the version in chisel3._", "3.2")
- implicit class fromIntToWidth(int: Int) extends chisel3.fromIntToWidth(int)
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val attach = chisel3.experimental.attach
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type EnumType = chisel3.experimental.EnumType
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type EnumFactory = chisel3.experimental.EnumFactory
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val EnumAnnotations = chisel3.experimental.EnumAnnotations
-
- @deprecated("Use the version in chisel3._", "3.2")
- val withClockAndReset = chisel3.withClockAndReset
- @deprecated("Use the version in chisel3._", "3.2")
- val withClock = chisel3.withClock
- @deprecated("Use the version in chisel3._", "3.2")
- val withReset = chisel3.withReset
-
- @deprecated("Use the version in chisel3._", "3.2")
- val dontTouch = chisel3.dontTouch
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type BaseModule = chisel3.experimental.BaseModule
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type ExtModule = chisel3.experimental.ExtModule
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val IO = chisel3.experimental.IO
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type FixedPoint = chisel3.experimental.FixedPoint
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val FixedPoint = chisel3.experimental.FixedPoint
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- implicit class fromDoubleToLiteral(double: Double) extends experimental.FixedPoint.Implicits.fromDoubleToLiteral(double)
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- implicit class fromIntToBinaryPoint(int: Int) extends chisel3.fromIntToBinaryPoint(int)
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- type RunFirrtlTransform = chisel3.experimental.RunFirrtlTransform
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val annotate = chisel3.experimental.annotate
-
- @deprecated("Use the version in chisel3.experimental._", "3.2")
- val DataMirror = chisel3.experimental.DataMirror
- @deprecated("Use the version in chisel3._", "3.2")
- type ActualDirection = chisel3.ActualDirection
- @deprecated("Use the version in chisel3._", "3.2")
- val ActualDirection = chisel3.ActualDirection
-
- @deprecated("Use the version in chisel3.internal._", "3.2")
- val requireIsHardware = chisel3.internal.requireIsHardware
- @deprecated("Use the version in chisel3.internal._", "3.2")
- val requireIsChiselType = chisel3.internal.requireIsChiselType
- @deprecated("Use the version in chisel3.internal._", "3.2")
- val BiConnect = chisel3.internal.BiConnect
- @deprecated("Use the version in chisel3.internal._", "3.2")
- val MonoConnect = chisel3.internal.MonoConnect
- @deprecated("Use the version in chisel3.internal._", "3.2")
- val BindingDirection = chisel3.internal.BindingDirection
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type Binding = chisel3.internal.Binding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type TopBinding = chisel3.internal.TopBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type UnconstrainedBinding = chisel3.internal.UnconstrainedBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type ConstrainedBinding = chisel3.internal.ConstrainedBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type ReadOnlyBinding = chisel3.internal.ReadOnlyBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type OpBinding = chisel3.internal.OpBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type MemoryPortBinding = chisel3.internal.MemoryPortBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type PortBinding = chisel3.internal.PortBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type RegBinding = chisel3.internal.RegBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type WireBinding = chisel3.internal.WireBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type ChildBinding = chisel3.internal.ChildBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type DontCareBinding = chisel3.internal.DontCareBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type LitBinding = chisel3.internal.LitBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type ElementLitBinding = chisel3.internal.ElementLitBinding
- @deprecated("Use the version in chisel3.internal._", "3.2")
- type BundleLitBinding = chisel3.internal.BundleLitBinding
-}
diff --git a/core/src/main/scala/chisel3/dontTouch.scala b/core/src/main/scala/chisel3/dontTouch.scala
index 0487a567..e679f1a4 100644
--- a/core/src/main/scala/chisel3/dontTouch.scala
+++ b/core/src/main/scala/chisel3/dontTouch.scala
@@ -5,7 +5,8 @@ package chisel3
import chisel3.experimental.{ChiselAnnotation, annotate, requireIsHardware}
import firrtl.transforms.DontTouchAnnotation
-/** Marks that a signal should not be removed by Chisel and Firrtl optimization passes
+/** Marks that a signal is an optimization barrier to Chisel and the FIRRTL compiler. This has the effect of
+ * guaranteeing that a signal will not be removed.
*
* @example {{{
* class MyModule extends Module {
@@ -21,9 +22,11 @@ import firrtl.transforms.DontTouchAnnotation
* @note Calling this on [[Data]] creates an annotation that Chisel emits to a separate annotations
* file. This file must be passed to FIRRTL independently of the `.fir` file. The execute methods
* in [[chisel3.Driver]] will pass the annotations to FIRRTL automatically.
+ * @note Because this is an optimization barrier, constants will not be propagated through a signal marked as
+ * dontTouch.
*/
object dontTouch {
- /** Marks a signal to be preserved in Chisel and Firrtl
+ /** Mark a signal as an optimization barrier to Chisel and FIRRTL.
*
* @note Requires the argument to be bound to hardware
* @param data The signal to be marked
diff --git a/core/src/main/scala/chisel3/experimental/Analog.scala b/core/src/main/scala/chisel3/experimental/Analog.scala
index df76fd70..6cca81f5 100644
--- a/core/src/main/scala/chisel3/experimental/Analog.scala
+++ b/core/src/main/scala/chisel3/experimental/Analog.scala
@@ -45,7 +45,8 @@ final class Analog private (private[chisel3] val width: Width) extends Element {
// Define setter/getter pairing
// Analog can only be bound to Ports and Wires (and Unbound)
- private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) {
+ private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = {
+ _parent.foreach(_.addId(this))
SpecifiedDirection.fromParent(parentDirection, specifiedDirection) match {
case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip =>
case x => throwException(s"Analog may not have explicit direction, got '$x'")
@@ -55,6 +56,7 @@ final class Analog private (private[chisel3] val width: Width) extends Element {
case ChildBinding(parent) => parent.topBinding
// See https://github.com/freechipsproject/chisel3/pull/946
case SampleElementBinding(parent) => parent.topBinding
+ case a: MemTypeBinding[_] => a
}
targetTopBinding match {
@@ -82,4 +84,3 @@ final class Analog private (private[chisel3] val width: Width) extends Element {
object Analog {
def apply(width: Width): Analog = new Analog(width)
}
-
diff --git a/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala b/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala
new file mode 100644
index 00000000..55dd8505
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.dataview
+
+import chisel3.experimental.BaseModule
+import chisel3.{Data, getRecursiveFields}
+
+import scala.annotation.implicitNotFound
+
+/** Typeclass interface for getting elements of type [[Data]]
+ *
+ * This is needed for validating [[DataView]]s targeting type `A`.
+ * Can be thought of as "can be the Target of a DataView".
+ *
+ * Chisel provides some implementations in [[DataProduct$ object DataProduct]] that are available
+ * by default in the implicit scope.
+ *
+ * @tparam A Type that has elements of type [[Data]]
+ * @see [[https://www.chisel-lang.org/chisel3/docs/explanations/dataview#dataproduct Detailed Documentation]]
+ */
+@implicitNotFound("Could not find implicit value for DataProduct[${A}].\n" +
+ "Please see https://www.chisel-lang.org/chisel3/docs/explanations/dataview#dataproduct")
+trait DataProduct[-A] {
+ /** Provides [[Data]] elements within some containing object
+ *
+ * @param a Containing object
+ * @param path Hierarchical path to current signal (for error reporting)
+ * @return Data elements and associated String paths (Strings for error reporting only!)
+ */
+ def dataIterator(a: A, path: String): Iterator[(Data, String)]
+
+ /** Returns a checker to test if the containing object contains a `Data` object
+ * @note Implementers may want to override if iterating on all `Data` is expensive for `A` and `A`
+ * will primarily be used in `PartialDataViews`
+ * @note The returned value is a function, not a true Set, but is describing the functionality of
+ * Set containment
+ * @param a Containing object
+ * @return A checker that itself returns True if a given `Data` is contained in `a`
+ * as determined by an `==` test
+ */
+ def dataSet(a: A): Data => Boolean = dataIterator(a, "").map(_._1).toSet
+}
+
+/** Encapsulating object for automatically provided implementations of [[DataProduct]]
+ *
+ * @note DataProduct implementations provided in this object are available in the implicit scope
+ */
+object DataProduct {
+ /** [[DataProduct]] implementation for [[Data]] */
+ implicit val dataDataProduct: DataProduct[Data] = new DataProduct[Data] {
+ def dataIterator(a: Data, path: String): Iterator[(Data, String)] =
+ getRecursiveFields.lazily(a, path).iterator
+ }
+
+ /** [[DataProduct]] implementation for [[BaseModule]] */
+ implicit val userModuleDataProduct: DataProduct[BaseModule] = new DataProduct[BaseModule] {
+ def dataIterator(a: BaseModule, path: String): Iterator[(Data, String)] = {
+ a.getIds.iterator.flatMap {
+ case d: Data if d.getOptionRef.isDefined => // Using ref to decide if it's truly hardware in the module
+ Seq(d -> s"${path}.${d.instanceName}")
+ case b: BaseModule => dataIterator(b, s"$path.${b.instanceName}")
+ case _ => Seq.empty
+ }
+ }
+ // Overridden for performance
+ override def dataSet(a: BaseModule): Data => Boolean = {
+ val lastId = a._lastId // Not cheap to compute
+ // Return a function
+ e => e._id > a._id && e._id <= lastId
+ }
+ }
+}
diff --git a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala
new file mode 100644
index 00000000..caf004c2
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.dataview
+
+import chisel3._
+import chisel3.internal.sourceinfo.SourceInfo
+import scala.reflect.runtime.universe.WeakTypeTag
+
+import annotation.implicitNotFound
+
+
+/** Mapping between a target type `T` and a view type `V`
+ *
+ * Enables calling `.viewAs[T]` on instances of the target type.
+ *
+ * ==Detailed documentation==
+ * - [[https://www.chisel-lang.org/chisel3/docs/explanations/dataview Explanation]]
+ * - [[https://www.chisel-lang.org/chisel3/docs/cookbooks/dataview Cookbook]]
+ *
+ * @example {{{
+ * class Foo(val w: Int) extends Bundle {
+ * val a = UInt(w.W)
+ * }
+ * class Bar(val w: Int) extends Bundle {
+ * val b = UInt(w.W)
+ * }
+ * // DataViews are created using factory methods in the companion object
+ * implicit val view = DataView[Foo, Bar](
+ * // The first argument is a function constructing a Foo from a Bar
+ * foo => new Bar(foo.w)
+ * // The remaining arguments are a variable number of field pairings
+ * _.a -> _.b
+ * )
+ * }}}
+ *
+ * @tparam T Target type (must have an implementation of [[DataProduct]])
+ * @tparam V View type
+ * @see [[DataView$ object DataView]] for factory methods
+ * @see [[PartialDataView object PartialDataView]] for defining non-total `DataViews`
+ */
+@implicitNotFound("Could not find implicit value for DataView[${T}, ${V}].\n" +
+ "Please see https://www.chisel-lang.org/chisel3/docs/explanations/dataview")
+sealed class DataView[T : DataProduct, V <: Data] private[chisel3] (
+ /** Function constructing an object of the View type from an object of the Target type */
+ private[chisel3] val mkView: T => V,
+ /** Function that returns corresponding fields of the target and view */
+ private[chisel3] val mapping: (T, V) => Iterable[(Data, Data)],
+ // Aliasing this with a def below to make the ScalaDoc show up for the field
+ _total: Boolean
+)(
+ implicit private[chisel3] val sourceInfo: SourceInfo
+) {
+ /** Indicates if the mapping contains every field of the target */
+ def total: Boolean = _total
+
+ override def toString: String = {
+ val base = sourceInfo.makeMessage(x => x)
+ val loc = if (base.nonEmpty) base else "@unknown"
+ val name = if (total) "DataView" else "PartialDataView"
+ s"$name(defined $loc)"
+ }
+
+ /** Compose two `DataViews` together to construct a view from the target of this `DataView` to the
+ * view type of the second `DataView`
+ *
+ * @param g a DataView from `V` to new view-type `V2`
+ * @tparam V2 View type of `DataView` `g`
+ * @return a new `DataView` from the original `T` to new view-type `V2`
+ */
+ def andThen[V2 <: Data](g: DataView[V, V2])(implicit sourceInfo: SourceInfo): DataView[T, V2] = {
+ val self = this
+ // We have to pass the DataProducts and DataViews manually to .viewAs below
+ val tdp = implicitly[DataProduct[T]]
+ val vdp = implicitly[DataProduct[V]]
+ new DataView[T, V2](
+ t => g.mkView(mkView(t)),
+ { case (t, v2) => List(t.viewAs[V](tdp, self).viewAs[V2](vdp, g) -> v2) },
+ this.total && g.total
+ ) {
+ override def toString: String = s"$self andThen $g"
+ }
+ }
+}
+
+/** Factory methods for constructing [[DataView]]s, see class for example use */
+object DataView {
+
+ /** Default factory method, alias for [[pairs]] */
+ def apply[T : DataProduct, V <: Data](mkView: T => V, pairs: ((T, V) => (Data, Data))*)(implicit sourceInfo: SourceInfo): DataView[T, V] =
+ DataView.pairs(mkView, pairs: _*)
+
+ /** Construct [[DataView]]s with pairs of functions from the target and view to corresponding fields */
+ def pairs[T : DataProduct, V <: Data](mkView: T => V, pairs: ((T, V) => (Data, Data))*)(implicit sourceInfo: SourceInfo): DataView[T, V] =
+ mapping(mkView: T => V, swizzle(pairs))
+
+ /** More general factory method for complex mappings */
+ def mapping[T : DataProduct, V <: Data](mkView: T => V, mapping: (T, V) => Iterable[(Data, Data)])(implicit sourceInfo: SourceInfo): DataView[T, V] =
+ new DataView[T, V](mkView, mapping, _total = true)
+
+ /** Provides `invert` for invertible [[DataView]]s
+ *
+ * This must be done as an extension method because it applies an addition constraint on the `Target`
+ * type parameter, namely that it must be a subtype of [[Data]].
+ *
+ * @note [[PartialDataView]]s are **not** invertible and will result in an elaboration time exception
+ */
+ implicit class InvertibleDataView[T <: Data : WeakTypeTag, V <: Data : WeakTypeTag](view: DataView[T, V]) {
+ def invert(mkView: V => T): DataView[V, T] = {
+ // It would've been nice to make this a compiler error, but it's unclear how to make that work.
+ // We tried having separate TotalDataView and PartialDataView and only defining inversion for
+ // TotalDataView. For some reason, implicit resolution wouldn't invert TotalDataViews. This is
+ // probably because it was looking for the super-type DataView and since invertDataView was
+ // only defined on TotalDataView, it wasn't included in implicit resolution. Thus we end up
+ // with a runtime check.
+ if (!view.total) {
+ val tt = implicitly[WeakTypeTag[T]].tpe
+ val vv = implicitly[WeakTypeTag[V]].tpe
+ val msg = s"Cannot invert '$view' as it is non-total.\n Try providing a DataView[$vv, $tt]." +
+ s"\n Please see https://www.chisel-lang.org/chisel3/docs/explanations/dataview."
+ throw InvalidViewException(msg)
+ }
+ implicit val sourceInfo = view.sourceInfo
+ new DataView[V, T](mkView, swapArgs(view.mapping), view.total)
+ }
+ }
+
+ private[dataview] def swizzle[A, B, C, D](fs: Iterable[(A, B) => (C, D)]): (A, B) => Iterable[(C, D)] = {
+ case (a, b) => fs.map(f => f(a, b))
+ }
+
+ private def swapArgs[A, B, C, D](f: (A, B) => Iterable[(C, D)]): (B, A) => Iterable[(D, C)] = {
+ case (b, a) => f(a, b).map(_.swap)
+ }
+
+ /** All Chisel Data are viewable as their own type */
+ implicit def identityView[A <: Data](implicit sourceInfo: SourceInfo): DataView[A, A] =
+ DataView[A, A](chiselTypeOf.apply, { case (x, y) => (x, y) })
+}
+
+/** Factory methods for constructing non-total [[DataView]]s */
+object PartialDataView {
+
+ /** Default factory method, alias for [[pairs]] */
+ def apply[T: DataProduct, V <: Data](mkView: T => V, pairs: ((T, V) => (Data, Data))*)(implicit sourceInfo: SourceInfo): DataView[T, V] =
+ PartialDataView.pairs(mkView, pairs: _*)
+
+ /** Construct [[DataView]]s with pairs of functions from the target and view to corresponding fields */
+ def pairs[T: DataProduct, V <: Data](mkView: T => V, pairs: ((T, V) => (Data, Data))*)(implicit sourceInfo: SourceInfo): DataView[T, V] =
+ mapping(mkView, DataView.swizzle(pairs))
+
+ /** More general factory method for complex mappings */
+ def mapping[T: DataProduct, V <: Data](mkView: T => V, mapping: (T, V) => Iterable[(Data, Data)])(implicit sourceInfo: SourceInfo): DataView[T, V] =
+ new DataView[T, V](mkView, mapping, _total = false)
+}
diff --git a/core/src/main/scala/chisel3/experimental/dataview/package.scala b/core/src/main/scala/chisel3/experimental/dataview/package.scala
new file mode 100644
index 00000000..1acf43e1
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/dataview/package.scala
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental
+
+import chisel3._
+import chisel3.internal._
+import chisel3.internal.sourceinfo.SourceInfo
+
+import scala.annotation.{implicitNotFound, tailrec}
+import scala.collection.mutable
+import scala.collection.immutable.LazyList // Needed for 2.12 alias
+
+package object dataview {
+ case class InvalidViewException(message: String) extends ChiselException(message)
+
+ /** Provides `viewAs` for types that have an implementation of [[DataProduct]]
+ *
+ * Calling `viewAs` also requires an implementation of [[DataView]] for the target type
+ */
+ implicit class DataViewable[T](target: T) {
+ def viewAs[V <: Data](implicit dataproduct: DataProduct[T], dataView: DataView[T, V]): V = {
+ // TODO put a try catch here for ExpectedHardwareException and perhaps others
+ // It's likely users will accidentally use chiselTypeOf or something that may error,
+ // The right thing to use is DataMirror...chiselTypeClone because of composition with DataView.andThen
+ // Another option is that .andThen could give a fake binding making chiselTypeOfs in the user code safe
+ val result: V = dataView.mkView(target)
+ requireIsChiselType(result, "viewAs")
+
+ doBind(target, result, dataView)
+
+ // Setting the parent marks these Data as Views
+ result.setAllParents(Some(ViewParent))
+ // The names of views do not matter except for when a view is annotated. For Views that correspond
+ // To a single Data, we just forward the name of the Target. For Views that correspond to more
+ // than one Data, we return this assigned name but rename it in the Convert stage
+ result.forceName(None, "view", Builder.viewNamespace)
+ result
+ }
+ }
+
+ // This private type alias lets us provide a custom error message for misuing the .viewAs for upcasting Bundles
+ @implicitNotFound("${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
+
+ /** 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
+ })
+ target.viewAs[V]
+ }
+ }
+
+ private def nonTotalViewException(dataView: DataView[_, _], target: Any, view: Data, targetFields: Seq[String], viewFields: Seq[String]) = {
+ def missingMsg(name: String, fields: Seq[String]): Option[String] = {
+ val str = fields.mkString(", ")
+ fields.size match {
+ case 0 => None
+ case 1 => Some(s"$name field '$str' is missing")
+ case _ => Some(s"$name fields '$str' are missing")
+ }
+ }
+ val vs = missingMsg("view", viewFields)
+ val ts = missingMsg("target", targetFields)
+ val reasons = (ts ++ vs).mkString(" and ").capitalize
+ val suggestion = if (ts.nonEmpty) "\n If the view *should* be non-total, try a 'PartialDataView'." else ""
+ val msg = s"Viewing $target as $view is non-Total!\n $reasons.\n DataView used is $dataView.$suggestion"
+ throw InvalidViewException(msg)
+ }
+
+ // TODO should this be moved to class Aggregate / can it be unified with Aggregate.bind?
+ private def doBind[T : DataProduct, V <: Data](target: T, view: V, dataView: DataView[T, V]): Unit = {
+ val mapping = dataView.mapping(target, view)
+ val total = dataView.total
+ // Lookups to check the mapping results
+ val viewFieldLookup: Map[Data, String] = getRecursiveFields(view, "_").toMap
+ val targetContains: Data => Boolean = implicitly[DataProduct[T]].dataSet(target)
+
+ // Resulting bindings for each Element of the View
+ val childBindings =
+ new mutable.HashMap[Data, mutable.ListBuffer[Element]] ++
+ viewFieldLookup.view
+ .collect { case (elt: Element, _) => elt }
+ .map(_ -> new mutable.ListBuffer[Element])
+
+ def viewFieldName(d: Data): String =
+ viewFieldLookup.get(d).map(_ + " ").getOrElse("") + d.toString
+
+ // Helper for recording the binding of each
+ def onElt(te: Element, ve: Element): Unit = {
+ // TODO can/should we aggregate these errors?
+ def err(name: String, arg: Data) =
+ throw InvalidViewException(s"View mapping must only contain Elements within the $name, got $arg")
+
+ // The elements may themselves be views, look through the potential chain of views for the Elements
+ // that are actually members of the target or view
+ val tex = unfoldView(te).find(targetContains).getOrElse(err("Target", te))
+ val vex = unfoldView(ve).find(viewFieldLookup.contains).getOrElse(err("View", ve))
+
+ if (tex.getClass != vex.getClass) {
+ val fieldName = viewFieldName(vex)
+ throw InvalidViewException(s"Field $fieldName specified as view of non-type-equivalent value $tex")
+ }
+ // View width must be unknown or match target width
+ if (vex.widthKnown && vex.width != tex.width) {
+ def widthAsString(x: Element) = x.widthOption.map("<" + _ + ">").getOrElse("<unknown>")
+ val fieldName = viewFieldName(vex)
+ val vwidth = widthAsString(vex)
+ val twidth = widthAsString(tex)
+ throw InvalidViewException(s"View field $fieldName has width ${vwidth} that is incompatible with target value $tex's width ${twidth}")
+ }
+ childBindings(vex) += tex
+ }
+
+ mapping.foreach {
+ // Special cased because getMatchedFields checks typeEquivalence on Elements (and is used in Aggregate path)
+ // Also saves object allocations on common case of Elements
+ case (ae: Element, be: Element) => onElt(ae, be)
+
+ case (aa: Aggregate, ba: Aggregate) =>
+ if (!ba.typeEquivalent(aa)) {
+ val fieldName = viewFieldLookup(ba)
+ throw InvalidViewException(s"field $fieldName specified as view of non-type-equivalent value $aa")
+ }
+ getMatchedFields(aa, ba).foreach {
+ case (aelt: Element, belt: Element) => onElt(aelt, belt)
+ case _ => // Ignore matching of Aggregates
+ }
+ }
+
+ // Errors in totality of the View, use var List to keep fast path cheap (no allocation)
+ var viewNonTotalErrors: List[Data] = Nil
+ var targetNonTotalErrors: List[String] = Nil
+
+ val targetSeen: Option[mutable.Set[Data]] = if (total) Some(mutable.Set.empty[Data]) else None
+
+ val resultBindings = childBindings.map { case (data, targets) =>
+ val targetsx = targets match {
+ case collection.Seq(target: Element) => target
+ case collection.Seq() =>
+ viewNonTotalErrors = data :: viewNonTotalErrors
+ data.asInstanceOf[Element] // Return the Data itself, will error after this map, cast is safe
+ case x =>
+ throw InvalidViewException(s"Got $x, expected Seq(_: Direct)")
+ }
+ // TODO record and report aliasing errors
+ targetSeen.foreach(_ += targetsx)
+ data -> targetsx
+ }.toMap
+
+ // Check for totality of Target
+ targetSeen.foreach { seen =>
+ val lookup = implicitly[DataProduct[T]].dataIterator(target, "_")
+ for (missed <- lookup.collect { case (d: Element, name) if !seen(d) => name }) {
+ targetNonTotalErrors = missed :: targetNonTotalErrors
+ }
+ }
+ if (viewNonTotalErrors != Nil || targetNonTotalErrors != Nil) {
+ val viewErrors = viewNonTotalErrors.map(f => viewFieldLookup.getOrElse(f, f.toString))
+ nonTotalViewException(dataView, target, view, targetNonTotalErrors, viewErrors)
+ }
+
+ view match {
+ case elt: Element => view.bind(ViewBinding(resultBindings(elt)))
+ case agg: Aggregate =>
+ // We record total Data mappings to provide a better .toTarget
+ val topt = target match {
+ case d: Data if total => Some(d)
+ case _ =>
+ // Record views that don't have the simpler .toTarget for later renaming
+ Builder.unnamedViews += view
+ None
+ }
+ // TODO We must also record children as unnamed, some could be namable but this requires changes to the Binding
+ getRecursiveFields.lazily(view, "_").foreach {
+ case (agg: Aggregate, _) if agg != view =>
+ Builder.unnamedViews += agg
+ case _ => // Do nothing
+ }
+ agg.bind(AggregateViewBinding(resultBindings, topt))
+ }
+ }
+
+ // Traces an Element that may (or may not) be a view until it no longer maps
+ // Inclusive of the argument
+ private def unfoldView(elt: Element): LazyList[Element] = {
+ def rec(e: Element): LazyList[Element] = e.topBindingOpt match {
+ case Some(ViewBinding(target)) => target #:: rec(target)
+ case Some(AggregateViewBinding(mapping, _)) =>
+ val target = mapping(e)
+ target #:: rec(target)
+ case Some(_) | None => LazyList.empty
+ }
+ elt #:: rec(elt)
+ }
+
+ // Safe for all Data
+ private[chisel3] def isView(d: Data): Boolean = d._parent.contains(ViewParent)
+
+ /** Turn any [[Element]] that could be a View into a concrete Element
+ *
+ * This is the fundamental "unwrapping" or "tracing" primitive operation for handling Views within
+ * Chisel.
+ */
+ private[chisel3] def reify(elt: Element): Element =
+ reify(elt, elt.topBinding)
+
+ /** Turn any [[Element]] that could be a View into a concrete Element
+ *
+ * This is the fundamental "unwrapping" or "tracing" primitive operation for handling Views within
+ * Chisel.
+ */
+ @tailrec private[chisel3] def reify(elt: Element, topBinding: TopBinding): Element =
+ topBinding match {
+ case ViewBinding(target) => reify(target, elt.topBinding)
+ case _ => elt
+ }
+
+ /** Determine the target of a View if it is a single Target
+ *
+ * @note An Aggregate may be a view of unrelated [[Data]] (eg. like a Seq or tuple) and thus this
+ * there is no single Data representing the Target and this function will return None
+ * @return The single Data target of this view or None if a single Data doesn't exist
+ */
+ private[chisel3] def reifySingleData(data: Data): Option[Data] = {
+ val candidate: Option[Data] =
+ data.binding.collect { // First check if this is a total mapping of an Aggregate
+ case AggregateViewBinding(_, Some(t)) => t
+ }.orElse { // Otherwise look via top binding
+ data.topBindingOpt match {
+ case None => None
+ case Some(ViewBinding(target)) => Some(target)
+ case Some(AggregateViewBinding(lookup, _)) => lookup.get(data)
+ case Some(_) => None
+ }
+ }
+ candidate.flatMap { d =>
+ // Candidate may itself be a view, keep tracing in those cases
+ if (isView(d)) reifySingleData(d) else Some(d)
+ }
+ }
+
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
new file mode 100644
index 00000000..0cc3d131
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import scala.language.experimental.macros
+import chisel3._
+
+import scala.collection.mutable.HashMap
+import chisel3.internal.{Builder, DynamicContext}
+import chisel3.internal.sourceinfo.{DefinitionTransform, DefinitionWrapTransform, SourceInfo}
+import chisel3.experimental.BaseModule
+import chisel3.internal.BaseModule.IsClone
+import firrtl.annotations.{IsModule, ModuleTarget}
+
+/** User-facing Definition type.
+ * Represents a definition of an object of type [[A]] which are marked as @instantiable
+ * Can be created using Definition.apply method.
+ *
+ * These definitions are then used to create multiple [[Instance]]s.
+ *
+ * @param cloned The internal representation of the instance, which may be either be directly the object, or a clone of an object
+ */
+case class Definition[+A] private[chisel3] (private[chisel3] cloned: Either[A, IsClone[A]]) extends IsLookupable {
+ private[chisel3] def proto: A = cloned match {
+ case Left(value: A) => value
+ case Right(i: IsClone[A]) => i._proto
+ }
+ /** Used by Chisel's internal macros. DO NOT USE in your normal Chisel code!!!
+ * Instead, mark the field you are accessing with [[@public]]
+ *
+ * Given a selector function (that) which selects a member from the original, return the
+ * corresponding member from the instance.
+ *
+ * Our @instantiable and @public macros generate the calls to this apply method
+ *
+ * By calling this function, we summon the proper Lookupable typeclass from our implicit scope.
+ *
+ * @param that a user-specified lookup function
+ * @param lookup typeclass which contains the correct lookup function, based on the types of A and B
+ * @param macroGenerated a value created in the macro, to make it harder for users to use this API
+ */
+ def _lookup[B, C](that: A => B)(implicit lookup: Lookupable[B], macroGenerated: chisel3.internal.MacroGenerated): lookup.C = {
+ lookup.definitionLookup(that, this)
+ }
+
+ /** Updated by calls to [[apply]], to avoid recloning returned Data's */
+ private [chisel3] val cache = HashMap[Data, Data]()
+
+
+ /** @return the context of any Data's return from inside the instance */
+ private[chisel3] def getInnerDataContext: Option[BaseModule] = proto match {
+ case value: BaseModule =>
+ val newChild = Module.do_apply(new internal.BaseModule.DefinitionClone(value))(chisel3.internal.sourceinfo.UnlocatableSourceInfo, chisel3.ExplicitCompileOptions.Strict)
+ newChild._circuit = value._circuit.orElse(Some(value))
+ newChild._parent = None
+ Some(newChild)
+ case value: IsInstantiable => None
+ }
+
+}
+
+/** Factory methods for constructing [[Definition]]s */
+object Definition extends SourceInfoDoc {
+ implicit class DefinitionBaseModuleExtensions[T <: BaseModule](d: Definition[T]) {
+ /** If this is an instance of a Module, returns the toTarget of this instance
+ * @return target of this instance
+ */
+ def toTarget: ModuleTarget = d.proto.toTarget
+
+ /** If this is an instance of a Module, returns the toAbsoluteTarget of this instance
+ * @return absoluteTarget of this instance
+ */
+ def toAbsoluteTarget: IsModule = d.proto.toAbsoluteTarget
+ }
+ /** A construction method to build a Definition of a Module
+ *
+ * @param proto the Module being defined
+ *
+ * @return the input module as a Definition
+ */
+ def apply[T <: BaseModule with IsInstantiable](proto: => T): Definition[T] = macro DefinitionTransform.apply[T]
+
+ /** A construction method to build a Definition of a Module
+ *
+ * @param bc the Module being defined
+ *
+ * @return the input module as a Definition
+ */
+ def do_apply[T <: BaseModule with IsInstantiable](proto: => T) (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Definition[T] = {
+ val dynamicContext = new DynamicContext(Nil)
+ Builder.globalNamespace.copyTo(dynamicContext.globalNamespace)
+ dynamicContext.inDefinition = true
+ val (ir, module) = Builder.build(Module(proto), dynamicContext)
+ Builder.components ++= ir.components
+ Builder.annotations ++= ir.annotations
+ module._circuit = Builder.currentModule
+ dynamicContext.globalNamespace.copyTo(Builder.globalNamespace)
+ new Definition(Left(module))
+ }
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala
new file mode 100644
index 00000000..9b17bfce
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import scala.collection.mutable.{ArrayBuffer, HashMap}
+import scala.language.experimental.macros
+import chisel3._
+import chisel3.internal.BaseModule.{InstantiableClone, IsClone, ModuleClone}
+import chisel3.internal.sourceinfo.{InstanceTransform, SourceInfo}
+import chisel3.experimental.BaseModule
+import firrtl.annotations.IsModule
+
+/** User-facing Instance type.
+ * Represents a unique instance of type [[A]] which are marked as @instantiable
+ * Can be created using Instance.apply method.
+ *
+ * @param cloned The internal representation of the instance, which may be either be directly the object, or a clone of an object
+ */
+case class Instance[+A] private [chisel3] (private[chisel3] cloned: Either[A, IsClone[A]]) {
+
+ /** Returns the original object which is instantiated here.
+ * If this is an instance of a clone, return that clone's original proto
+ *
+ * @return the original object which was instantiated
+ */
+ private[chisel3] def proto: A = cloned match {
+ case Left(value: A) => value
+ case Right(i: IsClone[A]) => i._proto
+ }
+
+ /** @return the context of any Data's return from inside the instance */
+ private[chisel3] def getInnerDataContext: Option[BaseModule] = cloned match {
+ case Left(value: BaseModule) => Some(value)
+ case Left(value: IsInstantiable) => None
+ case Right(i: BaseModule) => Some(i)
+ case Right(i: InstantiableClone[_]) => i._parent
+ }
+
+ /** @return the context this instance. Note that for non-module clones, getInnerDataContext will be the same as getClonedParent */
+ private[chisel3] def getClonedParent: Option[BaseModule] = cloned match {
+ case Left(value: BaseModule) => value._parent
+ case Right(i: BaseModule) => i._parent
+ case Right(i: InstantiableClone[_]) => i._parent
+ }
+
+ /** Updated by calls to [[apply]], to avoid recloning returned Data's */
+ private [chisel3] val cache = HashMap[Data, Data]()
+
+ /** Used by Chisel's internal macros. DO NOT USE in your normal Chisel code!!!
+ * Instead, mark the field you are accessing with [[@public]]
+ *
+ * Given a selector function (that) which selects a member from the original, return the
+ * corresponding member from the instance.
+ *
+ * Our @instantiable and @public macros generate the calls to this apply method
+ *
+ * By calling this function, we summon the proper Lookupable typeclass from our implicit scope.
+ *
+ * @param that a user-specified lookup function
+ * @param lookup typeclass which contains the correct lookup function, based on the types of A and B
+ * @param macroGenerated a value created in the macro, to make it harder for users to use this API
+ */
+ def _lookup[B, C](that: A => B)(implicit lookup: Lookupable[B], macroGenerated: chisel3.internal.MacroGenerated): lookup.C = {
+ lookup.instanceLookup(that, this)
+ }
+
+ /** Returns the definition of this Instance */
+ def toDefinition: Definition[A] = new Definition(Left(proto))
+
+}
+
+/** Factory methods for constructing [[Instance]]s */
+object Instance extends SourceInfoDoc {
+ implicit class InstanceBaseModuleExtensions[T <: BaseModule](i: Instance[T]) {
+ /** If this is an instance of a Module, returns the toTarget of this instance
+ * @return target of this instance
+ */
+ def toTarget: IsModule = i.cloned match {
+ case Left(x: BaseModule) => x.getTarget
+ case Right(x: IsClone[_] with BaseModule) => x.getTarget
+ }
+
+ /** If this is an instance of a Module, returns the toAbsoluteTarget of this instance
+ * @return absoluteTarget of this instance
+ */
+ def toAbsoluteTarget: IsModule = i.cloned match {
+ case Left(x) => x.toAbsoluteTarget
+ case Right(x: IsClone[_] with BaseModule) => x.toAbsoluteTarget
+ }
+
+ }
+ /** A constructs an [[Instance]] from a [[Definition]]
+ *
+ * @param definition the Module being created
+ * @return an instance of the module definition
+ */
+ def apply[T <: BaseModule with IsInstantiable](definition: Definition[T]): Instance[T] = macro InstanceTransform.apply[T]
+
+ /** A constructs an [[Instance]] from a [[Definition]]
+ *
+ * @param definition the Module being created
+ * @return an instance of the module definition
+ */
+ def do_apply[T <: BaseModule with IsInstantiable](definition: Definition[T])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Instance[T] = {
+ val ports = experimental.CloneModuleAsRecord(definition.proto)
+ val clone = ports._parent.get.asInstanceOf[ModuleClone[T]]
+ clone._madeFromDefinition = true
+ new Instance(Right(clone))
+ }
+
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala
new file mode 100644
index 00000000..26ba0286
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+/** While this is public, it is not recommended for users to extend directly.
+ * Instead, use the [[@instantiable]] annotation on your trait or class.
+ *
+ * This trait indicates whether a class can be returned from an Instance.
+ *
+ */
+trait IsInstantiable
+
+object IsInstantiable {
+ implicit class IsInstantiableExtensions[T <: IsInstantiable](i: T) {
+ def toInstance: Instance[T] = new Instance(Left(i))
+ }
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/IsLookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/IsLookupable.scala
new file mode 100644
index 00000000..37d29a43
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/IsLookupable.scala
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+/** A User-extendable trait to mark metadata-containers, e.g. parameter case classes, as valid to return unchanged
+ * from an instance.
+ *
+ * This should only be true of the metadata returned is identical for ALL instances!
+ *
+ * @example For instances of the same proto, metadata or other construction parameters
+ * may be useful to access outside of the instance construction. For parameters that are
+ * the same for all instances, we should mark it as IsLookupable
+ * {{{
+ * case class Params(debugMessage: String) extends IsLookupable
+ * class MyModule(p: Params) extends MultiIOModule {
+ * printf(p.debugMessage)
+ * }
+ * val myParams = Params("Hello World")
+ * val definition = Definition(new MyModule(myParams))
+ * val i0 = Instance(definition)
+ * val i1 = Instance(definition)
+ * require(i0.p == i1.p) // p is only accessable because it extends IsLookupable
+ * }}}
+ */
+trait IsLookupable
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
new file mode 100644
index 00000000..b9617723
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.experimental.hierarchy
+
+import chisel3.experimental.BaseModule
+import chisel3.internal.sourceinfo.SourceInfo
+import chisel3.internal.BaseModule.{InstanceClone, InstantiableClone, IsClone, ModuleClone}
+
+import scala.annotation.implicitNotFound
+import scala.collection.mutable.HashMap
+import chisel3._
+import chisel3.experimental.dataview.{isView, reify, reifySingleData}
+import chisel3.internal.firrtl.{Arg, ILit, Index, Slot, ULit}
+import chisel3.internal.{AggregateViewBinding, Builder, ChildBinding, ViewBinding, ViewParent, throwException}
+
+/** Represents lookup typeclass to determine how a value accessed from an original IsInstantiable
+ * should be tweaked to return the Instance's version
+ * Sealed.
+ */
+@implicitNotFound("@public is only legal within a class marked @instantiable and only on vals of type" +
+ " Data, BaseModule, IsInstantiable, IsLookupable, or Instance[_], or in an Iterable or Option")
+sealed trait Lookupable[-B] {
+ type C // Return type of the lookup
+ /** Function called to modify the returned value of type B from A, into C
+ *
+ * @param that function that selects B from A
+ * @param instance Instance of A, used to determine C's context
+ * @return
+ */
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C
+
+ /** Function called to modify the returned value of type B from A, into C
+ *
+ * @param that function that selects B from A
+ * @param definition Definition of A, used to determine C's context
+ * @return
+ */
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C
+}
+
+private[chisel3] object Lookupable {
+
+ /** Clones a data and sets its internal references to its parent module to be in a new context.
+ *
+ * @param data data to be cloned
+ * @param context new context
+ * @return
+ */
+ private[chisel3] def cloneDataToContext[T <: Data](data: T, context: BaseModule)
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
+ internal.requireIsHardware(data, "cross module reference type")
+ data._parent match {
+ case None => data
+ case Some(parent) =>
+ val newParent = cloneModuleToContext(Left(parent), context)
+ newParent match {
+ case Left(p) if p == parent => data
+ case Right(m: BaseModule) =>
+ val newChild = data.cloneTypeFull
+ newChild.setRef(data.getRef, true)
+ newChild.bind(internal.CrossModuleBinding)
+ newChild.setAllParents(Some(m))
+ newChild
+ }
+ }
+ }
+ // The business logic of lookupData
+ // Also called by cloneViewToContext which potentially needs to lookup stuff from ioMap or the cache
+ private[chisel3] def doLookupData[A, B <: Data](data: B, cache: HashMap[Data, Data], ioMap: Option[Map[Data, Data]], context: Option[BaseModule])
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): B = {
+ def impl[C <: Data](d: C): C = d match {
+ case x: Data if ioMap.nonEmpty && ioMap.get.contains(x) => ioMap.get(x).asInstanceOf[C]
+ case x: Data if cache.contains(x) => cache(x).asInstanceOf[C]
+ case _ =>
+ assert(context.nonEmpty) // TODO is this even possible? Better error message here
+ val ret = cloneDataToContext(d, context.get)
+ cache(d) = ret
+ ret
+ }
+ data.binding match {
+ case Some(_: ChildBinding) => mapRootAndExtractSubField(data, impl)
+ case _ => impl(data)
+ }
+ }
+
+ // Helper for co-iterating on Elements of aggregates, they must be the same type but that is unchecked
+ private def coiterate(a: Data, b: Data): Iterable[(Element, Element)] = {
+ val as = getRecursiveFields.lazily(a, "_")
+ val bs = getRecursiveFields.lazily(b, "_")
+ as.zip(bs).collect { case ((ae: Element, _), (be: Element, _)) => (ae, be) }
+ }
+
+ /** Given a Data, find the root of its binding, apply a function to the root to get a "new root",
+ * and find the equivalent child Data in the "new root"
+ *
+ * @example {{{
+ * Given `arg = a.b[2].c` and some `f`:
+ * 1. a = root(arg) = root(a.b[2].c)
+ * 2. newRoot = f(root(arg)) = f(a)
+ * 3. return newRoot.b[2].c
+ * }}}
+ *
+ * Invariants that elt is a Child of something of the type of data is dynamically checked as we traverse
+ */
+ private def mapRootAndExtractSubField[A <: Data](arg: A, f: Data => Data): A = {
+ def err(msg: String) = throwException(s"Internal Error! $msg")
+ def unrollCoordinates(res: List[Arg], d: Data): (List[Arg], Data) = d.binding.get match {
+ case ChildBinding(parent) => d.getRef match {
+ case arg @ (_: Slot | _: Index) => unrollCoordinates(arg :: res, parent)
+ case other => err(s"Unroll coordinates failed for '$arg'! Unexpected arg '$other'")
+ }
+ case _ => (res, d)
+ }
+ def applyCoordinates(fullCoor: List[Arg], start: Data): Data = {
+ def rec(coor: List[Arg], d: Data): Data = {
+ if (coor.isEmpty) d
+ else {
+ val next = (coor.head, d) match {
+ case (Slot(_, name), rec: Record) => rec.elements(name)
+ case (Index(_, ILit(n)), vec: Vec[_]) => vec.apply(n.toInt)
+ case (arg, _) => err(s"Unexpected Arg '$arg' applied to '$d'! Root was '$start'.")
+ }
+ applyCoordinates(coor.tail, next)
+ }
+ }
+ rec(fullCoor, start)
+ }
+ val (coor, root) = unrollCoordinates(Nil, arg)
+ val newRoot = f(root)
+ val result = applyCoordinates(coor, newRoot)
+ try {
+ result.asInstanceOf[A]
+ } catch {
+ case _: ClassCastException => err(s"Applying '$coor' to '$newRoot' somehow resulted in '$result'")
+ }
+ }
+
+ // TODO this logic is complicated, can any of it be unified with viewAs?
+ // If `.viewAs` would capture its arguments, we could potentially use it
+ // TODO Describe what this is doing at a high level
+ private[chisel3] def cloneViewToContext[A, B <: Data](data: B, cache: HashMap[Data, Data], ioMap: Option[Map[Data, Data]], context: Option[BaseModule])
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): B = {
+ // alias to shorten lookups
+ def lookupData[C <: Data](d: C) = doLookupData(d, cache, ioMap, context)
+
+ val result = data.cloneTypeFull
+
+ // We have to lookup the target(s) of the view since they may need to be cloned into the current context
+ val newBinding = data.topBinding match {
+ case ViewBinding(target) => ViewBinding(lookupData(reify(target)))
+ case avb @ AggregateViewBinding(map, targetOpt) => data match {
+ case _: Element => ViewBinding(lookupData(reify(map(data))))
+ case _: Aggregate =>
+ // Provide a 1:1 mapping if possible
+ val singleTargetOpt = targetOpt.filter(_ => avb == data.binding.get).flatMap(reifySingleData)
+ singleTargetOpt match {
+ case Some(singleTarget) => // It is 1:1!
+ // This is a little tricky because the values in newMap need to point to Elements of newTarget
+ val newTarget = lookupData(singleTarget)
+ val newMap = coiterate(result, data).map { case (res, from) =>
+ (res: Data) -> mapRootAndExtractSubField(map(from), _ => newTarget)
+ }.toMap
+ AggregateViewBinding(newMap, Some(newTarget))
+
+ case None => // No 1:1 mapping so we have to do a flat binding
+ // Just remap each Element of this aggregate
+ val newMap = coiterate(result, data).map {
+ // Upcast res to Data since Maps are invariant in the Key type parameter
+ case (res, from) => (res: Data) -> lookupData(reify(map(from)))
+ }.toMap
+ AggregateViewBinding(newMap, None)
+ }
+ }
+ }
+
+ // TODO Unify the following with `.viewAs`
+ // We must also mark non-1:1 and child Aggregates in the view for renaming
+ newBinding match {
+ case _: ViewBinding => // Do nothing
+ case AggregateViewBinding(_, target) =>
+ if (target.isEmpty) {
+ Builder.unnamedViews += result
+ }
+ // Binding does not capture 1:1 for child aggregates views
+ getRecursiveFields.lazily(result, "_").foreach {
+ case (agg: Aggregate, _) if agg != result =>
+ Builder.unnamedViews += agg
+ case _ => // Do nothing
+ }
+ }
+
+ result.bind(newBinding)
+ result.setAllParents(Some(ViewParent))
+ result.forceName(None, "view", Builder.viewNamespace)
+ result
+ }
+ /** Given a module (either original or a clone), clone it to a new context
+ *
+ * This function effectively recurses up the parents of module to find whether:
+ * (1) A parent is already in the context; then we do nothing and return module
+ * (2) A parent is in a different clone of the context; then we clone all the parents up
+ * to that parent and set their parents to be in this cloned context
+ * (3) A parent has no root; in that case, we do nothing and return the module.
+ *
+ * @param module original or clone to be cloned into a new context
+ * @param context new context
+ * @return original or clone in the new context
+ */
+ private[chisel3] def cloneModuleToContext[T <: BaseModule](module: Either[T, IsClone[T]], context: BaseModule)
+ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Either[T, IsClone[T]] = {
+ // Recursive call
+ def rec[A <: BaseModule](m: A): Either[A, IsClone[A]] = {
+ def clone(x: A, p: Option[BaseModule], name: () => String): Either[A, IsClone[A]] = {
+ val newChild = Module.do_apply(new internal.BaseModule.InstanceClone(x, name))
+ newChild._parent = p
+ Right(newChild)
+ }
+ (m, context) match {
+ case (c, ctx) if ctx == c => Left(c)
+ case (c, ctx: IsClone[_]) if ctx.isACloneOf(c) => Right(ctx.asInstanceOf[IsClone[A]])
+ case (c, ctx) if c._parent.isEmpty => Left(c)
+ case (_, _) =>
+ cloneModuleToContext(Left(m._parent.get), context) match {
+ case Left(p) => Left(m)
+ case Right(p: BaseModule) =>
+ clone(m, Some(p), () => m.instanceName)
+ }
+ }
+ }
+ module match {
+ case Left(m) => rec(m)
+ case Right(m: ModuleClone[_]) =>
+ rec(m) match {
+ case Left(mx) => Right(mx)
+ case Right(i: InstanceClone[_]) =>
+ val newChild = Module.do_apply(new InstanceClone(m._proto, () => m.instanceName))
+ newChild._parent = i._parent
+ Right(newChild)
+ }
+ case Right(m: InstanceClone[_]) =>
+ rec(m) match {
+ case Left(mx) => Right(mx)
+ case Right(i: InstanceClone[_]) =>
+ val newChild = Module.do_apply(new InstanceClone(m._proto, () => m.instanceName))
+ newChild._parent = i._parent
+ Right(newChild)
+ }
+ }
+ }
+
+ class SimpleLookupable[X] extends Lookupable[X] {
+ type B = X
+ type C = X
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C = that(definition.proto)
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C = that(instance.proto)
+ }
+
+ implicit def lookupInstance[B <: BaseModule](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new Lookupable[Instance[B]] {
+ type C = Instance[B]
+ def definitionLookup[A](that: A => Instance[B], definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ new Instance(cloneModuleToContext(ret.cloned, definition.getInnerDataContext.get))
+ }
+ def instanceLookup[A](that: A => Instance[B], instance: Instance[A]): C = {
+ val ret = that(instance.proto)
+ instance.cloned match {
+ // If instance is just a normal module, no changing of context is necessary
+ case Left(_) => new Instance(ret.cloned)
+ case Right(_) => new Instance(cloneModuleToContext(ret.cloned, instance.getInnerDataContext.get))
+ }
+ }
+ }
+
+ implicit def lookupModule[B <: BaseModule](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new Lookupable[B] {
+ type C = Instance[B]
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ new Instance(cloneModuleToContext(Left(ret), definition.getInnerDataContext.get))
+ }
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C = {
+ val ret = that(instance.proto)
+ instance.cloned match {
+ // If instance is just a normal module, no changing of context is necessary
+ case Left(_) => new Instance(Left(ret))
+ case Right(_) => new Instance(cloneModuleToContext(Left(ret), instance.getInnerDataContext.get))
+ }
+ }
+ }
+
+ implicit def lookupData[B <: Data](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new Lookupable[B] {
+ type C = B
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ if (isView(ret)) {
+ ??? // TODO!!!!!! cloneViewToContext(ret, instance, ioMap, instance.getInnerDataContext)
+ } else {
+ doLookupData(ret, definition.cache, None, definition.getInnerDataContext)
+ }
+ }
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C = {
+ val ret = that(instance.proto)
+ val ioMap: Option[Map[Data, Data]] = instance.cloned match {
+ case Right(x: ModuleClone[_]) => Some(x.ioMap)
+ case Left(x: BaseModule) => Some(x.getChiselPorts.map { case (_, data) => data -> data }.toMap)
+ case _ => None
+ }
+ if (isView(ret)) {
+ cloneViewToContext(ret, instance.cache, ioMap, instance.getInnerDataContext)
+ } else {
+ doLookupData(ret, instance.cache, ioMap, instance.getInnerDataContext)
+ }
+
+ }
+ }
+
+ import scala.language.higherKinds // Required to avoid warning for lookupIterable type parameter
+ implicit def lookupIterable[B, F[_] <: Iterable[_]](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions, lookupable: Lookupable[B]) = new Lookupable[F[B]] {
+ type C = F[lookupable.C]
+ def definitionLookup[A](that: A => F[B], definition: Definition[A]): C = {
+ val ret = that(definition.proto).asInstanceOf[Iterable[B]]
+ ret.map{ x: B => lookupable.definitionLookup[A](_ => x, definition) }.asInstanceOf[C]
+ }
+ def instanceLookup[A](that: A => F[B], instance: Instance[A]): C = {
+ import instance._
+ val ret = that(proto).asInstanceOf[Iterable[B]]
+ ret.map{ x: B => lookupable.instanceLookup[A](_ => x, instance) }.asInstanceOf[C]
+ }
+ }
+ implicit def lookupOption[B](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions, lookupable: Lookupable[B]) = new Lookupable[Option[B]] {
+ type C = Option[lookupable.C]
+ def definitionLookup[A](that: A => Option[B], definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ ret.map{ x: B => lookupable.definitionLookup[A](_ => x, definition) }
+ }
+ def instanceLookup[A](that: A => Option[B], instance: Instance[A]): C = {
+ import instance._
+ val ret = that(proto)
+ ret.map{ x: B => lookupable.instanceLookup[A](_ => x, instance) }
+ }
+ }
+ implicit def lookupIsInstantiable[B <: IsInstantiable](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new Lookupable[B] {
+ type C = Instance[B]
+ def definitionLookup[A](that: A => B, definition: Definition[A]): C = {
+ val ret = that(definition.proto)
+ val cloned = new InstantiableClone(ret)
+ cloned._parent = definition.getInnerDataContext
+ new Instance(Right(cloned))
+ }
+ def instanceLookup[A](that: A => B, instance: Instance[A]): C = {
+ val ret = that(instance.proto)
+ val cloned = new InstantiableClone(ret)
+ cloned._parent = instance.getInnerDataContext
+ new Instance(Right(cloned))
+ }
+ }
+
+ implicit def lookupIsLookupable[B <: IsLookupable](implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = new SimpleLookupable[B]()
+
+ implicit val lookupInt = new SimpleLookupable[Int]()
+ implicit val lookupByte = new SimpleLookupable[Byte]()
+ implicit val lookupShort = new SimpleLookupable[Short]()
+ implicit val lookupLong = new SimpleLookupable[Long]()
+ implicit val lookupFloat = new SimpleLookupable[Float]()
+ implicit val lookupChar = new SimpleLookupable[Char]()
+ implicit val lookupString = new SimpleLookupable[String]()
+ implicit val lookupBoolean = new SimpleLookupable[Boolean]()
+ implicit val lookupBigInt = new SimpleLookupable[BigInt]()
+}
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/package.scala b/core/src/main/scala/chisel3/experimental/hierarchy/package.scala
new file mode 100644
index 00000000..c309ab52
--- /dev/null
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/package.scala
@@ -0,0 +1,48 @@
+package chisel3.experimental
+
+package object hierarchy {
+
+ /** Classes or traits which will be used with the [[Definition]] + [[Instance]] api should be marked
+ * with the [[@instantiable]] annotation at the class/trait definition.
+ *
+ * @example {{{
+ * @instantiable
+ * class MyModule extends Module {
+ * ...
+ * }
+ *
+ * val d = Definition(new MyModule)
+ * val i0 = Instance(d)
+ * val i1 = Instance(d)
+ * }}}
+ */
+ class instantiable extends chisel3.internal.instantiable
+
+ /** Classes marked with [[@instantiable]] can have their vals marked with the [[@public]] annotation to
+ * enable accessing these values from a [[Definition]] or [[Instance]] of the class.
+ *
+ * Only vals of the the following types can be marked [[@public]]:
+ * 1. IsInstantiable
+ * 2. IsLookupable
+ * 3. Data
+ * 4. BaseModule
+ * 5. Iterable/Option containing a type that meets these requirements
+ * 6. Basic type like String, Int, BigInt etc.
+ *
+ * @example {{{
+ * @instantiable
+ * class MyModule extends Module {
+ * @public val in = IO(Input(UInt(3.W)))
+ * @public val out = IO(Output(UInt(3.W)))
+ * ..
+ * }
+ *
+ * val d = Definition(new MyModule)
+ * val i0 = Instance(d)
+ * val i1 = Instance(d)
+ *
+ * i1.in := i0.out
+ * }}}
+ */
+ class public extends chisel3.internal.public
+}
diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala
index ec3f2a79..8018159f 100644
--- a/core/src/main/scala/chisel3/experimental/package.scala
+++ b/core/src/main/scala/chisel3/experimental/package.scala
@@ -2,6 +2,9 @@
package chisel3
+import chisel3.internal.NamedComponent
+import chisel3.internal.sourceinfo.SourceInfo
+
/** Package for experimental features, which may have their API changed, be removed, etc.
*
* Because its contents won't necessarily have the same level of stability and support as
@@ -20,13 +23,6 @@ package object experimental {
type ChiselEnum = EnumFactory
- @deprecated("Use the version in chisel3._", "3.2")
- val withClockAndReset = chisel3.withClockAndReset
- @deprecated("Use the version in chisel3._", "3.2")
- val withClock = chisel3.withClock
- @deprecated("Use the version in chisel3._", "3.2")
- val withReset = chisel3.withReset
-
// Rocket Chip-style clonemodule
/** A record containing the results of CloneModuleAsRecord
@@ -131,14 +127,48 @@ package object experimental {
object BundleLiterals {
implicit class AddBundleLiteralConstructor[T <: Record](x: T) {
- def Lit(elems: (T => (Data, Data))*): T = {
+ def Lit(elems: (T => (Data, Data))*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
x._makeLit(elems: _*)
}
}
}
+ /** This class provides the `Lit` method needed to define a `Vec` literal
+ */
+ object VecLiterals {
+ implicit class AddVecLiteralConstructor[T <: Data](x: Vec[T]) {
+ /** Given a generator of a list tuples of the form [Int, Data]
+ * constructs a Vec literal, parallel concept to `BundleLiteral`
+ *
+ * @param elems tuples of an index and a literal value
+ * @return
+ */
+ def Lit(elems: (Int, T)*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = {
+ x._makeLit(elems: _*)
+ }
+ }
+
+ implicit class AddObjectLiteralConstructor(x: Vec.type) {
+ /** This provides an literal construction method for cases using
+ * object `Vec` as in `Vec.Lit(1.U, 2.U)`
+ */
+ def Lit[T <: Data](elems: T*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = {
+ require(elems.nonEmpty, s"Lit.Vec(...) must have at least one element")
+ val indexElements = elems.zipWithIndex.map { case (element, index) => (index, element)}
+ val widestElement = elems.maxBy(_.getWidth)
+ val vec: Vec[T] = Vec.apply(indexElements.length, chiselTypeOf(widestElement))
+ vec.Lit(indexElements:_*)
+ }
+ }
+ }
+
// Use to add a prefix to any component generated in input scope
val prefix = chisel3.internal.prefix
// Use to remove prefixes not in provided scope
val noPrefix = chisel3.internal.noPrefix
+
+ /** Base simulation-only component. */
+ abstract class BaseSim extends NamedComponent {
+ _parent.foreach(_.addId(this))
+ }
}
diff --git a/core/src/main/scala/chisel3/experimental/verification/package.scala b/core/src/main/scala/chisel3/experimental/verification/package.scala
index 5c71bd5f..190083fd 100644
--- a/core/src/main/scala/chisel3/experimental/verification/package.scala
+++ b/core/src/main/scala/chisel3/experimental/verification/package.scala
@@ -2,40 +2,59 @@
package chisel3.experimental
-import chisel3.{Bool, CompileOptions}
+import chisel3._
import chisel3.internal.Builder
-import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl.{Formal, Verification}
import chisel3.internal.sourceinfo.SourceInfo
package object verification {
+
object assert {
+ /** Named class for assertions. */
+ final class Assert(private[chisel3] val predicate: Bool) extends BaseSim
+
+
def apply(predicate: Bool, msg: String = "")(
implicit sourceInfo: SourceInfo,
- compileOptions: CompileOptions): Unit = {
- val clock = Builder.forcedClock
- pushCommand(Verification(Formal.Assert, sourceInfo, clock.ref,
- predicate.ref, msg))
+ compileOptions: CompileOptions): Assert = {
+ val a = new Assert(predicate)
+ when (!Module.reset.asBool) {
+ val clock = Module.clock
+ Builder.pushCommand(Verification(a, Formal.Assert, sourceInfo, clock.ref, predicate.ref, msg))
+ }
+ a
}
}
object assume {
+ /** Named class for assumes. */
+ final class Assume(private[chisel3] val predicate: Bool) extends BaseSim
+
def apply(predicate: Bool, msg: String = "")(
implicit sourceInfo: SourceInfo,
- compileOptions: CompileOptions): Unit = {
- val clock = Builder.forcedClock
- pushCommand(Verification(Formal.Assume, sourceInfo, clock.ref,
- predicate.ref, msg))
+ compileOptions: CompileOptions): Assume = {
+ val a = new Assume(predicate)
+ when (!Module.reset.asBool) {
+ val clock = Module.clock
+ Builder.pushCommand(Verification(a, Formal.Assume, sourceInfo, clock.ref, predicate.ref, msg))
+ }
+ a
}
}
object cover {
+ /** Named class for covers. */
+ final class Cover(private[chisel3] val predicate: Bool) extends BaseSim
+
def apply(predicate: Bool, msg: String = "")(
implicit sourceInfo: SourceInfo,
- compileOptions: CompileOptions): Unit = {
- val clock = Builder.forcedClock
- pushCommand(Verification(Formal.Cover, sourceInfo, clock.ref,
- predicate.ref, msg))
+ compileOptions: CompileOptions): Cover = {
+ val clock = Module.clock
+ val c = new Cover(predicate)
+ when (!Module.reset.asBool) {
+ Builder.pushCommand(Verification(c, Formal.Cover, sourceInfo, clock.ref, predicate.ref, msg))
+ }
+ c
}
}
}
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))
+
+}
diff --git a/core/src/main/scala/chisel3/package.scala b/core/src/main/scala/chisel3/package.scala
index d5a4bfae..64cfa8b9 100644
--- a/core/src/main/scala/chisel3/package.scala
+++ b/core/src/main/scala/chisel3/package.scala
@@ -207,9 +207,6 @@ package object chisel3 {
a.allElements
}
def getModulePorts(m: Module): Seq[Port] = m.getPorts
- // Invalidate API - a DontCare element for explicit assignment to outputs,
- // indicating the signal is intentionally not driven.
- val DontCare = chisel3.internal.InternalDontCare
class BindingException(message: String) extends ChiselException(message)
/** A function expected a Chisel type but got a hardware object