From e63ff302bc4f188eea313ed8c89a93e6f7923502 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Wed, 5 Jun 2024 13:05:08 -0700 Subject: [PATCH] Preserve literals across .asUInt --- core/src/main/scala/chisel3/Aggregate.scala | 10 +++- core/src/main/scala/chisel3/Bits.scala | 19 +++++- .../scala/chiselTests/BundleLiteralSpec.scala | 58 ++++++++++++++++++- src/test/scala/chiselTests/SIntOps.scala | 28 +++++++++ src/test/scala/chiselTests/UIntOps.scala | 4 ++ .../scala/chiselTests/VecLiteralSpec.scala | 35 ++++++++++- 6 files changed, 144 insertions(+), 10 deletions(-) diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index c3d849f7b7f..8e65dfe2071 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -105,8 +105,14 @@ sealed abstract class Aggregate extends Data { // This means we need the `first` argument so that we can preserve this behavior of Aggregates while still allowing subclasses // to override .asUInt behavior override private[chisel3] def _asUIntImpl(first: Boolean)(implicit sourceInfo: SourceInfo): UInt = { - val elts = this.getElements.map(_._asUIntImpl(false)) - if (elts.isEmpty && !first) 0.U(0.W) else SeqUtils.do_asUInt(elts) + checkingLitOption(checkForDontCares = false) match { + case Some(value) => + // Using UInt.Lit instead of .U so we can use Width argument which may be Unknown + UInt.Lit(value, this.width) + case None => + val elts = this.getElements.map(_._asUIntImpl(false)) + if (elts.isEmpty && !first) 0.U(0.W) else SeqUtils.do_asUInt(elts) + } } private[chisel3] override def connectFromBits( diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala index e2786f96794..2ec43937412 100644 --- a/core/src/main/scala/chisel3/Bits.scala +++ b/core/src/main/scala/chisel3/Bits.scala @@ -1023,9 +1023,22 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S override def do_>>(that: UInt)(implicit sourceInfo: SourceInfo): SInt = binop(sourceInfo, SInt(this.width), DynamicShiftRightOp, that) - override private[chisel3] def _asUIntImpl(first: Boolean)(implicit sourceInfo: SourceInfo): UInt = pushOp( - DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref) - ) + override private[chisel3] def _asUIntImpl(first: Boolean)(implicit sourceInfo: SourceInfo): UInt = + this.litOption match { + case Some(value) => + // This is a reinterpretation of raw bits + val posValue = + if (value.signum == -1) { + (BigInt(1) << this.width.get) + value + } else { + value + } + // Using UInt.Lit instead of .U so we can use Width argument which may be Unknown + UInt.Lit(posValue, this.width) + case None => + pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) + } + override def do_asSInt(implicit sourceInfo: SourceInfo): SInt = this private[chisel3] override def connectFromBits( diff --git a/src/test/scala/chiselTests/BundleLiteralSpec.scala b/src/test/scala/chiselTests/BundleLiteralSpec.scala index ee8cb4a21b0..9dd3cc360af 100644 --- a/src/test/scala/chiselTests/BundleLiteralSpec.scala +++ b/src/test/scala/chiselTests/BundleLiteralSpec.scala @@ -3,6 +3,7 @@ package chiselTests import chisel3._ +import chisel3.util.Cat import circt.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.experimental.BundleLiterals._ @@ -28,6 +29,15 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils { val c = UInt(16.W) } + class ChildBundle extends Bundle { + val foo = UInt(4.W) + } + class ComplexBundle(w: Int) extends Bundle { + val a = Vec(2, new ChildBundle) + val b = UInt(w.W) + val c = UInt(4.W) + } + "bundle literals" should "pack" in { assertTesterPasses { new BasicTester { @@ -351,7 +361,7 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils { } val (stdout, _, chirrtl) = grabStdOutErr(ChiselStage.emitCHIRRTL(new RawModule { val lit = (new SimpleBundle).Lit(_.a -> 0xde.U, _.b -> 0xad.U) - val x = lit.asUInt + val x = Cat(lit.a, lit.b) })) stdout should include("[W007] Literal value ULit(222,) is too wide for field _.a with width 4") stdout should include("[W007] Literal value ULit(173,) is too wide for field _.b with width 4") @@ -366,7 +376,7 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils { val chirrtl = ChiselStage.emitCHIRRTL( new RawModule { val lit = (new SimpleBundle).Lit(_.a -> 5.U, _.b -> 0.U) - val x = lit.asUInt + val x = Cat(lit.a, lit.b) }, args = Array("--warnings-as-errors") ) @@ -407,8 +417,50 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils { 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 + val cat = Cat(lit.a, lit.b) }) chirrtl should include("node cat = cat(UInt<4>(0h3), UInt<4>(0h3))") } + + "Calling .asUInt on a Bundle literal" should "return a UInt literal and work outside of elaboration" in { + val blit = (new MyBundle).Lit(_.a -> 42.U, _.b -> true.B, _.c -> MyEnum.sB) + val ulit = blit.asUInt + ulit.litOption should be(Some(171)) + + assertTesterPasses { + new BasicTester { + // Check that it gives the same value as the generated hardware + val wire = WireInit(blit).asUInt + chisel3.assert(ulit.litValue.U === wire) + stop() + } + } + } + + "Calling .asUInt on a Bundle literal with DontCare fields" should "NOT return a UInt literal" in { + ChiselStage.emitCHIRRTL(new RawModule { + val blit = (new MyBundle).Lit(_.a -> 42.U, _.c -> MyEnum.sB) + val ulit = blit.asUInt + ulit.litOption should be(None) + }) + } + + "Calling .asUInt on a Bundle literal with zero-width fields" should "return a UInt literal and work outside of elaboration" in { + import chisel3.experimental.VecLiterals._ + + val vlit = Vec.Lit((new ChildBundle).Lit(_.foo -> 0xa.U), (new ChildBundle).Lit(_.foo -> 0xb.U)) + val blit = (new ComplexBundle(0)).Lit(_.a -> vlit, _.b -> 0.U(0.W), _.c -> 0xc.U) + val ulit = blit.asUInt + ulit.litOption should be(Some(0xbac)) + + assertTesterPasses { + new BasicTester { + // Check that it gives the same value as the generated hardware + val wire = WireInit(blit).asUInt + chisel3.assert(ulit.litValue.U === wire) + stop() + } + } + } + } diff --git a/src/test/scala/chiselTests/SIntOps.scala b/src/test/scala/chiselTests/SIntOps.scala index 540d3607c03..b293de5ddcb 100644 --- a/src/test/scala/chiselTests/SIntOps.scala +++ b/src/test/scala/chiselTests/SIntOps.scala @@ -219,4 +219,32 @@ class SIntOpsSpec extends ChiselPropSpec with Utils with ShiftRightWidthBehavior val verilog = ChiselStage.emitSystemVerilog(new TestModule, args) verilog should include(" widthcheck = in[7];") } + + property("Calling .asUInt on an SInt literal should maintain the literal value") { + val s0 = 3.S + val u0 = s0.asUInt + u0.litValue should be(3) + + val s1 = -3.S + val u1 = s1.asUInt + u1.litValue should be(5) + + val s2 = -3.S(8.W) + val u2 = s2.asUInt + u2.litValue should be(0xfd) + + assertTesterPasses { + new BasicTester { + // Check that it gives the same value as the generated hardware + val wire0 = WireInit(s0).asUInt + chisel3.assert(u0.litValue.U === wire0) + val wire1 = WireInit(s1).asUInt + chisel3.assert(u1.litValue.U === wire1) + val wire2 = WireInit(s2).asUInt + chisel3.assert(u2.litValue.U === wire2) + + stop() + } + } + } } diff --git a/src/test/scala/chiselTests/UIntOps.scala b/src/test/scala/chiselTests/UIntOps.scala index b669ba20282..512ef4cf0b4 100644 --- a/src/test/scala/chiselTests/UIntOps.scala +++ b/src/test/scala/chiselTests/UIntOps.scala @@ -607,4 +607,8 @@ class UIntOpsSpec extends ChiselPropSpec with Matchers with Utils with ShiftRigh val e = the[IllegalArgumentException] thrownBy (UInt(-8.W)) e.getMessage should include("Widths must be non-negative, got -8") } + + property("Calling .asUInt on a UInt literal should maintain the literal value") { + 3.U.asUInt.litValue should be(3) + } } diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala index bd2d506c139..f0c84e11bd7 100644 --- a/src/test/scala/chiselTests/VecLiteralSpec.scala +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -3,6 +3,7 @@ package chiselTests import chisel3._ +import chisel3.util.Cat import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor import chisel3.experimental.VecLiterals._ import chisel3.experimental.VecLiteralException @@ -537,13 +538,43 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { 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 uint0 = Cat(lit0(1), lit0(0)) 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 + val uint1 = Cat(lit1(1), lit1(0)) }) chirrtl should include("node uint0 = cat(UInt<4>(0h2), UInt<4>(0h3))") chirrtl should include("node uint1 = cat(UInt<4>(0h2), UInt<4>(0h3))") } + + "Calling .asUInt on a Vec literal should return a UInt literal and work outside of elaboration" in { + val vlit0 = Vec(2, UInt(4.W)).Lit(0 -> 0x3.U, 1 -> 0x2.U(3.W)) + val ulit0 = vlit0.asUInt + ulit0.litOption should be(Some(0x23)) + + val vlit1 = Vec.Lit(0x3.U, 0x2.U(4.W)) + val ulit1 = vlit1.asUInt + ulit1.litOption should be(Some(0x23)) + + assertTesterPasses { + new BasicTester { + // Check that it gives the same value as the generated hardware + val wire0 = WireInit(vlit0).asUInt + chisel3.assert(ulit0.litValue.U === wire0) + val wire1 = WireInit(vlit1).asUInt + chisel3.assert(ulit1.litValue.U === wire1) + + stop() + } + } + } + + "Calling .asUInt on a Vec literal with DontCare fields should NOT return a UInt literal" in { + ChiselStage.emitCHIRRTL(new RawModule { + val vlit = Vec(2, UInt(4.W)).Lit(1 -> 0x2.U(3.W)) + val ulit = vlit.asUInt + ulit.litOption should be(None) + }) + } }