Skip to content

Commit

Permalink
Support literals in DataView (chipsalliance#3964)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackkoenig authored Apr 4, 2024
1 parent 5cd8ad0 commit ce05462
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 10 deletions.
24 changes: 16 additions & 8 deletions core/src/main/scala/chisel3/Aggregate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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
Expand All @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/scala/chisel3/Element.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/chisel3/internal/Builder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
71 changes: 71 additions & 0 deletions src/test/scala/chiselTests/experimental/DataView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit ce05462

Please sign in to comment.