Skip to content

Commit

Permalink
Add extension point on Data for customizing Connectable behavior. (#3978
Browse files Browse the repository at this point in the history
)

The user can override this method to customize how their Data
applies waive, squeeze, and exclude in its Connectable.
  • Loading branch information
mikeurbach committed Apr 11, 2024
1 parent 263930c commit 1ecab68
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 2 deletions.
41 changes: 40 additions & 1 deletion core/src/main/scala/chisel3/Data.scala
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,25 @@ object Data {
* add to Data we also want on Connectable, so an implicit conversion makes the most sense
* so the ScalaDoc can be shared.
*/
implicit def toConnectableDefault[T <: Data](d: T): Connectable[T] = Connectable.apply(d)
implicit def toConnectableDefault[T <: Data](d: T): Connectable[T] = makeConnectableDefault(d)

/** Create the default [[Connectable]] used for all instances of a [[Data]] of type T.
*
* This uses the default [[connectable.Connectable.apply]] as a starting point.
*
* Users can extend the [[HasCustomConnectable]] trait on any [[Data]] to further customize the [[Connectable]]. This
* is checked for in any potentially nested [[Data]] and any customizations are applied on top of the default
* [[Connectable]].
*/
private[chisel3] def makeConnectableDefault[T <: Data](d: T): Connectable[T] = {
val base = Connectable.apply(d)
DataMirror
.collectMembers(d) {
case hasCustom: HasCustomConnectable =>
hasCustom
}
.foldLeft(base)((connectable, hasCustom) => hasCustom.customConnectable(connectable))
}

/** Typeclass implementation of HasMatchingZipOfChildren for Data
*
Expand Down Expand Up @@ -1160,3 +1178,24 @@ final case object DontCare extends Element with connectable.ConnectableDocs {
final def :>=[T <: Data](producer: => T)(implicit sourceInfo: SourceInfo): Unit =
this.asInstanceOf[Data] :>= producer.asInstanceOf[Data]
}

/** Trait to indicate that a subclass of [[Data]] has a custom [[Connectable]].
*
* Users can implement the [[customConnectable]] method, which receives a default [[Connectable]], and is expected to
* use the methods on [[Connectable]] to customize it. For example, a [[Bundle]] could define this by using
* [[connectable.Connectable.exclude(members*]] to always exlude a specific member:
*
* {{{
* class MyBundle extends Bundle with HasCustomConnectable {
* val foo = Bool()
* val bar = Bool()
*
* override def customConnectable[T <: Data](base: Connectable[T]): Connectable[T] = {
* base.exclude(_ => bar)
* }
* }
* }}}
*/
trait HasCustomConnectable { this: Data =>
def customConnectable[T <: Data](base: Connectable[T]): Connectable[T]
}
2 changes: 1 addition & 1 deletion core/src/main/scala/chisel3/connectable/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ package object connectable {
* @param consumer the left-hand-side of the connection
*/
implicit class ConnectableOperators[T <: Data](consumer: T)
extends Connectable.ConnectableOpExtension(Connectable(consumer))
extends Connectable.ConnectableOpExtension(Data.makeConnectableDefault(consumer))

/** ConnectableVec Typeclass defines the following operators on between a (consumer: Vec) and (producer: Seq): :<=, :>=, :<>=, :#=
*
Expand Down
240 changes: 240 additions & 0 deletions src/test/scala/chiselTests/ConnectableSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1839,4 +1839,244 @@ class ConnectableSpec extends ChiselFunSpec with Utils {
}
}
}
describe("(9): HasCustomConnectable") {
describe("(9.a) customizing waive behavior") {

class MyBundle(includeFoo: Boolean) extends Bundle with HasCustomConnectable {
val foo = Option.when(includeFoo)(Bool())
val bar = Bool()

override def customConnectable[T <: Data](base: Connectable[T]): Connectable[T] = {
if (includeFoo) {
base.waive(_ => foo.get)
} else {
base
}
}
}

class OuterBundle(includeFoo: Boolean) extends Bundle {
val bundle = new MyBundle(includeFoo)
}

it("(9.a.1) allows the user to customize the waive behavior of the Connectable for their class as producer") {

class MyModule extends RawModule {
val in = IO(Input(new MyBundle(true)))
val out = IO(Output(new MyBundle(false)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bar, in.bar"
),
Seq(
"connect out.foo, in.foo"
)
)
}

it("(9.a.2) allows the user to customize the waive behavior of the Connectable for their class as consumer") {

class MyModule extends RawModule {
val in = IO(Input(new MyBundle(false)))
val out = IO(Output(new MyBundle(true)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bar, in.bar"
),
Seq(
"connect out.foo, in.foo"
)
)
}

it(
"(9.a.3) allows the user to customize the waive behavior of the Connectable for their class nested in other Connectables"
) {

class MyModule extends RawModule {
val in = IO(Input(new OuterBundle(true)))
val out = IO(Output(new OuterBundle(false)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bundle.bar, in.bundle.bar"
),
Seq(
"connect out.bundle.foo, in.bundle.foo"
)
)
}
}

describe("(9.b) customizing squeeze behavior") {

class MyBundle(fooWidth: Int) extends Bundle with HasCustomConnectable {
val foo = UInt(fooWidth.W)
val bar = Bool()

override def customConnectable[T <: Data](base: Connectable[T]): Connectable[T] = {
base.squeeze(_ => foo)
}
}

class OuterBundle(fooWidth: Int) extends Bundle {
val bundle = new MyBundle(fooWidth)
}

it("(9.b.1) allows the user to customize the squeeze behavior of the Connectable for their class as producer") {

class MyModule extends RawModule {
val in = IO(Input(new MyBundle(2)))
val out = IO(Output(new MyBundle(1)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bar, in.bar",
"connect out.foo, in.foo"
),
Nil
)
}

it("(9.b.2) allows the user to customize the squeeze behavior of the Connectable for their class as consumer") {

class MyModule extends RawModule {
val in = IO(Input(new MyBundle(1)))
val out = IO(Output(new MyBundle(2)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bar, in.bar",
"connect out.foo, in.foo"
),
Nil
)
}

it(
"(9.b.3) allows the user to customize the squeeze behavior of the Connectable for their class nested in other Connectables"
) {

class MyModule extends RawModule {
val in = IO(Input(new OuterBundle(2)))
val out = IO(Output(new OuterBundle(1)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bundle.bar, in.bundle.bar",
"connect out.bundle.foo, in.bundle.foo"
),
Nil
)
}
}

describe("(9.c) connectableExcludeSelection") {

class MyBundle(includeFoo: Boolean) extends Bundle with HasCustomConnectable {
val foo = Option.when(includeFoo)(Bool())
val bar = Bool()

override def customConnectable[T <: Data](base: Connectable[T]): Connectable[T] = {
if (includeFoo) {
base.exclude(_ => foo.get)
} else {
base
}
}
}

class OuterBundle(includeFoo: Boolean) extends Bundle {
val bundle = new MyBundle(includeFoo)
}

it("(9.c.1) allows the user to customize the exclude behavior of the Connectable for their class as producer") {

class MyModule extends RawModule {
val in = IO(Input(new MyBundle(true)))
val out = IO(Output(new MyBundle(false)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bar, in.bar"
),
Seq(
"connect out.foo, in.foo"
)
)
}

it("(9.c.2) allows the user to customize the exclude behavior of the Connectable for their class as consumer") {

class MyModule extends RawModule {
val in = IO(Input(new MyBundle(false)))
val out = IO(Output(new MyBundle(true)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bar, in.bar"
),
Seq(
"connect out.foo, in.foo"
)
)
}

it(
"(9.c.3) allows the user to customize the exclude behavior of the Connectable for their class nested in other Connectables"
) {

class MyModule extends RawModule {
val in = IO(Input(new OuterBundle(true)))
val out = IO(Output(new OuterBundle(false)))

out :<>= in
}

testCheck(
ChiselStage.emitCHIRRTL(new MyModule()),
Seq(
"connect out.bundle.bar, in.bundle.bar"
),
Seq(
"connect out.bundle.foo, in.bundle.foo"
)
)
}
}
}
}

0 comments on commit 1ecab68

Please sign in to comment.