From a0ac3fd2c3f5b3529c62f85af9cfc6d5646028c3 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 22 Feb 2024 22:58:59 -0800 Subject: [PATCH] Add DataProduct for Iterables and primitive types (#3856) --- .../experimental/dataview/DataProduct.scala | 35 ++++++++++++++++- .../hierarchy/core/Lookupable.scala | 1 + .../chiselTests/experimental/DataView.scala | 39 +++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala b/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala index e87698f032d..b07bdbf706d 100644 --- a/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala +++ b/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala @@ -46,7 +46,7 @@ trait DataProduct[-A] { /** Low priority built-in implementations of [[DataProduct]] * - * @note This trait exists so that `dataDataProduct` can be lower priority than `seqDataProduct` to resolve ambiguity + * @note This trait exists so that `dataDataProduct` can be lower priority than `iterableDataProduct` to resolve ambiguity */ sealed trait LowPriorityDataProduct { @@ -63,6 +63,25 @@ sealed trait LowPriorityDataProduct { */ object DataProduct extends LowPriorityDataProduct { + /** Factory method for constructing [[DataProduct]]s */ + def apply[A](f: (A, String) => Iterator[(Data, String)]): DataProduct[A] = new DataProduct[A] { + def dataIterator(a: A, path: String): Iterator[(Data, String)] = f(a, path) + } + + /** Factory for constructing [[DataProduct]] for types that do not contain any [[Data]] */ + def empty[A]: DataProduct[A] = apply[A] { case _ => Iterator.empty } + + // DataProducts for simple primitive types + implicit val charDataProduct: DataProduct[Char] = empty + implicit val stringDataProduct: DataProduct[String] = empty + implicit val intDataProduct: DataProduct[Int] = empty + implicit val byteDataProduct: DataProduct[Byte] = empty + implicit val shortDataProduct: DataProduct[Short] = empty + implicit val longDataProduct: DataProduct[Long] = empty + implicit val bigIntDataProduct: DataProduct[BigInt] = empty + implicit val floatDataProduct: DataProduct[Float] = empty + implicit val doubleDataProduct: DataProduct[Double] = empty + /** [[DataProduct]] implementation for [[BaseModule]] */ implicit val userModuleDataProduct: DataProduct[BaseModule] = new DataProduct[BaseModule] { def dataIterator(a: BaseModule, path: String): Iterator[(Data, String)] = { @@ -82,7 +101,8 @@ object DataProduct extends LowPriorityDataProduct { } /** [[DataProduct]] implementation for any `Seq[A]` where `A` has an implementation of `DataProduct`. */ - implicit def seqDataProduct[A: DataProduct]: DataProduct[Seq[A]] = new DataProduct[Seq[A]] { + @deprecated("Use iterableDataProduct instead", "Chisel 7.0") + def seqDataProduct[A: DataProduct]: DataProduct[Seq[A]] = new DataProduct[Seq[A]] { def dataIterator(a: Seq[A], path: String): Iterator[(Data, String)] = { val dpa = implicitly[DataProduct[A]] a.iterator.zipWithIndex.flatMap { @@ -92,6 +112,17 @@ object DataProduct extends LowPriorityDataProduct { } } + /** [[DataProduct]] implementation for any `Iterable[A]` where `A` has an implementation of `DataProduct`. */ + implicit def iterableDataProduct[A: DataProduct, F[A] <: IterableOnce[A]]: DataProduct[F[A]] = new DataProduct[F[A]] { + def dataIterator(a: F[A], path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + a.iterator.zipWithIndex.flatMap { + case (elt, idx) => + dpa.dataIterator(elt, s"$path[$idx]") + } + } + } + /** [[DataProduct]] implementation for any [[scala.Tuple2]] where each field has an implementation of `DataProduct`. */ implicit def tuple2DataProduct[A: DataProduct, B: DataProduct]: DataProduct[(A, B)] = new DataProduct[(A, B)] { def dataIterator(tup: (A, B), path: String): Iterator[(Data, String)] = { diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/core/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/core/Lookupable.scala index 65c154d163f..0afd6ffcce7 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/core/Lookupable.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/core/Lookupable.scala @@ -514,6 +514,7 @@ object Lookupable { implicit val lookupShort: SimpleLookupable[Short] = new SimpleLookupable[Short]() implicit val lookupLong: SimpleLookupable[Long] = new SimpleLookupable[Long]() implicit val lookupFloat: SimpleLookupable[Float] = new SimpleLookupable[Float]() + implicit val lookupDouble: SimpleLookupable[Double] = new SimpleLookupable[Double]() implicit val lookupChar: SimpleLookupable[Char] = new SimpleLookupable[Char]() implicit val lookupString: SimpleLookupable[String] = new SimpleLookupable[String]() implicit val lookupBoolean: SimpleLookupable[Boolean] = new SimpleLookupable[Boolean]() diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index 827bb45476f..ca2fe9d710a 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -552,6 +552,45 @@ class DataViewSpec extends ChiselFlatSpec { verilog should include("assign z = sel ? b : d;") } + it should "support primitive types in the view target" in { + class MyBundle(val name: String) extends Bundle { + val x = UInt(8.W) + } + implicit val view: DataView[(UInt, Char, String, Int, Byte, Short, Long, BigInt, Float, Double), MyBundle] = + DataView( + tup => new MyBundle(tup.productIterator.toList.tail.mkString("_")), + _._1 -> _.x + ) + class MyModule extends Module { + val in = IO(Input(UInt(8.W))) + val bun = (in, 'a', "b", 0, 1.toByte, 2.toShort, 3L, BigInt(4), 5.0f, 6.0).viewAs[MyBundle] + val out = IO(Output(chiselTypeOf(bun))) + out := bun + bun.name should be("a_b_0_1_2_3_4_5.0_6.0") + } + val chirrtl = ChiselStage.emitCHIRRTL(new MyModule) + chirrtl should include("connect out.x, in") + } + + it should "support views of Options" in { + class MyBundle(w: Option[Int]) extends Bundle { + val x = w.map(v => UInt(v.W)) + } + implicit val view: DataView[Option[UInt], MyBundle] = DataView.mapping( + opt => new MyBundle(opt.map(_.getWidth)), + { case (opt, bun) => opt.zip(bun.x).map { case (o, b) => o -> b } } + ) + class MyModule extends Module { + val in = IO(Input(UInt(8.W))) + val out1 = IO(Output(new MyBundle(Some(8)))) + val out2 = IO(Output(new MyBundle(None))) + out1 := Option(in).viewAs[MyBundle] + out2 := Option.empty[UInt].viewAs[MyBundle] + } + val chirrtl = ChiselStage.emitCHIRRTL(new MyModule) + chirrtl should include("connect out1.x, in") + } + // This example should be turned into a built-in feature it should "enable viewing Seqs as Vecs" in {