From 4149157df6531d124483d992daf96cf4e62a0f0c Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Fri, 4 Nov 2022 18:20:07 +0000 Subject: Add PartialDataView.supertype (backport #2826) (#2827) * Add PartialDataView.supertype (#2826) This factory method makes it easy to create PartialDataViews from a Bundle type to its supertype. Because of the typing relationship, there is no need to provide a mapping between fields. The only thing necessary is to provide a function for constructing an instance of the supertype from an instance of the subtype. (cherry picked from commit 251d454a224e5a961438ba0ea41134d7da7a5992) # Conflicts: # core/src/main/scala/chisel3/experimental/dataview/package.scala # src/test/scala/chiselTests/experimental/DataView.scala * Resolve backport conflicts Co-authored-by: Jack Koenig --- .../chisel3/experimental/dataview/DataView.scala | 23 ++++++++++++++ .../chisel3/experimental/dataview/package.scala | 14 ++------- docs/src/cookbooks/dataview.md | 35 ++++++++++++++++++++++ .../scala/chiselTests/experimental/DataView.scala | 22 ++++++++++++++ 4 files changed, 82 insertions(+), 12 deletions(-) diff --git a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala index 7f20964d..cc555b11 100644 --- a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala +++ b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala @@ -592,4 +592,27 @@ object PartialDataView { implicit sourceInfo: SourceInfo ): DataView[T, V] = new DataView[T, V](mkView, mapping, _total = false) + + /** Constructs a non-total [[DataView]] mapping from a [[Bundle]] type to a parent [[Bundle]] type + * + * @param mkView a function constructing an instance `V` from an instance of `T` + * @return the [[DataView]] that enables viewing instances of a [[Bundle]] as instances of a parent type + */ + def supertype[T <: Bundle, V <: Bundle]( + mkView: T => V + )( + implicit ev: SubTypeOf[T, V], + sourceInfo: SourceInfo + ): DataView[T, V] = + mapping[T, V]( + mkView, + { + case (a, b) => + val aElts = a.elements + val bElts = b.elements + val bKeys = bElts.keySet + val keys = aElts.keysIterator.filter(bKeys.contains) + keys.map(k => aElts(k) -> bElts(k)).toSeq + } + ) } diff --git a/core/src/main/scala/chisel3/experimental/dataview/package.scala b/core/src/main/scala/chisel3/experimental/dataview/package.scala index 71ae2d8f..a52e88cf 100644 --- a/core/src/main/scala/chisel3/experimental/dataview/package.scala +++ b/core/src/main/scala/chisel3/experimental/dataview/package.scala @@ -43,24 +43,14 @@ package object dataview { "${A} is not a subtype of ${B}! Did you mean .viewAs[${B}]? " + "Please see https://www.chisel-lang.org/chisel3/docs/cookbooks/dataview" ) - private type SubTypeOf[A, B] = A <:< B + private[dataview] type SubTypeOf[A, B] = A <:< B /** Provides `viewAsSupertype` for subclasses of [[Bundle]] */ implicit class BundleUpcastable[T <: Bundle](target: T) { /** View a [[Bundle]] or [[Record]] as a parent type (upcast) */ def viewAsSupertype[V <: Bundle](proto: V)(implicit ev: SubTypeOf[T, V], sourceInfo: SourceInfo): V = { - implicit val dataView = PartialDataView.mapping[T, V]( - _ => proto, - { - case (a, b) => - val aElts = a.elements - val bElts = b.elements - val bKeys = bElts.keySet - val keys = aElts.keysIterator.filter(bKeys.contains) - keys.map(k => aElts(k) -> bElts(k)).toSeq - } - ) + implicit val dataView = PartialDataView.supertype[T, V](_ => proto) target.viewAs[V] } } diff --git a/docs/src/cookbooks/dataview.md b/docs/src/cookbooks/dataview.md index ed969ca1..f970cfe4 100644 --- a/docs/src/cookbooks/dataview.md +++ b/docs/src/cookbooks/dataview.md @@ -12,6 +12,7 @@ section: "chisel3" * [How do I connect a subset of Bundle fields?](#how-do-i-connect-a-subset-of-bundle-fields) * [How do I view a Bundle as a parent type (superclass)?](#how-do-i-view-a-bundle-as-a-parent-type-superclass) * [How do I view a Bundle as a parent type when the parent type is abstract (like a trait)?](#how-do-i-view-a-bundle-as-a-parent-type-when-the-parent-type-is-abstract-like-a-trait) + * [How can I use `.viewAs` instead of `.viewAsSupertype(type)`?](#how-can-i-use-viewas-instead-of-viewassupertypetype) ## How do I view a Data as a UInt or vice versa? @@ -177,3 +178,37 @@ As indicated in the comment, abstract methods must still be implemented. This is the same that happens when one writes `new Bundle {}`, the curly braces create a new concrete subclass; however, because `Bundle` has no abstract methods, the contents of the body can be empty. + +### How can I use `.viewAs` instead of `.viewAsSupertype(type)`? + +While `viewAsSupertype` is helpful for one-off casts, the need to provide a type template object +each time can be onerous. +Because of the subtyping relationship, you can use `PartialDataView.supertype` to create a +`DataView` from a Bundle type to a parent type by just providing the function to construct an +instance of the parent type from an instance of the child type. +The mapping of corresponding fields is automatically determined by Chisel to be the fields defined +in the supertype. + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.experimental.dataview._ + +class Foo(x: Int) extends Bundle { + val foo = UInt(x.W) +} +class Bar(val x: Int) extends Foo(x) { + val bar = UInt(x.W) +} +// Define a DataView without having to specify the mapping! +implicit val view = PartialDataView.supertype[Bar, Foo](b => new Foo(b.x)) + +class MyModule extends Module { + val foo = IO(Input(new Foo(8))) + val bar = IO(Output(new Bar(8))) + bar.viewAs[Foo] := foo // bar.foo := foo.foo + bar.bar := 123.U // all fields need to be connected +} +``` +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new MyModule) +``` diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index ac8357f0..3673778b 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -177,6 +177,28 @@ class DataViewSpec extends ChiselFlatSpec { chirrtl should include("fooOut.foo <= barIn.foo") } + it should "be easy to make a PartialDataView viewing a Bundle as a Parent Bundle type" in { + class Foo(x: Int) extends Bundle { + val foo = UInt(x.W) + } + class Bar(val x: Int) extends Foo(x) { + val bar = UInt(x.W) + } + implicit val view = PartialDataView.supertype[Bar, Foo](b => new Foo(b.x)) + class MyModule extends Module { + val fooIn = IO(Input(new Foo(8))) + val barOut = IO(Output(new Bar(8))) + barOut.viewAs[Foo] := fooIn + + val barIn = IO(Input(new Bar(8))) + val fooOut = IO(Output(new Foo(8))) + fooOut := barIn.viewAs[Foo] + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("barOut.foo <= fooIn.foo") + chirrtl should include("fooOut.foo <= barIn.foo") + } + it should "error if viewing a parent Bundle as a child Bundle type" in { assertTypeError(""" class Foo extends Bundle { -- cgit v1.2.3