Skip to content

Commit

Permalink
Add a new BoringUtils.drive API for boring to drive a sink. (#3960)
Browse files Browse the repository at this point in the history
This API allows users to bore to a sink they plan to drive, which complements the existing API to bore from a source to read.
  • Loading branch information
mikeurbach authored and SpriteOvO committed Apr 20, 2024
1 parent 6406c41 commit a2c75b0
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 9 deletions.
7 changes: 4 additions & 3 deletions core/src/main/scala/chisel3/RawModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,11 @@ abstract class RawModule extends BaseModule {
case (true, false) if left.probeInfo.get.writable => ProbeDefine(si, left.lref, RWProbeExpr(Node(right)))
case (true, false) => ProbeDefine(si, left.lref, ProbeExpr(Node(right)))
case (false, true) => Connect(si, left.lref, ProbeRead(Node(right)))
case (false, false) =>
case (false, false) =>
// For non-probe, directly create Nodes for lhs, skipping visibility check to support BoringUtils.drive.
(left, right) match {
case (_: Property[_], _: Property[_]) => PropAssign(si, left.lref, Node(right))
case (_, _) => Connect(si, left.lref, Node(right))
case (_: Property[_], _: Property[_]) => PropAssign(si, Node(left), Node(right))
case (_, _) => Connect(si, Node(left), Node(right))
}
}
val secretCommands = if (_closed) {
Expand Down
37 changes: 31 additions & 6 deletions src/main/scala/chisel3/util/experimental/BoringUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package chisel3.util.experimental

import chisel3._
import chisel3.probe.{Probe, RWProbe}
import chisel3.reflect.DataMirror
import chisel3.Data.ProbeInfo
import chisel3.experimental.{annotate, requireIsHardware, skipPrefix, BaseModule, ChiselAnnotation, SourceInfo}
import chisel3.internal.{Builder, BuilderContextCache, NamedComponent, Namespace, PortBinding}
Expand Down Expand Up @@ -217,8 +218,13 @@ object BoringUtils {
genName
}

private def boreOrTap[A <: Data](source: A, createProbe: Option[ProbeInfo] = None)(implicit si: SourceInfo): A = {
import reflect.DataMirror
private def boreOrTap[A <: Data](
source: A,
createProbe: Option[ProbeInfo] = None,
isDrive: Boolean = false
)(
implicit si: SourceInfo
): A = {
def parent(d: Data): BaseModule = d.topBinding.location.get
def purePortTypeBase = if (createProbe.nonEmpty) Output(chiselTypeOf(source))
else if (DataMirror.hasOuterFlip(source)) Flipped(chiselTypeOf(source))
Expand Down Expand Up @@ -263,7 +269,11 @@ object BoringUtils {
// if drilling down, don't drill Probe types
val bore = if (up) module.createSecretIO(purePortType) else module.createSecretIO(Flipped(purePortTypeBase))
module.addSecretIO(bore)
conLoc.asInstanceOf[RawModule].secretConnection(bore, rhs)
if (isDrive) {
conLoc.asInstanceOf[RawModule].secretConnection(rhs, bore)
} else {
conLoc.asInstanceOf[RawModule].secretConnection(bore, rhs)
}
bore
}
}
Expand All @@ -290,8 +300,8 @@ object BoringUtils {
Builder.error(s"Cannot bore from $source to ${thisModule.name}, as they do not share a least common ancestor")
}
val (upPath, downPath) = lcaResult.get
val lcaSource = drill(source, upPath.dropRight(1), upPath.dropRight(1), true)
val sink = drill(lcaSource, downPath.reverse.tail, downPath.reverse, false)
val lcaSource = drill(source, upPath.dropRight(1), upPath.dropRight(1), up = !isDrive)
val sink = drill(lcaSource, downPath.reverse.tail, downPath.reverse, up = isDrive)

if (
createProbe.nonEmpty || DataMirror.hasProbeTypeModifier(purePortTypeBase) ||
Expand All @@ -301,7 +311,11 @@ object BoringUtils {
} else {
// Creating a wire to assign the result to. We will return this.
val bore = Wire(purePortTypeBase)
thisModule.asInstanceOf[RawModule].secretConnection(bore, sink)
if (isDrive) {
thisModule.asInstanceOf[RawModule].secretConnection(sink, bore)
} else {
thisModule.asInstanceOf[RawModule].secretConnection(bore, sink)
}
bore
}
}
Expand All @@ -314,6 +328,17 @@ object BoringUtils {
boreOrTap(source, createProbe = None)
}

/** Access a sink [[Data]] for driving that may or may not be in the current module.
*
* If the sink is in a child module, than create input ports to allow driving the requested sink.
*
* Note that the sink may not be a probe, and [[rwTap]] should be used instead.
*/
def drive[A <: Data](sink: A)(implicit si: SourceInfo): A = {
require(!DataMirror.hasProbeTypeModifier(sink), "cannot drive a probe from BoringUtils.drive")
boreOrTap(sink, createProbe = None, isDrive = true)
}

/** Access a source [[Data]] that may or may not be in the current module. If
* this is in a child module, then create read-only probe ports to allow
* access to the requested source.
Expand Down
61 changes: 61 additions & 0 deletions src/test/scala/chiselTests/BoringUtilsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,65 @@ class BoringUtilsSpec extends ChiselFlatSpec with ChiselRunners with Utils with
"propassign a, bar.a_bore"
)()
}

behavior.of("BoringUtils.drive")

it should "fail on probes" in {
class Foo extends RawModule {
val a = Wire(Bool())
val p = ProbeValue(a)
}

class Bar extends RawModule {
val foo = Module(new Foo)

BoringUtils.drive(foo.p) := 1.B
}

val e = the[Exception] thrownBy circt.stage.ChiselStage.emitCHIRRTL(new Bar)

e.getMessage should include("requirement failed: cannot drive a probe from BoringUtils.drive")
}

it should "bore ports for driving hardware" in {
class Foo extends RawModule {
val a = Wire(Bool())
}

class Bar extends RawModule {
val foo = Module(new Foo)

BoringUtils.drive(foo.a) := 1.B
}

val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Bar)

matchesAndOmits(chirrtl)(
"input bore",
"connect a, bore",
"wire bore",
"connect bore, UInt<1>(0h1)",
"connect foo.bore, bore"
)()
}

it should "bore ports for driving properties" in {
class Foo extends RawModule {
val a = Wire(Property[Int]())
}

class Bar extends RawModule {
val foo = Module(new Foo)

BoringUtils.drive(foo.a) := Property(1)
}

val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Bar)

matchesAndOmits(chirrtl)(
"input bore",
"propassign a, bore",
"propassign foo.bore, Integer(1)"
)()
}
}

0 comments on commit a2c75b0

Please sign in to comment.