Skip to content

Commit 5cd8ad0

Browse files
authored
Add a new BoringUtils.drive API for boring to drive a sink. (chipsalliance#3960)
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.
1 parent feab7a7 commit 5cd8ad0

File tree

3 files changed

+96
-9
lines changed

3 files changed

+96
-9
lines changed

core/src/main/scala/chisel3/RawModule.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,11 @@ abstract class RawModule extends BaseModule {
213213
case (true, false) if left.probeInfo.get.writable => ProbeDefine(si, left.lref, RWProbeExpr(Node(right)))
214214
case (true, false) => ProbeDefine(si, left.lref, ProbeExpr(Node(right)))
215215
case (false, true) => Connect(si, left.lref, ProbeRead(Node(right)))
216-
case (false, false) =>
216+
case (false, false) =>
217+
// For non-probe, directly create Nodes for lhs, skipping visibility check to support BoringUtils.drive.
217218
(left, right) match {
218-
case (_: Property[_], _: Property[_]) => PropAssign(si, left.lref, Node(right))
219-
case (_, _) => Connect(si, left.lref, Node(right))
219+
case (_: Property[_], _: Property[_]) => PropAssign(si, Node(left), Node(right))
220+
case (_, _) => Connect(si, Node(left), Node(right))
220221
}
221222
}
222223
val secretCommands = if (_closed) {

src/main/scala/chisel3/util/experimental/BoringUtils.scala

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package chisel3.util.experimental
44

55
import chisel3._
66
import chisel3.probe.{Probe, RWProbe}
7+
import chisel3.reflect.DataMirror
78
import chisel3.Data.ProbeInfo
89
import chisel3.experimental.{annotate, requireIsHardware, skipPrefix, BaseModule, ChiselAnnotation, SourceInfo}
910
import chisel3.internal.{Builder, BuilderContextCache, NamedComponent, Namespace, PortBinding}
@@ -217,8 +218,13 @@ object BoringUtils {
217218
genName
218219
}
219220

220-
private def boreOrTap[A <: Data](source: A, createProbe: Option[ProbeInfo] = None)(implicit si: SourceInfo): A = {
221-
import reflect.DataMirror
221+
private def boreOrTap[A <: Data](
222+
source: A,
223+
createProbe: Option[ProbeInfo] = None,
224+
isDrive: Boolean = false
225+
)(
226+
implicit si: SourceInfo
227+
): A = {
222228
def parent(d: Data): BaseModule = d.topBinding.location.get
223229
def purePortTypeBase = if (createProbe.nonEmpty) Output(chiselTypeOf(source))
224230
else if (DataMirror.hasOuterFlip(source)) Flipped(chiselTypeOf(source))
@@ -263,7 +269,11 @@ object BoringUtils {
263269
// if drilling down, don't drill Probe types
264270
val bore = if (up) module.createSecretIO(purePortType) else module.createSecretIO(Flipped(purePortTypeBase))
265271
module.addSecretIO(bore)
266-
conLoc.asInstanceOf[RawModule].secretConnection(bore, rhs)
272+
if (isDrive) {
273+
conLoc.asInstanceOf[RawModule].secretConnection(rhs, bore)
274+
} else {
275+
conLoc.asInstanceOf[RawModule].secretConnection(bore, rhs)
276+
}
267277
bore
268278
}
269279
}
@@ -290,8 +300,8 @@ object BoringUtils {
290300
Builder.error(s"Cannot bore from $source to ${thisModule.name}, as they do not share a least common ancestor")
291301
}
292302
val (upPath, downPath) = lcaResult.get
293-
val lcaSource = drill(source, upPath.dropRight(1), upPath.dropRight(1), true)
294-
val sink = drill(lcaSource, downPath.reverse.tail, downPath.reverse, false)
303+
val lcaSource = drill(source, upPath.dropRight(1), upPath.dropRight(1), up = !isDrive)
304+
val sink = drill(lcaSource, downPath.reverse.tail, downPath.reverse, up = isDrive)
295305

296306
if (
297307
createProbe.nonEmpty || DataMirror.hasProbeTypeModifier(purePortTypeBase) ||
@@ -301,7 +311,11 @@ object BoringUtils {
301311
} else {
302312
// Creating a wire to assign the result to. We will return this.
303313
val bore = Wire(purePortTypeBase)
304-
thisModule.asInstanceOf[RawModule].secretConnection(bore, sink)
314+
if (isDrive) {
315+
thisModule.asInstanceOf[RawModule].secretConnection(sink, bore)
316+
} else {
317+
thisModule.asInstanceOf[RawModule].secretConnection(bore, sink)
318+
}
305319
bore
306320
}
307321
}
@@ -314,6 +328,17 @@ object BoringUtils {
314328
boreOrTap(source, createProbe = None)
315329
}
316330

331+
/** Access a sink [[Data]] for driving that may or may not be in the current module.
332+
*
333+
* If the sink is in a child module, than create input ports to allow driving the requested sink.
334+
*
335+
* Note that the sink may not be a probe, and [[rwTap]] should be used instead.
336+
*/
337+
def drive[A <: Data](sink: A)(implicit si: SourceInfo): A = {
338+
require(!DataMirror.hasProbeTypeModifier(sink), "cannot drive a probe from BoringUtils.drive")
339+
boreOrTap(sink, createProbe = None, isDrive = true)
340+
}
341+
317342
/** Access a source [[Data]] that may or may not be in the current module. If
318343
* this is in a child module, then create read-only probe ports to allow
319344
* access to the requested source.

src/test/scala/chiselTests/BoringUtilsSpec.scala

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,4 +376,65 @@ class BoringUtilsSpec extends ChiselFlatSpec with ChiselRunners with Utils with
376376
"propassign a, bar.a_bore"
377377
)()
378378
}
379+
380+
behavior.of("BoringUtils.drive")
381+
382+
it should "fail on probes" in {
383+
class Foo extends RawModule {
384+
val a = Wire(Bool())
385+
val p = ProbeValue(a)
386+
}
387+
388+
class Bar extends RawModule {
389+
val foo = Module(new Foo)
390+
391+
BoringUtils.drive(foo.p) := 1.B
392+
}
393+
394+
val e = the[Exception] thrownBy circt.stage.ChiselStage.emitCHIRRTL(new Bar)
395+
396+
e.getMessage should include("requirement failed: cannot drive a probe from BoringUtils.drive")
397+
}
398+
399+
it should "bore ports for driving hardware" in {
400+
class Foo extends RawModule {
401+
val a = Wire(Bool())
402+
}
403+
404+
class Bar extends RawModule {
405+
val foo = Module(new Foo)
406+
407+
BoringUtils.drive(foo.a) := 1.B
408+
}
409+
410+
val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Bar)
411+
412+
matchesAndOmits(chirrtl)(
413+
"input bore",
414+
"connect a, bore",
415+
"wire bore",
416+
"connect bore, UInt<1>(0h1)",
417+
"connect foo.bore, bore"
418+
)()
419+
}
420+
421+
it should "bore ports for driving properties" in {
422+
class Foo extends RawModule {
423+
val a = Wire(Property[Int]())
424+
}
425+
426+
class Bar extends RawModule {
427+
val foo = Module(new Foo)
428+
429+
BoringUtils.drive(foo.a) := Property(1)
430+
}
431+
432+
val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Bar)
433+
434+
matchesAndOmits(chirrtl)(
435+
"input bore",
436+
"propassign a, bore",
437+
"propassign foo.bore, Integer(1)"
438+
)()
439+
}
379440
}

0 commit comments

Comments
 (0)