From ce05462c239deec8aee0704efbd3a71e8b64ec49 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Wed, 3 Apr 2024 21:18:00 -0700 Subject: [PATCH] Support literals in DataView (#3964) --- core/src/main/scala/chisel3/Aggregate.scala | 24 ++++--- core/src/main/scala/chisel3/Element.scala | 5 +- .../main/scala/chisel3/internal/Builder.scala | 2 +- .../chiselTests/experimental/DataView.scala | 71 +++++++++++++++++++ 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index 37f2fb7627f..6598cd40c40 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -3,7 +3,7 @@ package chisel3 import chisel3.experimental.VecLiterals.AddVecLiteralConstructor -import chisel3.experimental.dataview.{isView, reifySingleData, InvalidViewException} +import chisel3.experimental.dataview.{isView, reify, reifySingleData, InvalidViewException} import scala.collection.immutable.{SeqMap, VectorMap} import scala.collection.mutable.{HashSet, LinkedHashMap} @@ -29,7 +29,7 @@ sealed abstract class Aggregate extends Data { private def checkingLitOption(checkForDontCares: Boolean): 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] = { + def shiftAdd(elt: Data, accumulator: Option[BigInt]): Option[BigInt] = { (accumulator, elt.litOption) match { case (Some(accumulator), Some(eltLit)) => val width = elt.width.get @@ -44,24 +44,32 @@ sealed abstract class Aggregate extends Data { } topBindingOpt match { - case Some(BundleLitBinding(_)) | Some(VecLitBinding(_)) => - getElements.reverse - .foldLeft[Option[BigInt]](Some(BigInt(0)))(shiftAdd) + case Some(_: BundleLitBinding | _: VecLitBinding | _: AggregateViewBinding) => + // Records store elements in reverse order and higher indices are more significant in Vecs + this.getElements.foldRight(Option(BigInt(0)))(shiftAdd) case _ => None } } /** Return an Aggregate's literal value if it is a literal, None otherwise. - * If any element of the aggregate is not a literal with a defined width, the result isn't a literal. + * If any element of the aggregate is not a literal (or DontCare), the result isn't a literal. * - * @return an Aggregate's literal value if it is a literal. + * @note [[DontCare]] is allowed and will be replaced with 0. Use [[litValue]] to disallow DontCare. + * @return an Aggregate's literal value if it is a literal, None otherwise. */ override def litOption: Option[BigInt] = { checkingLitOption(checkForDontCares = false) } + /** Return an Aggregate's literal value if it is a literal, otherwise an exception is thrown. + * If any element of the aggregate is not a literal with a defined width, the result isn't a literal. + * + * @return an Aggregate's literal value if it is a literal, exception otherwise. + */ override def litValue: BigInt = { - checkingLitOption(checkForDontCares = true).get + checkingLitOption(checkForDontCares = true).getOrElse( + throw new ChiselException(s"Cannot ask for litValue of $this as it is not a literal.") + ) } /** Returns a Seq of the immediate contents of this Aggregate, in order. diff --git a/core/src/main/scala/chisel3/Element.scala b/core/src/main/scala/chisel3/Element.scala index 3da10164d04..52a7e170cc9 100644 --- a/core/src/main/scala/chisel3/Element.scala +++ b/core/src/main/scala/chisel3/Element.scala @@ -5,6 +5,7 @@ package chisel3 import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl.ir._ import chisel3.experimental.SourceInfo +import chisel3.experimental.dataview.reify import chisel3.internal._ /** Element is a leaf data type: it cannot contain other [[Data]] objects. Example uses are for representing primitive @@ -51,7 +52,9 @@ abstract class Element extends Data { private[chisel3] def litArgOption: Option[LitArg] = topBindingOpt match { case Some(ElementLitBinding(litArg)) => Some(litArg) - case _ => None + case Some(_: ViewBinding) => + reify(this).litArgOption + case _ => None } override def litOption: Option[BigInt] = litArgOption.map(_.num) diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index b78b7eafe19..b3f42fb2980 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -940,7 +940,7 @@ private[chisel3] object Builder extends LazyLogging { val localTarget = view.toTarget val absTarget = view.toAbsoluteTarget val elts = getRecursiveFields.lazily(view, "").collect { case (elt: Element, _) => elt } - for (elt <- elts) { + for (elt <- elts if !elt.isLit) { // This is a hack to not crash when .viewAs is called on non-hardware // It can be removed in Chisel 6.0.0 when it becomes illegal to call .viewAs on non-hardware val targetOfViewOpt = diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index ca2fe9d710a..fecb551661a 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -1050,6 +1050,77 @@ class DataViewSpec extends ChiselFlatSpec { ) } + it should "preserve literal values for Elements" in { + class MyModule extends Module { + val x = 123.U + val xv = x.viewAs[UInt] + xv.litOption should be(x.litOption) + xv.litValue should be(x.litValue) + val y = -23.S + val yv = y.viewAs[SInt] + yv.litOption should be(y.litOption) + yv.litValue should be(y.litValue) + } + ChiselStage.emitCHIRRTL(new MyModule) + } + + it should "preserve literal values for BundleLiterals" in { + import chisel3.experimental.BundleLiterals._ + class BundleA extends Bundle { + val foo = UInt(4.W) + val bar = UInt(4.W) + } + class BundleB extends Bundle { + val a = UInt(4.W) + val b = UInt(4.W) + val c = UInt(4.W) + } + implicit val dv = + DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.c, _.bar -> _.a, (_, b) => 6.U(4.W) -> b.b) + class MyModule extends Module { + val bunA = (new BundleA).Lit(_.foo -> 0xa.U, _.bar -> 0xd.U) + val bunAView = bunA.viewAs[BundleA] + bunA.litValue should be(0xad) + bunA.litOption should be(Some(0xad)) + bunAView.litValue should be(0xad) + bunAView.litOption should be(Some(0xad)) + + val bunBView = bunA.viewAs[BundleB] + bunBView.litValue should be(0xd6a) + bunBView.litOption should be(Some(0xd6a)) + } + ChiselStage.emitCHIRRTL(new MyModule) + } + + it should "preserve literal values for VecLiterals (viewed as nested Bundles)" in { + import chisel3.experimental.VecLiterals._ + case class Box(value: UInt) extends Bundle + class MyBundle extends Bundle { + val foo = Vec(2, UInt(4.W)) + val bar = Vec(2, Box(UInt(4.W))) + } + implicit val dv = DataView[Vec[UInt], MyBundle]( + _ => new MyBundle, + _(0) -> _.foo(0), + _(1) -> _.bar(1).value, + _(2) -> _.bar(0).value, + _(3) -> _.foo(1) + ) + class MyModule extends Module { + val vec = Vec.Lit(0xa.U, 0xb.U, 0xc.U, 0xd.U) + val vecView = vec.viewAs[Vec[UInt]] + vec.litValue should be(0xdcba) + vec.litOption should be(Some(0xdcba)) + vecView.litValue should be(0xdcba) + vecView.litOption should be(Some(0xdcba)) + + val bunView = vec.viewAs[MyBundle] + bunView.litValue should be(0xdabc) + bunView.litOption should be(Some(0xdabc)) + } + ChiselStage.emitCHIRRTL(new MyModule) + } + behavior.of("PartialDataView") it should "still error if the mapping is non-total in the view" in {