diff --git a/src/main/scala/chisel3/util/experimental/BoringUtils.scala b/src/main/scala/chisel3/util/experimental/BoringUtils.scala index e37c61f926f..7215335fbb4 100644 --- a/src/main/scala/chisel3/util/experimental/BoringUtils.scala +++ b/src/main/scala/chisel3/util/experimental/BoringUtils.scala @@ -243,6 +243,9 @@ object BoringUtils { case Some(PortBinding(_)) => true case _ => false } + def isDriveDone(d: Data): Boolean = { + isDrive && isPort(d) && DataMirror.directionOf(d) == ActualDirection.Input + } def boringError(module: BaseModule): Unit = { (module.fullyClosedErrorMessages ++ Seq( (si, s"Can only bore into modules that are not fully closed: ${module.name} was fully closed") @@ -254,8 +257,9 @@ object BoringUtils { path.zip(connectionLocation).foldLeft(source) { case (rhs, (module, conLoc)) if (module.isFullyClosed) => boringError(module); DontCare.asInstanceOf[A] case (rhs, (module, _)) - if (up && module == path(0) && isPort(rhs) && (!createProbe.nonEmpty || !createProbe.get.writable)) => { - // When drilling from the original source, if it's already a port just return it. + if ((up || isDriveDone(rhs)) && module == path(0) && isPort(rhs) && + (!createProbe.nonEmpty || !createProbe.get.writable)) => { + // When drilling from the original source, or driving to the sink, if it's already a port just return it. // As an exception, insist rwTaps are done from within the module and exported out. rhs } diff --git a/src/test/scala/chiselTests/BoringUtilsSpec.scala b/src/test/scala/chiselTests/BoringUtilsSpec.scala index 11b74b8b73d..b87f13d3c54 100644 --- a/src/test/scala/chiselTests/BoringUtilsSpec.scala +++ b/src/test/scala/chiselTests/BoringUtilsSpec.scala @@ -437,4 +437,53 @@ class BoringUtilsSpec extends ChiselFlatSpec with ChiselRunners with Utils with "propassign foo.bore, Integer(1)" )() } + + it should "bore to the final instance, but not into it, for inputs" in { + class Foo extends RawModule { + val a = IO(Input(Property[Int]())) + } + + class Bar extends RawModule { + val foo = Module(new Foo) + } + + class Baz extends RawModule { + val bar = Module(new Bar) + + BoringUtils.drive(bar.foo.a) := Property(1) + } + + val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Baz) + + matchesAndOmits(chirrtl)( + "input bore", + "propassign foo.a, bore", + "propassign bar.bore, Integer(1)" + )() + } + + it should "bore into the final instance for outputs" in { + class Foo extends RawModule { + val a = IO(Output(Property[Int]())) + } + + class Bar extends RawModule { + val foo = Module(new Foo) + } + + class Baz extends RawModule { + val bar = Module(new Bar) + + BoringUtils.drive(bar.foo.a) := Property(1) + } + + val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Baz) + + matchesAndOmits(chirrtl)( + "input bore", + "propassign a, bore", + "propassign foo.bore, bore", + "propassign bar.bore, Integer(1)" + )() + } }