diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 1930351e616..cf5a661b594 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -482,7 +482,17 @@ abstract class Data extends HasId with NamedComponent with DataIntf { // Trace views to give better error messages // Reifying involves checking against ViewParent which requires being in a Builder context // Since we're just printing a String, suppress such errors and use this object - val thiz = Try(reifySingleTarget(this)).toOption.flatten.getOrElse(this) + // Use a guard to prevent infinite recursion when reifySingleTarget triggers toString + val thiz = if (Data.avoidReifyingViews.get()) { + this + } else { + Data.avoidReifyingViews.set(true) + try { + Try(reifySingleTarget(this)).toOption.flatten.getOrElse(this) + } finally { + Data.avoidReifyingViews.set(false) + } + } thiz.topBindingOpt match { case None => chiselTypeWithModifier // Handle DontCares specially as they are "literal-like" but not actually literals @@ -896,6 +906,9 @@ object Data { // Needed for the `implicit def toConnectableDefault` import scala.language.implicitConversions + // ThreadLocal to prevent infinite recursion in stringAccessor when reifySingleTarget triggers toString + private[chisel3] val avoidReifyingViews: ThreadLocal[Boolean] = ThreadLocal.withInitial(() => false) + private[chisel3] case class ProbeInfo(val writable: Boolean, color: Option[layer.Layer]) /** Provides :<=, :>=, :<>=, and :#= between consumer and producer of the same T <: Data */ diff --git a/src/test/scala-2/chiselTests/ProbeSpec.scala b/src/test/scala-2/chiselTests/ProbeSpec.scala index 6cae1e4c893..09385a2b074 100644 --- a/src/test/scala-2/chiselTests/ProbeSpec.scala +++ b/src/test/scala-2/chiselTests/ProbeSpec.scala @@ -771,4 +771,30 @@ class ProbeSpec extends AnyFlatSpec with Matchers with FileCheck with ChiselSim } ChiselStage.emitCHIRRTL(new TestMod) } + + "FlatIO probe" should "not stack overflow" in { + class ProbeIO extends Bundle { + val a = UInt(32.W) + } + class WithProbeIO extends Bundle { + val probe = Output(Probe(new ProbeIO)) + } + class FlatIOModule extends RawModule { + val io = FlatIO(new WithProbeIO) + val w = Wire(new ProbeIO) + define(io.probe, ProbeValue(w)) + w.a := 0.U + dontTouch(io) + dontTouch(w) + } + class TestTop extends RawModule { + val testMod = Module(new FlatIOModule) + val r: Bool = testMod.io.probe.a === 0.U + dontTouch(r) + } + val exc = intercept[Throwable] { + ChiselStage.emitSystemVerilog(new TestTop) + } + exc should not be a[StackOverflowError] + } }