Skip to content

Commit

Permalink
Preserve literals across .asUInt (#4148)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackkoenig authored Jun 6, 2024
1 parent 45dd82a commit d35daa2
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 10 deletions.
10 changes: 8 additions & 2 deletions core/src/main/scala/chisel3/Aggregate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
19 changes: 16 additions & 3 deletions core/src/main/scala/chisel3/Bits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
58 changes: 55 additions & 3 deletions src/test/scala/chiselTests/BundleLiteralSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package chiselTests

import chisel3._
import chisel3.util.Cat
import circt.stage.ChiselStage
import chisel3.testers.BasicTester
import chisel3.experimental.BundleLiterals._
Expand All @@ -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 {
Expand Down Expand Up @@ -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")
Expand All @@ -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")
)
Expand Down Expand Up @@ -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()
}
}
}

}
28 changes: 28 additions & 0 deletions src/test/scala/chiselTests/SIntOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
}
}
4 changes: 4 additions & 0 deletions src/test/scala/chiselTests/UIntOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
35 changes: 33 additions & 2 deletions src/test/scala/chiselTests/VecLiteralSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
})
}
}

0 comments on commit d35daa2

Please sign in to comment.