From 72b9cdbd9e4fd798c57f423aa1ef0f4bb40cc9ef Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 23 May 2024 15:32:09 -0700 Subject: [PATCH] Fix widths for literal values in Bundle literals (#4082) * Fix widths for literal values in Bundle literals Previously, the user-specified (or unspecified minimum width) of the literal would be used in some operations like concatenation. * Implicitly truncate too-wide literals in Bundle literals This is to fix a bug in concatenation without breaking things relying on implicit truncation. This will be a warning in newer versions of Chisel. (cherry picked from commit 3939e570dd1b6855da30326b43754f5b237952d4) # Conflicts: # core/src/main/scala/chisel3/internal/firrtl/IR.scala # src/test/scala/chiselTests/ConnectableSpec.scala # src/test/scala/chiselTests/VecLiteralSpec.scala --- core/src/main/scala/chisel3/Aggregate.scala | 27 +- .../scala/chisel3/internal/firrtl/IR.scala | 422 ++++++++++++++++++ .../scala/chiselTests/BundleLiteralSpec.scala | 27 ++ .../scala/chiselTests/ConnectableSpec.scala | 22 + .../scala/chiselTests/VecLiteralSpec.scala | 24 + 5 files changed, 518 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index 9030e2c59d9..907f6c878a9 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -1080,10 +1080,10 @@ abstract class Record extends Aggregate { requireIsChiselType(this, "bundle literal constructor model") val clone = cloneType - val cloneFields = getRecursiveFields(clone, "(bundle root)").toMap + val cloneFields = getRecursiveFields(clone, "_").toMap // Create the Bundle literal binding from litargs of arguments - val bundleLitMap = elems.map { fn => fn(clone) }.flatMap { + val bundleLitMapping = elems.map { fn => fn(clone) }.flatMap { case (field, value) => val fieldName = cloneFields.getOrElse( field, @@ -1165,12 +1165,31 @@ abstract class Record extends Aggregate { } // 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 } + val duplicates = bundleLitMapping.map(_._1).groupBy(identity).collect { case (x, elts) if elts.size > 1 => x } if (!duplicates.isEmpty) { val duplicateNames = duplicates.map(cloneFields(_)).mkString(", ") throw new BundleLiteralException(s"duplicate fields $duplicateNames in Bundle literal constructor") } - clone.bind(BundleLitBinding(bundleLitMap.toMap)) + // Check widths and sign extend as appropriate. + val bundleLitMap = bundleLitMapping.view.map { + case (field, value) => + field.width match { + // If width is unknown, then it is set by the literal value. + case UnknownWidth() => field -> value + case width @ KnownWidth(widthValue) => + // TODO make this a warning then an error, but for older versions, just truncate. + val valuex = if (widthValue < value.width.get) { + // Mask the value to the width of the field. + val mask = (BigInt(1) << widthValue) - 1 + value.cloneWithValue(value.num & mask).cloneWithWidth(width) + } else if (widthValue > value.width.get) value.cloneWithWidth(width) + // Otherwise, ensure width is same as that of the field. + else value + + field -> valuex + } + }.toMap + clone.bind(BundleLitBinding(bundleLitMap)) clone } diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala index 64e15f5d384..d7ff68f4648 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -82,9 +82,431 @@ case class Node(id: HasId) extends Arg { case Some(arg) => arg.localName case None => id.instanceName } +<<<<<<< HEAD def name: String = id.getOptionRef match { case Some(arg) => arg.name case None => id.instanceName +======= + + sealed abstract class Arg { + 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 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 { + case Some(arg) => arg.name + case None => id.instanceName + } + } + + object Arg { + def earlyLocalName(id: HasId): String = earlyLocalName(id, true) + + def earlyLocalName(id: HasId, includeRoot: Boolean): String = id.getOptionRef match { + case Some(Index(Node(imm), Node(value))) => + s"${earlyLocalName(imm, includeRoot)}[${earlyLocalName(imm, includeRoot)}]" + case Some(Index(Node(imm), arg)) => s"${earlyLocalName(imm, includeRoot)}[${arg.localName}]" + case Some(Slot(Node(imm), name)) => s"${earlyLocalName(imm, includeRoot)}.$name" + case Some(OpaqueSlot(Node(imm))) => s"${earlyLocalName(imm, includeRoot)}" + case Some(arg) if includeRoot => arg.name + case None if includeRoot => + id match { + case data: Data => data._computeName(Some("?")).get + case obj: DynamicObject => obj._computeName(Some("?")).get + case _ => "?" + } + case _ => "_" // Used when includeRoot == false + } + } + + abstract class LitArg(val num: BigInt, widthArg: Width) extends Arg { + def forcedWidth = widthArg.known + def width: Width = if (forcedWidth) widthArg else Width(minWidth) + 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)) + elem.setRef(this) + elem + } + + /** Provides a mechanism that LitArgs can have their width adjusted. + * + * @param newWidth the new width for this + * @return + */ + def cloneWithWidth(newWidth: Width): this.type + + /** Provides a mechanism that LitArgs can have their value adjusted. + * + * @param newWidth the new width for this + * @return + */ + def cloneWithValue(newValue: BigInt): this.type + + protected def minWidth: Int + if (forcedWidth) { + require( + widthArg.get >= minWidth, + s"The literal value ${num} was elaborated with a specified width of ${widthArg.get} bits, but at least ${minWidth} bits are required." + ) + } + } + + case class ILit(n: BigInt) extends Arg { + def name: String = n.toString + } + + case class ULit(n: BigInt, w: Width) extends LitArg(n, w) { + def name: String = "UInt" + width + "(0h0" + num.toString(16) + ")" + def minWidth: Int = (if (w.known) 0 else 1).max(n.bitLength) + + def cloneWithWidth(newWidth: Width): this.type = { + ULit(n, newWidth).asInstanceOf[this.type] + } + + def cloneWithValue(newValue: BigInt): this.type = ULit(newValue, w).asInstanceOf[this.type] + + require(n >= 0, s"UInt literal ${n} is negative") + } + + case class SLit(n: BigInt, w: Width) extends LitArg(n, w) { + def name: String = { + val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n + s"asSInt(${ULit(unsigned, width).name})" + } + def minWidth: Int = (if (w.known) 0 else 1) + n.bitLength + + def cloneWithWidth(newWidth: Width): this.type = { + SLit(n, newWidth).asInstanceOf[this.type] + } + + def cloneWithValue(newValue: BigInt): this.type = SLit(newValue, w).asInstanceOf[this.type] + } + + /** Literal property value. + * + * These are not LitArgs, because not all property literals are integers. + */ + case class PropertyLit[T, U]( + propertyType: PropertyTypeclass[_] { type Underlying = U; type Type = T }, + lit: U) + extends Arg { + def name: String = s"PropertyLit($lit)" + def minWidth: Int = 0 + def cloneWithWidth(newWidth: Width): this.type = PropertyLit(propertyType, lit).asInstanceOf[this.type] + + /** Expose a bindLitArg API for PropertyLit, similar to LitArg. + */ + def bindLitArg(elem: Property[T]): Property[T] = { + elem.bind(PropertyValueBinding) + elem.setRef(this) + elem + } + } + + /** Property expressions. + * + * Property expressions are conceptually similar to Nodes, but only exist as a tree of Args in-memory. + */ + case class PropExpr(sourceInfo: SourceInfo, tpe: firrtlir.PropertyType, op: firrtlir.PropPrimOp, args: List[Arg]) + extends Arg { + // PropExpr is different from other Args, because this is only used as an internal data structure, and we never name + // the Arg or use the name in textual FIRRTL. This is always expected to be the exp of a PropAssign, and it would be + // an internal error to request the name. + def name: String = throwException("Internal Error! PropExpr has no name") + } + + 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 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: Arg, name: String) extends Arg { + 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 OpaqueSlot(imm: Node) extends Arg { + override def contextualName(ctx: Component): String = imm.contextualName(ctx) + override def name: String = imm.name + } + + case class Index(imm: Arg, value: Arg) extends Arg { + def name: String = s"[$value]" + override def contextualName(ctx: Component): String = s"${imm.contextualName(ctx)}[${value.contextualName(ctx)}]" + override def localName: String = s"${imm.localName}[${value.localName}]" + } + + sealed trait ProbeDetails { this: Arg => + val probe: Arg + override def name: String = s"$probe" + } + case class ProbeExpr(probe: Arg) extends Arg with ProbeDetails + case class RWProbeExpr(probe: Arg) extends Arg with ProbeDetails + case class ProbeRead(probe: Arg) extends Arg with ProbeDetails + + sealed abstract class MemPortDirection(name: String) { + override def toString: String = name + } + object MemPortDirection { + object READ extends MemPortDirection("read") + object WRITE extends MemPortDirection("write") + object RDWR extends MemPortDirection("rdwr") + object INFER extends MemPortDirection("infer") + } + + abstract class Command { + def sourceInfo: SourceInfo + } + + abstract class Definition extends Command { + def id: HasId + def name: String = id.getRef.name + } + + case class DefPrim[T <: Data](sourceInfo: SourceInfo, id: T, op: PrimOp, args: Arg*) extends Definition + + case class DefInvalid(sourceInfo: SourceInfo, arg: Arg) extends Command + + case class DefWire(sourceInfo: SourceInfo, id: Data) extends Definition + + case class DefReg(sourceInfo: SourceInfo, id: Data, clock: Arg) extends Definition + + case class DefRegInit(sourceInfo: SourceInfo, id: Data, clock: Arg, reset: Arg, init: Arg) extends Definition + + case class DefMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: BigInt) extends Definition + + case class DefSeqMemory( + sourceInfo: SourceInfo, + id: HasId, + t: Data, + size: BigInt, + readUnderWrite: fir.ReadUnderWrite.Value) + extends Definition + + case class FirrtlMemory( + sourceInfo: SourceInfo, + id: HasId, + t: Data, + size: BigInt, + readPortNames: Seq[String], + writePortNames: Seq[String], + readwritePortNames: Seq[String]) + extends Definition + + case class DefMemPort[T <: Data]( + sourceInfo: SourceInfo, + id: T, + source: Node, + dir: MemPortDirection, + index: Arg, + clock: Arg) + extends Definition + + case class DefInstance(sourceInfo: SourceInfo, id: BaseModule, ports: Seq[Port]) extends Definition + case class DefInstanceChoice( + sourceInfo: SourceInfo, + id: HasId, + default: BaseModule, + option: String, + choices: Seq[(String, BaseModule)]) + extends Definition + case class DefObject(sourceInfo: SourceInfo, id: HasId, className: String) extends Definition + case class WhenBegin(sourceInfo: SourceInfo, pred: Arg) extends Command + case class WhenEnd(sourceInfo: SourceInfo, firrtlDepth: Int, hasAlt: Boolean = false) extends Command + case class AltBegin(sourceInfo: SourceInfo) extends Command + case class OtherwiseEnd(sourceInfo: SourceInfo, firrtlDepth: Int) extends Command + case class Connect(sourceInfo: SourceInfo, loc: Arg, exp: Arg) extends Command + case class PropAssign(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command + case class Attach(sourceInfo: SourceInfo, locs: Seq[Node]) extends Command + case class ConnectInit(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command + case class Stop(id: stop.Stop, sourceInfo: SourceInfo, clock: Arg, ret: Int) extends Definition + + object LayerConvention { + sealed trait Type + case object Bind extends Type + } + + case class Layer( + sourceInfo: SourceInfo, + name: String, + convention: LayerConvention.Type, + children: Seq[Layer]) + + case class LayerBlockBegin(sourceInfo: SourceInfo, layer: chisel3.layer.Layer) extends Command + case class LayerBlockEnd(sourceInfo: SourceInfo) extends Command + + case class DefOption( + sourceInfo: SourceInfo, + name: String, + cases: Seq[DefOptionCase]) + case class DefOptionCase(sourceInfo: SourceInfo, name: String) + + case class Port(id: Data, dir: SpecifiedDirection, sourceInfo: SourceInfo) + + case class Printf(id: printf.Printf, sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Definition + + case class ProbeDefine(sourceInfo: SourceInfo, sink: Arg, probe: Arg) extends Command + case class ProbeForceInitial(sourceInfo: SourceInfo, probe: Arg, value: Arg) extends Command + case class ProbeReleaseInitial(sourceInfo: SourceInfo, probe: Arg) extends Command + case class ProbeForce(sourceInfo: SourceInfo, clock: Arg, cond: Arg, probe: Arg, value: Arg) extends Command + case class ProbeRelease(sourceInfo: SourceInfo, clock: Arg, cond: Arg, probe: Arg) extends Command + + object Formal extends Enumeration { + val Assert = Value("assert") + val Assume = Value("assume") + val Cover = Value("cover") + } + + case class Verification[T <: VerificationStatement]( + id: T, + op: Formal.Value, + sourceInfo: SourceInfo, + clock: Arg, + predicate: Arg, + pable: Printable) + extends Definition + + abstract class Component extends Arg { + def id: BaseModule + def name: String + def ports: Seq[Port] + val secretPorts: mutable.ArrayBuffer[Port] = id.secretPorts + } + + case class DefTypeAlias(sourceInfo: SourceInfo, underlying: fir.Type, val name: String) + + case class DefModule( + id: RawModule, + name: String, + isPublic: Boolean, + layers: Seq[chisel3.layer.Layer], + ports: Seq[Port], + commands: Seq[Command]) + extends Component { + val secretCommands: mutable.ArrayBuffer[Command] = mutable.ArrayBuffer[Command]() + } + + case class DefBlackBox( + id: BaseBlackBox, + name: String, + ports: Seq[Port], + topDir: SpecifiedDirection, + params: Map[String, Param]) + extends Component + + case class DefIntrinsicModule( + id: BaseIntrinsicModule, + name: String, + ports: Seq[Port], + topDir: SpecifiedDirection, + params: Map[String, Param]) + extends Component + + case class DefIntrinsicExpr[T <: Data]( + sourceInfo: SourceInfo, + intrinsic: String, + id: T, + args: Seq[Arg], + params: Seq[(String, Param)]) + extends Definition + + case class DefIntrinsic(sourceInfo: SourceInfo, intrinsic: String, args: Seq[Arg], params: Seq[(String, Param)]) + extends Command + + case class DefClass(id: Class, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component + + case class Circuit( + name: String, + components: Seq[Component], + annotations: Seq[ChiselAnnotation], + renames: RenameMap, + newAnnotations: Seq[ChiselMultiAnnotation], + typeAliases: Seq[DefTypeAlias], + layers: Seq[Layer], + options: Seq[DefOption]) { + + def this( + name: String, + components: Seq[Component], + annotations: Seq[ChiselAnnotation], + renames: RenameMap, + typeAliases: Seq[DefTypeAlias], + layers: Seq[Layer], + options: Seq[DefOption] + ) = + this(name, components, annotations, renames, Seq.empty, typeAliases, layers, options) + + def firrtlAnnotations: Iterable[Annotation] = + annotations.flatMap(_.toFirrtl.update(renames)) ++ newAnnotations.flatMap( + _.toFirrtl.flatMap(_.update(renames)) + ) + + def copy( + name: String = name, + components: Seq[Component] = components, + annotations: Seq[ChiselAnnotation] = annotations, + renames: RenameMap = renames, + typeAliases: Seq[DefTypeAlias] = typeAliases, + layers: Seq[Layer] = layers, + options: Seq[DefOption] = options + ) = Circuit(name, components, annotations, renames, newAnnotations, typeAliases, layers, options) + + } + + object Circuit + extends scala.runtime.AbstractFunction7[String, Seq[Component], Seq[ChiselAnnotation], RenameMap, Seq[ + DefTypeAlias + ], Seq[Layer], Seq[DefOption], Circuit] { + def unapply(c: Circuit): Option[(String, Seq[Component], Seq[ChiselAnnotation], RenameMap, Seq[DefTypeAlias])] = { + Some((c.name, c.components, c.annotations, c.renames, c.typeAliases)) + } + + def apply( + name: String, + components: Seq[Component], + annotations: Seq[ChiselAnnotation], + renames: RenameMap, + typeAliases: Seq[DefTypeAlias] = Seq.empty, + layers: Seq[Layer] = Seq.empty, + options: Seq[DefOption] = Seq.empty + ): Circuit = + new Circuit(name, components, annotations, renames, typeAliases, layers, options) +>>>>>>> 3939e570d (Fix widths for literal values in Bundle literals (#4082)) } } diff --git a/src/test/scala/chiselTests/BundleLiteralSpec.scala b/src/test/scala/chiselTests/BundleLiteralSpec.scala index 8118c2bf9f4..fa9dff22ced 100644 --- a/src/test/scala/chiselTests/BundleLiteralSpec.scala +++ b/src/test/scala/chiselTests/BundleLiteralSpec.scala @@ -344,6 +344,18 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils { exc.getMessage should include(".c") } + "bundle literals with too-wide of literal values" should "truncate" in { + class SimpleBundle extends Bundle { + val a = UInt(4.W) + val b = UInt(4.W) + } + val chirrtl = ChiselStage.emitCHIRRTL(new RawModule { + val lit = (new SimpleBundle).Lit(_.a -> 0xde.U, _.b -> 0xad.U) + val x = lit.asUInt + }) + chirrtl should include("node x = cat(UInt<4>(0he), UInt<4>(0hd))") + } + "partial bundle literals" should "fail to pack" in { ChiselStage.emitCHIRRTL { new RawModule { @@ -359,4 +371,19 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils { lit.litOption should equal(Some(0)) }) } + + "bundle literals" should "use the widths of the Bundle fields rather than the widths of the literals" in { + class SimpleBundle extends Bundle { + val a = UInt(4.W) + val b = UInt(4.W) + } + val chirrtl = ChiselStage.emitCHIRRTL(new RawModule { + // Whether the user specifies a width or not. + val lit = (new SimpleBundle).Lit(_.a -> 0x3.U, _.b -> 0x3.U(3.W)) + lit.a.getWidth should be(4) + lit.b.getWidth should be(4) + val cat = lit.asUInt + }) + chirrtl should include("node cat = cat(UInt<4>(0h3), UInt<4>(0h3))") + } } diff --git a/src/test/scala/chiselTests/ConnectableSpec.scala b/src/test/scala/chiselTests/ConnectableSpec.scala index 1afb9a9f145..ea35eeca44b 100644 --- a/src/test/scala/chiselTests/ConnectableSpec.scala +++ b/src/test/scala/chiselTests/ConnectableSpec.scala @@ -1048,10 +1048,17 @@ class ConnectableSpec extends ChiselFunSpec with Utils { testCheck( ChiselStage.emitCHIRRTL({ new MyModule() }, args = Array("--full-stacktrace", "--throw-on-first-error")), Seq( +<<<<<<< HEAD "out.valid <= in.valid", "in.ready <= out.ready", "out.data.b <= in.data.b", "out.data.c <= UInt<1>(\"h1\")" +======= + "connect out.valid, in.valid", + "connect in.ready, out.ready", + "connect out.data.b, in.data.b", + "connect out.data.c, UInt<2>(0h1)" +>>>>>>> 3939e570d (Fix widths for literal values in Bundle literals (#4082)) ), Nil ) @@ -1526,12 +1533,21 @@ class ConnectableSpec extends ChiselFunSpec with Utils { out, Seq( """wire w0 : { foo : UInt<3>, flip bar : UInt<3>}""", +<<<<<<< HEAD """w0.bar <= UInt<1>("h1")""", """w0.foo <= UInt<1>("h0")""", """wire w1 : { foo : UInt<3>, flip bar : UInt<3>}""", """w1.bar <= UInt<1>("h1")""", """w1.foo <= UInt<1>("h0")""", """w1 <= w0""" +======= + """connect w0.bar, UInt<3>(0h1)""", + """connect w0.foo, UInt<3>(0h0)""", + """wire w1 : { foo : UInt<3>, flip bar : UInt<3>}""", + """connect w1.bar, UInt<3>(0h1)""", + """connect w1.foo, UInt<3>(0h0)""", + """connect w1, w0""" +>>>>>>> 3939e570d (Fix widths for literal values in Bundle literals (#4082)) ), Nil ) @@ -1616,9 +1632,15 @@ class ConnectableSpec extends ChiselFunSpec with Utils { testCheck( out, Seq( +<<<<<<< HEAD """out.data <= UInt<1>("h0")""", """in.ready <= out.ready""", """out.valid <= in.valid""" +======= + """connect out.data, UInt<32>(0h0)""", + """connect in.ready, out.ready""", + """connect out.valid, in.valid""" +>>>>>>> 3939e570d (Fix widths for literal values in Bundle literals (#4082)) ), Nil ) diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala index 58d7d240def..6d3f0fcefa9 100644 --- a/src/test/scala/chiselTests/VecLiteralSpec.scala +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -436,6 +436,7 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { class VecExample extends RawModule { val out = IO(Output(Vec(2, new SubBundle))) + // Note that 22.U is too wide for bar so gets truncated below. 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) @@ -445,10 +446,17 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { "vec literals can contain bundles and should not be bulk connected" in { val chirrtl = ChiselStage.emitCHIRRTL(new VecExample) +<<<<<<< HEAD chirrtl should include("""out[0].bar <= UInt<5>("h16")""") chirrtl should include("""out[0].foo <= UInt<6>("h2a")""") chirrtl should include("""out[1].bar <= UInt<2>("h3")""") chirrtl should include("""out[1].foo <= UInt<3>("h7")""") +======= + chirrtl should include("""connect out[0].bar, UInt<4>(0h6)""") + chirrtl should include("""connect out[0].foo, UInt<8>(0h2a)""") + chirrtl should include("""connect out[1].bar, UInt<4>(0h3)""") + chirrtl should include("""connect out[1].foo, UInt<8>(0h7)""") +>>>>>>> 3939e570d (Fix widths for literal values in Bundle literals (#4082)) } "vec literals can have bundle children" in { @@ -522,4 +530,20 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { lit.litOption should equal(Some(0)) }) } + + "Vec literals should use the width of the Vec element rather than the widths of the literals" in { + val chirrtl = ChiselStage.emitCHIRRTL(new RawModule { + // Whether the user specifies a width or not. + val lit0 = (Vec(2, UInt(4.W))).Lit(0 -> 0x3.U, 1 -> 0x2.U(3.W)) + lit0(0).getWidth should be(4) + lit0(1).getWidth should be(4) + val uint0 = lit0.asUInt + val lit1 = Vec.Lit(0x3.U, 0x2.U(4.W)) + lit1(0).getWidth should be(4) + lit1(1).getWidth should be(4) + val uint1 = lit1.asUInt + }) + chirrtl should include("node uint0 = cat(UInt<4>(0h2), UInt<4>(0h3))") + chirrtl should include("node uint1 = cat(UInt<4>(0h2), UInt<4>(0h3))") + } }