diff options
| -rw-r--r-- | core/src/main/scala/chisel3/Aggregate.scala | 274 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/Data.scala | 6 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/Element.scala | 4 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/experimental/package.scala | 33 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Binding.scala | 4 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/firrtl/IR.scala | 24 | ||||
| -rw-r--r-- | docs/src/appendix/experimental-features.md | 82 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/BundleLiteralSpec.scala | 23 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/ChiselSpec.scala | 25 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/VecLiteralSpec.scala | 526 |
10 files changed, 950 insertions, 51 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index df992bb9..c0b965b6 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -2,18 +2,19 @@ package chisel3 +import chisel3.experimental.VecLiterals.AddVecLiteralConstructor + import scala.collection.immutable.ListMap 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 @@ -51,16 +52,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) @@ -73,6 +77,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. @@ -83,11 +88,50 @@ sealed abstract class Aggregate extends Data { } } + // Returns pairs of all fields, element-level and containers, in a Record and their path names + private[chisel3] 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: Vec[_] => + data.getElements.zipWithIndex.map { case (fieldData, fieldIndex) => + getRecursiveFields(fieldData, path = s"$path($fieldIndex)") + }.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 + private[chisel3] 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)) { + _ ++ _ + } + case (x: Vec[_], y: Vec[_]) => + (x.getElements zip y.getElements).map { case (xElt, yElt) => + getMatchedFields(xElt, yElt) + }.fold(Seq(x -> y)) { + _ ++ _ + } + } + 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) { @@ -155,9 +199,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 { @@ -318,6 +371,154 @@ 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(ListMap(vecLitLinkedMap.toSeq:_*))) + clone + } } object VecInit extends SourceInfoDoc { @@ -528,25 +729,6 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio * }}} */ 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 @@ -572,9 +754,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") @@ -584,18 +772,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(", ") @@ -694,6 +897,7 @@ class AutoClonetypeException(message: String) extends ChiselException(message) package experimental { class BundleLiteralException(message: String) extends ChiselException(message) + class VecLiteralException(message: String) extends ChiselException(message) } diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 377a94e6..0241f248 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -392,6 +392,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. @@ -491,6 +492,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 diff --git a/core/src/main/scala/chisel3/Element.scala b/core/src/main/scala/chisel3/Element.scala index 0c99ff70..40291b12 100644 --- a/core/src/main/scala/chisel3/Element.scala +++ b/core/src/main/scala/chisel3/Element.scala @@ -30,6 +30,10 @@ 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 topBindingOpt => topBindingOpt } diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala index 4dc7ba4b..e8360430 100644 --- a/core/src/main/scala/chisel3/experimental/package.scala +++ b/core/src/main/scala/chisel3/experimental/package.scala @@ -2,6 +2,8 @@ package chisel3 +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 @@ -124,10 +126,39 @@ 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 diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala index 9e17aded..8a3c4330 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.ListMap + /** Requires that a node is hardware ("bound") */ object requireIsHardware { @@ -123,3 +125,5 @@ sealed trait LitBinding extends UnconstrainedBinding with ReadOnlyBinding 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: ListMap[Data, LitArg]) extends LitBinding diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala index 61f97ce6..81b4f7ab 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -91,6 +91,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 +114,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 +127,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 +139,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,6 +155,10 @@ 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 diff --git a/docs/src/appendix/experimental-features.md b/docs/src/appendix/experimental-features.md index 363f7a06..4b1208aa 100644 --- a/docs/src/appendix/experimental-features.md +++ b/docs/src/appendix/experimental-features.md @@ -85,7 +85,87 @@ class Example3 extends RawModule { chisel3.stage.ChiselStage.emitVerilog(new Example3) ``` -Vec literals are not yet supported. +### Vec Literals + +Vec literals are very similar to Bundle literals and can be constructed via an experimental import. +They can be constructed in two forms, with type and length inferred as in: + +```scala mdoc +import chisel3._ +import chisel3.experimental.VecLiterals._ + +class VecExample1 extends Module { + val out = IO(Output(Vec(2, UInt(4.W)))) + out := Vec.Lit(0xa.U, 0xbb.U) +} +``` +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new VecExample1) +``` + +or explicitly as in: + +```scala mdoc +import chisel3._ +import chisel3.experimental.VecLiterals._ + +class VecExample1a extends Module { + val out = IO(Output(Vec(2, UInt(4.W)))) + out := Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U) +} +``` + +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new VecExample1a) +``` + +The following examples all use the explicit form. +With the explicit form partial specification is allowed. +When used with as a `Reg` `reset` value, only specified indices of the `Reg`'s `Vec` +will be reset + +```scala mdoc +class VecExample2 extends RawModule { + val out = IO(Output(Vec(4, UInt(4.W)))) + out := Vec(4, UInt(4.W)).Lit(0 -> 1.U, 3 -> 7.U) +} +``` + +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new VecExample2) +``` + +Registers can be initialized from Vec literals + +```scala mdoc +class VecExample3 extends Module { + val out = IO(Output(Vec(4, UInt(8.W)))) + val y = RegInit( + Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + ) + out := y +} +``` + +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new VecExample3) +``` + +Vec literals can also be nested arbitrarily. + +```scala mdoc +class VecExample5 extends RawModule { + val out = IO(Output(Vec(2, new ChildBundle))) + out := Vec(2, new ChildBundle).Lit( + 0 -> (new ChildBundle).Lit(_.foo -> 42.U), + 1 -> (new ChildBundle).Lit(_.foo -> 7.U) + ) +} +``` + +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new VecExample5) +``` ### Interval Type <a name="interval-type"></a> diff --git a/src/test/scala/chiselTests/BundleLiteralSpec.scala b/src/test/scala/chiselTests/BundleLiteralSpec.scala index 2a3ce2c9..b4adde4a 100644 --- a/src/test/scala/chiselTests/BundleLiteralSpec.scala +++ b/src/test/scala/chiselTests/BundleLiteralSpec.scala @@ -6,9 +6,8 @@ import chisel3._ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.experimental.BundleLiterals._ -import chisel3.experimental.BundleLiteralException -import chisel3.experimental.ChiselEnum -import chisel3.experimental.FixedPoint +import chisel3.experimental.VecLiterals.AddVecLiteralConstructor +import chisel3.experimental.{BundleLiteralException, ChiselEnum, ChiselRange, FixedPoint, Interval} class BundleLiteralSpec extends ChiselFlatSpec with Utils { object MyEnum extends ChiselEnum { @@ -76,6 +75,24 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils { } } } + "bundle literals of vec literals" should "work" in { + assertTesterPasses(new BasicTester { + val range = range"[0,4].2" + val bundleWithVecs = new Bundle { + val a = Vec(2, UInt(4.W)) + val b = Vec(2, Interval(range)) + }.Lit( + _.a -> Vec(2, UInt(4.W)).Lit(0 -> 0xA.U, 1 -> 0xB.U), + _.b -> Vec(2, Interval(range)).Lit(0 -> (1.5).I(range), 1 -> (0.25).I(range)) + ) + chisel3.assert(bundleWithVecs.a(0) === 0xA.U) + chisel3.assert(bundleWithVecs.a(1) === 0xB.U) + chisel3.assert(bundleWithVecs.b(0) === (1.5).I(range)) + chisel3.assert(bundleWithVecs.b(1) === (0.25).I(range)) + stop() + }) + } + "partial bundle literals" should "work in RTL" in { assertTesterPasses{ new BasicTester{ val bundleLit = (new MyBundle).Lit(_.a -> 42.U) diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 8df680d6..9503089a 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -2,22 +2,22 @@ package chiselTests -import org.scalatest._ -import org.scalatest.prop._ -import org.scalatest.flatspec.AnyFlatSpec -import org.scalacheck._ import chisel3._ -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.aop.Aspect +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation} import chisel3.testers._ -import firrtl.{AnnotationSeq, CommonOptions, EmittedVerilogCircuitAnnotation, ExecutionOptionsManager, FirrtlExecutionFailure, FirrtlExecutionSuccess, HasFirrtlOptions} -import firrtl.annotations.{Annotation, DeletedAnnotation} +import firrtl.annotations.Annotation import firrtl.util.BackendCompilationUtilities +import firrtl.{AnnotationSeq, EmittedVerilogCircuitAnnotation} +import org.scalacheck._ +import org.scalatest._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.should._ +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + import java.io.ByteArrayOutputStream import java.security.Permission - -import chisel3.aop.Aspect -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation} -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scala.reflect.ClassTag /** Common utility functions for Chisel unit tests. */ @@ -90,6 +90,9 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { /** Spec base class for BDD-style testers. */ abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matchers +/** Spec base class for BDD-style testers. */ +abstract class ChiselFreeSpec extends AnyFreeSpec with ChiselRunners with Matchers + /** Spec base class for property-based testers. */ abstract class ChiselPropSpec extends PropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala new file mode 100644 index 00000000..d11289e1 --- /dev/null +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor +import chisel3.experimental.VecLiterals._ +import chisel3.experimental.{ChiselEnum, FixedPoint, VecLiteralException} +import chisel3.stage.ChiselStage +import chisel3.testers.BasicTester +import chisel3.util.Counter +import scala.language.reflectiveCalls + +class VecLiteralSpec extends ChiselFreeSpec with Utils { + object MyEnum extends ChiselEnum { + val sA, sB, sC = Value + } + object MyEnumB extends ChiselEnum { + val sA, sB = Value + } + + "Vec literals should work with chisel Enums" in { + val enumVec = Vec(3, MyEnum()).Lit(0 -> MyEnum.sA, 1 -> MyEnum.sB, 2-> MyEnum.sC) + enumVec(0).toString should include (MyEnum.sA.toString) + enumVec(1).toString should include (MyEnum.sB.toString) + enumVec(2).toString should include (MyEnum.sC.toString) + } + + "improperly constructed vec literals should be detected" - { + "indices in vec literal muse be greater than zero and less than length" in { + val e = intercept[VecLiteralException] { + Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 3 -> 4.U, -2 -> 7.U) + } + e.getMessage should include ( + "VecLiteral: The following indices (2,3,-2) are less than zero or greater or equal to than Vec length" + ) + } + + "indices in vec literals must not be repeated" in { + val e = intercept[VecLiteralException] { + Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 2 -> 3.U, 2 -> 3.U, 3 -> 4.U) + } + e.getMessage should include("VecLiteral: has duplicated indices 2(3 times)") + } + "lits must fit in vec element width" in { + val e = intercept[VecLiteralException] { + Vec(2, SInt(4.W)).Lit(0 -> 0xab.S, 1 -> 0xbc.S) + } + e.getMessage should include( + "VecLiteral: Vec[SInt<4>] has the following incorrectly typed or sized initializers: " + + "0 -> SInt<9>(171),1 -> SInt<9>(188)" + ) + } + + "all lits must be the same type but width can be equal or smaller than the Vec's element width" in { + val v = Vec(2, SInt(4.W)).Lit(0 -> 1.S, 1 -> -2.S) + v(0).toString should include(1.S(4.W).toString) + v(1).toString should include((-2).S(4.W).toString) + v.toString should include ("SInt<4>[2](0=SLit(1,<4>), 1=SLit(-2,<4>)") + } + + "all lits must be the same type but width cannot be greater than Vec's element width" in { + val e = intercept[VecLiteralException] { + val v = Vec(2, SInt(4.W)).Lit(0 -> 11.S, 1 -> -0xffff.S) + } + e.getMessage should include( + "VecLiteral: Vec[SInt<4>] has the following incorrectly typed or sized initializers: 0 -> SInt<5>(11),1 -> SInt<17>(-65535)" + ) + } + } + + //NOTE: I had problems where this would not work if this class declaration was inside test scope + class HasVecInit extends Module { + val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + val y = RegInit(initValue) + } + + "Vec literals should work when used to initialize a reg of vec" in { + val firrtl = (new ChiselStage).emitFirrtl(new HasVecInit, args = Array("--full-stacktrace")) + firrtl should include("""_y_WIRE[0] <= UInt<8>("hab")""") + firrtl should include("""_y_WIRE[1] <= UInt<8>("hcd")""") + firrtl should include("""_y_WIRE[2] <= UInt<8>("hef")""") + firrtl should include("""_y_WIRE[3] <= UInt<8>("hff")""") + firrtl should include(""" reset => (reset, _y_WIRE)""".stripMargin) + } + + //NOTE: I had problems where this would not work if this class declaration was inside test scope + class HasPartialVecInit extends Module { + val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + val y = RegInit(initValue) + } + + "Vec literals should work when used to partially initialize a reg of vec" in { + val firrtl = (new ChiselStage).emitFirrtl(new HasPartialVecInit, args = Array("--full-stacktrace")) + firrtl should include("""_y_WIRE[0] <= UInt<8>("hab")""") + firrtl should include("""_y_WIRE[1] is invalid""") + firrtl should include("""_y_WIRE[2] <= UInt<8>("hef")""") + firrtl should include("""_y_WIRE[3] <= UInt<8>("hff")""") + firrtl should include(""" reset => (reset, _y_WIRE)""".stripMargin) + } + + class ResetRegWithPartialVecLiteral extends Module { + val in = IO(Input(Vec(4, UInt(8.W)))) + val out = IO(Output(Vec(4, UInt(8.W)))) + val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + val y = RegInit(initValue) + when(in(1) > 0.U) { + y(1) := in(1) + } + when(in(2) > 0.U) { + y(2) := in(2) + } + out := y + } + + "Vec literals should only init specified fields when used to partially initialize a reg of vec" in { + println(ChiselStage.emitFirrtl(new ResetRegWithPartialVecLiteral)) + assertTesterPasses(new BasicTester { + val m = Module(new ResetRegWithPartialVecLiteral) + val (counter, wrapped) = Counter(true.B, 8) + m.in := DontCare + when(counter < 2.U) { + m.in(1) := 0xff.U + m.in(2) := 0xff.U + }.elsewhen(counter === 2.U) { + chisel3.assert(m.out(1) === 0xff.U) + chisel3.assert(m.out(2) === 0xff.U) + }.elsewhen(counter === 3.U) { + m.in(1) := 0.U + m.in(2) := 0.U + m.reset := true.B + }.elsewhen(counter > 2.U) { + // m.out(1) should not be reset, m.out(2) should be reset + chisel3.assert(m.out(1) === 0xff.U) + chisel3.assert(m.out(2) === 0xEF.U) + } + when(wrapped) { + stop() + } + }) + } + + "lowest of vec literal contains least significant bits and " in { + val y = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)) + y.litValue() should be(BigInt("FFEFCDAB", 16)) + } + + "the order lits are specified does not matter" in { + val y = Vec(4, UInt(8.W)).Lit(3 -> 0xFF.U(8.W), 2 -> 0xEF.U(8.W), 1 -> 0xCD.U(8.W), 0 -> 0xAB.U(8.W)) + y.litValue() should be(BigInt("FFEFCDAB", 16)) + } + + "regardless of the literals widths, packing should be done based on the width of the Vec's gen" in { + val z = Vec(4, UInt(8.W)).Lit(0 -> 0x2.U, 1 -> 0x2.U, 2 -> 0x2.U, 3 -> 0x3.U) + z.litValue() should be(BigInt("03020202", 16)) + } + + "packing sparse vec lits should not pack, litOption returns None" in { + // missing sub-listeral for index 2 + val z = Vec(4, UInt(8.W)).Lit(0 -> 0x2.U, 1 -> 0x2.U, 3 -> 0x3.U) + + z.litOption should be(None) + } + + "registers can be initialized with a Vec literal" in { + assertTesterPasses(new BasicTester { + val y = RegInit(Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))) + chisel3.assert(y.asUInt === BigInt("FFEFCDAB", 16).U) + stop() + }) + } + + "how does asUInt work" in { + assertTesterPasses(new BasicTester { + val vec1 = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + + val vec2 = VecInit(Seq(0xDD.U, 0xCC.U, 0xBB.U, 0xAA.U)) + printf("vec1 %x\n", vec1.asUInt()) + printf("vec2 %x\n", vec2.asUInt()) + stop() + }) + } + + "Vec literals uint conversion" in { + class M1 extends Module { + val out1 = IO(Output(UInt(64.W))) + val out2 = IO(Output(UInt(64.W))) + + val v1 = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + out1 := v1.asUInt + + val v2 = VecInit(0xDD.U(16.W), 0xCC.U, 0xBB.U, 0xAA.U) + out2 := v2.asUInt + } + + assertTesterPasses(new BasicTester { + val m = Module(new M1) + chisel3.assert(m.out1 === m.out2) + stop() + }) + } + + "VecLits should work properly with .asUInt" in { + val outsideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + + assertTesterPasses { + new BasicTester { + chisel3.assert(outsideVecLit(0) === 0xDD.U, s"v(0)") + stop() + } + } + } + + "bundle literals should work in RTL" in { + val outsideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + + assertTesterPasses { + new BasicTester { + chisel3.assert(outsideVecLit(0) === 0xDD.U, s"v(0)") + chisel3.assert(outsideVecLit(1) === 0xCC.U) + chisel3.assert(outsideVecLit(2) === 0xBB.U) + chisel3.assert(outsideVecLit(3) === 0xAA.U) + + chisel3.assert(outsideVecLit.litValue().U === outsideVecLit.asUInt()) + + val insideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U) + chisel3.assert(insideVecLit(0) === 0xDD.U) + chisel3.assert(insideVecLit(1) === 0xCC.U) + chisel3.assert(insideVecLit(2) === 0xBB.U) + chisel3.assert(insideVecLit(3) === 0xAA.U) + + chisel3.assert(insideVecLit(0) === outsideVecLit(0)) + chisel3.assert(insideVecLit(1) === outsideVecLit(1)) + chisel3.assert(insideVecLit(2) === outsideVecLit(2)) + chisel3.assert(insideVecLit(3) === outsideVecLit(3)) + + val vecWire1 = Wire(Vec(4, UInt(16.W))) + vecWire1 := outsideVecLit + + chisel3.assert(vecWire1(0) === 0xDD.U) + chisel3.assert(vecWire1(1) === 0xCC.U) + chisel3.assert(vecWire1(2) === 0xBB.U) + chisel3.assert(vecWire1(3) === 0xAA.U) + + val vecWire2 = Wire(Vec(4, UInt(16.W))) + vecWire2 := insideVecLit + + chisel3.assert(vecWire2(0) === 0xDD.U) + chisel3.assert(vecWire2(1) === 0xCC.U) + chisel3.assert(vecWire2(2) === 0xBB.U) + chisel3.assert(vecWire2(3) === 0xAA.U) + + stop() + } + } + } + + "partial vec literals should work in RTL" in { + assertTesterPasses{ new BasicTester{ + val vecLit = Vec(4, UInt(8.W)).Lit(0 -> 42.U, 2 -> 5.U) + chisel3.assert(vecLit(0) === 42.U) + chisel3.assert(vecLit(2) === 5.U) + + val vecWire = Wire(Vec(4, UInt(8.W))) + vecWire := vecLit + + chisel3.assert(vecWire(0) === 42.U) + chisel3.assert(vecWire(2) === 5.U) + + stop() + }} + } + + "nested vec literals should be constructable" in { + val outerVec = Vec(2, Vec(3, UInt(4.W))).Lit( + 0 -> Vec(3, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + 1 -> Vec(3, UInt(4.W)).Lit(0 -> 4.U, 1 -> 5.U, 2 -> 6.U) + ) + + outerVec.litValue() should be (BigInt("654321", 16)) + outerVec(0).litValue() should be (BigInt("321", 16)) + outerVec(1).litValue() should be (BigInt("654", 16)) + outerVec(0)(0).litValue() should be (BigInt(1)) + outerVec(0)(1).litValue() should be (BigInt(2)) + outerVec(0)(2).litValue() should be (BigInt(3)) + outerVec(1)(0).litValue() should be (BigInt(4)) + outerVec(1)(1).litValue() should be (BigInt(5)) + outerVec(1)(2).litValue() should be (BigInt(6)) + } + + "contained vecs should work" in { + assertTesterPasses{ new BasicTester { + val outerVec = Vec(2, Vec(3, UInt(4.W))).Lit( + 0 -> Vec(3, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + 1 -> Vec(3, UInt(4.W)).Lit(0 -> 4.U, 1 -> 5.U, 2 -> 6.U) + ) + + chisel3.assert(outerVec(0)(0) === 1.U) + chisel3.assert(outerVec(0)(1) === 2.U) + chisel3.assert(outerVec(0)(2) === 3.U) + chisel3.assert(outerVec(1)(0) === 4.U) + chisel3.assert(outerVec(1)(1) === 5.U) + chisel3.assert(outerVec(1)(2) === 6.U) + + val v0 = outerVec(0) + val v1 = outerVec(1) + chisel3.assert(v0(0) === 1.U) + chisel3.assert(v0(1) === 2.U) + chisel3.assert(v0(2) === 3.U) + chisel3.assert(v1(0) === 4.U) + chisel3.assert(v1(1) === 5.U) + chisel3.assert(v1(2) === 6.U) + + stop() + }} + } + + //TODO: decide what behavior here should be + "This doesn't work should it" ignore { + assertTesterPasses { + new BasicTester { + def vecFactory = Vec(2, FixedPoint(8.W, 4.BP)) + + val vecWire1 = Wire(Output(vecFactory)) + val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP)) + val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP)) + + vecWire1 := vecLit1 + vecWire1 := vecLit2 + printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt(), vecWire1(1).asUInt()) + chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP)) + chisel3.assert(vecWire1(1) === (3.25).F(8.W, 4.BP)) + stop() + } + } + } + + "partially initialized Vec literals should assign" in { + assertTesterPasses { + new BasicTester { + def vecFactory = Vec(2, FixedPoint(8.W, 4.BP)) + + val vecWire1 = Wire(Output(vecFactory)) + val vecWire2 = Wire(Output(vecFactory)) + val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP)) + val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP)) + + vecWire1 := vecLit1 + vecWire2 := vecLit2 + vecWire1(1) := (0.5).F(8.W, 4.BP) + printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt(), vecWire1(1).asUInt()) + chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP)) + chisel3.assert(vecWire1(1) === (0.5).F(8.W, 4.BP)) // Last connect won + chisel3.assert(vecWire2(1) === (3.25).F(8.W, 4.BP)) + stop() + } + } + } + + "Vec literals should work as register reset values" in { + assertTesterPasses { + new BasicTester { + val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 1 -> 0xB.U, 2 -> 0xC.U)) + r := (r.asUInt + 1.U).asTypeOf(Vec(3, UInt(11.W))) // prevent constprop + + // check reset values on first cycle out of reset + chisel3.assert(r(0) === 0xA.U) + chisel3.assert(r(1) === 0xB.U) + chisel3.assert(r(2) === 0xC.U) + stop() + } + } + } + + "partially initialized Vec literals should work as register reset values" in { + assertTesterPasses { + new BasicTester { + val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 2 -> 0xC.U)) + r := (r.asUInt + 1.U).asTypeOf(Vec(3, UInt(11.W))) // prevent constprop + // check reset values on first cycle out of reset + chisel3.assert(r(0) === 0xA.U) + chisel3.assert(r(2) === 0xC.U) + stop() + } + } + } + + "Fields extracted from Vec Literals should work as register reset values" in { + assertTesterPasses { + new BasicTester { + val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 2 -> 0xC.U).apply(0)) + r := r + 1.U // prevent const prop + chisel3.assert(r === 0xA.U) // coming out of reset + stop() + } + } + } + + "DontCare fields extracted from Vec Literals should work as register reset values" in { + assertTesterPasses { + new BasicTester { + val r = RegInit(Vec(3, Bool()).Lit(0 -> true.B).apply(2)) + r := reset.asBool + printf(p"r = $r\n") // Can't assert because reset value is DontCare + stop() + } + } + } + + "DontCare fields extracted from Vec Literals should work in other Expressions" in { + assertTesterPasses { + new BasicTester { + val x = Vec(3, Bool()).Lit(0 -> true.B).apply(2) || true.B + chisel3.assert(x === true.B) + stop() + } + } + } + + "vec literals with non-literal values should fail" in { + val exc = intercept[VecLiteralException] { + extractCause[VecLiteralException] { + ChiselStage.elaborate { + new RawModule { + (Vec(3, UInt(11.W)).Lit(0 -> UInt())) + } + } + } + } + exc.getMessage should include("field 0 specified with non-literal value UInt") + } + + "vec literals are instantiated on connect" in { + class VecExample5 extends RawModule { + val out = IO(Output(Vec(2, UInt(4.W)))) + val bundle = Vec(2, UInt(4.W)).Lit( + 0 -> 0xa.U, + 1 -> 0xb.U + ) + out := bundle + } + + val firrtl = (new chisel3.stage.ChiselStage).emitFirrtl(new VecExample5, args = Array("--full-stacktrace")) + firrtl should include("""out[0] <= UInt<4>("ha")""") + firrtl should include("""out[1] <= UInt<4>("hb")""") + } + + class SubBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(4.W) + } + + class VecExample extends RawModule { + val out = IO(Output(Vec(2, new SubBundle))) + val bundle = Vec(2, new SubBundle).Lit( + 0 -> (new SubBundle).Lit(_.foo -> 42.U, _.bar -> 22.U), + 1 -> (new SubBundle).Lit(_.foo -> 7.U, _.bar -> 3.U) + ) + out := bundle + } + + "vec literals can contain bundles" in { + val chirrtl = (new chisel3.stage.ChiselStage).emitChirrtl(new VecExample, args = Array("--full-stacktrace")) + chirrtl should include("""out[0].bar <= UInt<5>("h016")""") + chirrtl should include("""out[0].foo <= UInt<6>("h02a")""") + chirrtl should include("""out[1].bar <= UInt<2>("h03")""") + chirrtl should include("""out[1].foo <= UInt<3>("h07")""") + + } + + "vec literals can have bundle children" in { + val vec = Vec(2, new SubBundle).Lit( + 0 -> (new SubBundle).Lit(_.foo -> 0xab.U, _.bar -> 0xc.U), + 1 -> (new SubBundle).Lit(_.foo -> 0xde.U, _.bar -> 0xf.U) + ) + vec.litValue().toString(16) should be("defabc") + } + + "vec literals can have bundle children assembled incrementally" in { + val bundle1 = (new SubBundle).Lit(_.foo -> 0xab.U, _.bar -> 0xc.U) + val bundle2 = (new SubBundle).Lit(_.foo -> 0xde.U, _.bar -> 0xf.U) + + bundle1.litValue().toString(16) should be("abc") + bundle2.litValue().toString(16) should be("def") + + val vec = Vec(2, new SubBundle).Lit(0 -> bundle1, 1 -> bundle2) + + vec.litValue().toString(16) should be("defabc") + } + + "bundles can contain vec lits" in { + val vec1 = Vec(3, UInt(4.W)).Lit(0 -> 0xa.U, 1 -> 0xb.U, 2 -> 0xc.U) + val vec2 = Vec(2, UInt(4.W)).Lit(0 -> 0xd.U, 1 -> 0xe.U) + val bundle = (new Bundle { + val foo = Vec(3, UInt(4.W)) + val bar = Vec(2, UInt(4.W)) + }).Lit(_.foo -> vec1, _.bar -> vec2) + bundle.litValue().toString(16) should be("cbaed") + } + + "bundles can contain vec lits in-line" in { + val bundle = (new Bundle { + val foo = Vec(3, UInt(4.W)) + val bar = Vec(2, UInt(4.W)) + }).Lit( + _.foo -> Vec(3, UInt(4.W)).Lit(0 -> 0xa.U, 1 -> 0xb.U, 2 -> 0xc.U), + _.bar -> Vec(2, UInt(4.W)).Lit(0 -> 0xd.U, 1 -> 0xe.U) + ) + bundle.litValue().toString(16) should be("cbaed") + } + + "Vec.Lit is a trivial Vec literal factory" in { + val vec = Vec.Lit(0xa.U, 0xb.U) + vec(0).litValue() should be(0xa) + vec(1).litValue() should be(0xb) + } + + "Vec.Lit bases it's element width on the widest literal supplied" in { + val vec = Vec.Lit(0xa.U, 0xbbbb.U) + vec(0).litValue() should be(0xa) + vec(1).litValue() should be(0xbbbb) + vec.length should be(2) + vec.getWidth should be(16 * 2) + vec.litValue() should be(BigInt("bbbb000a", 16)) + } +} |
