From a9b1017fb9bdb5c3fef6cae1639fcee57cf51a2c Mon Sep 17 00:00:00 2001 From: Mike Urbach Date: Mon, 15 Apr 2024 17:55:03 -0600 Subject: [PATCH] In BoringUtils.drive, don't bore into the final module for inputs. (#3998) This follows up on https://github.com/chipsalliance/chisel/pull/3960 to address the behavior at the final module for the sink being driven. We already have special handling for boring out from the original source, and we need the inverse here. When we reach the final sink, if it is an input port, we should use it, rather than boring into the module and connecting to it. --- .../util/experimental/BoringUtils.scala | 8 ++- .../scala/chiselTests/BoringUtilsSpec.scala | 49 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) 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)" + )() + } }