diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index 0d502fae84c..91d4b98fdcd 100644 --- a/core/src/main/scala/chisel3/RawModule.scala +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -4,7 +4,7 @@ package chisel3 import scala.util.Try import scala.language.experimental.macros -import chisel3.experimental.{BaseModule, SourceInfo, UnlocatableSourceInfo} +import chisel3.experimental.{BaseModule, OpaqueType, SourceInfo, UnlocatableSourceInfo} import chisel3.internal._ import chisel3.experimental.hierarchy.{InstanceClone, ModuleClone} import chisel3.properties.{DynamicObject, Property, StaticObject} @@ -208,23 +208,34 @@ abstract class RawModule extends BaseModule { .getOrElse( throwException(s"BoringUtils currently only support identity views, ${_right} has multiple targets.") ) - val rhs = (left.probeInfo.nonEmpty, right.probeInfo.nonEmpty) match { - case (true, true) => ProbeDefine(si, left.lref, Node(right)) - 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) => - (left, right) match { - case (_: Property[_], _: Property[_]) => PropAssign(si, left.lref, Node(right)) - // Use `connect lhs, read(probe(rhs))` if lhs is passive version of rhs. - // This provides solution for this: https://github.com/chipsalliance/chisel/issues/3557 - case (_, _) - if !DataMirror.checkAlignmentTypeEquivalence(left, right) && - DataMirror.checkAlignmentTypeEquivalence(left, Output(chiselTypeOf(right))) => - Connect(si, Node(left), ProbeRead(ProbeExpr(Node(right)))) - case (_, _) => Connect(si, left.lref, Node(right)) - } + + def computeConnection(left: Data, right: Data): Command = { + (left.probeInfo.nonEmpty, right.probeInfo.nonEmpty) match { + case (true, true) => ProbeDefine(si, left.lref, Node(right)) + 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) => + // For non-probe, directly create Nodes for lhs, skipping visibility check to support BoringUtils.drive. + (left, right) match { + case (lhsOpaque: Record, rhsOpaque: Record) + if lhsOpaque._isOpaqueType && rhsOpaque._isOpaqueType && DataMirror.isProperty( + lhsOpaque.allElements.head + ) && DataMirror.isProperty(rhsOpaque.allElements.head) => + computeConnection(lhsOpaque.allElements.head, rhsOpaque.allElements.head) + case (_: Property[_], _: Property[_]) => PropAssign(si, Node(left), Node(right)) + // Use `connect lhs, read(probe(rhs))` if lhs is passive version of rhs. + // This provides solution for this: https://github.com/chipsalliance/chisel/issues/3557 + case (_, _) + if !DataMirror.checkAlignmentTypeEquivalence(left, right) && + DataMirror.checkAlignmentTypeEquivalence(left, Output(chiselTypeOf(right))) => + Connect(si, Node(left), ProbeRead(ProbeExpr(Node(right)))) + case (_, _) => Connect(si, Node(left), Node(right)) + } + } } + + val rhs = computeConnection(left, right) val secretCommands = if (_closed) { _component.get.asInstanceOf[DefModule].secretCommands } else { diff --git a/src/test/scala/chiselTests/BoringUtilsSpec.scala b/src/test/scala/chiselTests/BoringUtilsSpec.scala index 5236e361ed4..3f003bf0074 100644 --- a/src/test/scala/chiselTests/BoringUtilsSpec.scala +++ b/src/test/scala/chiselTests/BoringUtilsSpec.scala @@ -5,7 +5,7 @@ package chiselTests import chisel3._ import chisel3.util.Counter import chisel3.testers._ -import chisel3.experimental.{BaseModule, ChiselAnnotation} +import chisel3.experimental.{BaseModule, ChiselAnnotation, OpaqueType} import chisel3.probe._ import chisel3.properties.Property import chisel3.util.experimental.BoringUtils @@ -376,4 +376,74 @@ class BoringUtilsSpec extends ChiselFlatSpec with ChiselRunners with Utils with "propassign a, bar.a_bore" )() } + + it should "bore from an opaque type that wraps a Property" in { + class MyOpaqueProperty extends Record with OpaqueType { + private val underlying = Property[Int]() + val elements = scala.collection.immutable.SeqMap("" -> underlying) + override protected def errorOnAsUInt = true + } + + class Baz extends RawModule { + val a = IO(Output(new MyOpaqueProperty)) + } + + class Bar extends RawModule { + val baz = Module(new Baz) + } + + class Foo extends RawModule { + val a = IO(Output(new MyOpaqueProperty)) + + val bar = Module(new Bar) + + a := BoringUtils.bore(bar.baz.a) + } + + val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Foo) + + matchesAndOmits(chirrtl)( + "output a_bore : Integer", + "propassign a_bore, baz.a", + "propassign a_bore, bar.a_bore" + )() + } + + it should "bore from nested opaque types that wrap a Property" in { + class MyOpaqueProperty extends Record with OpaqueType { + private val underlying = Property[Int]() + val elements = scala.collection.immutable.SeqMap("" -> underlying) + override protected def errorOnAsUInt = true + } + + class MyOuterOpaque extends Record with OpaqueType { + private val underlying = new MyOpaqueProperty + val elements = scala.collection.immutable.SeqMap("" -> underlying) + override protected def errorOnAsUInt = true + } + + class Baz extends RawModule { + val a = IO(Output(new MyOpaqueProperty)) + } + + class Bar extends RawModule { + val baz = Module(new Baz) + } + + class Foo extends RawModule { + val a = IO(Output(new MyOpaqueProperty)) + + val bar = Module(new Bar) + + a := BoringUtils.bore(bar.baz.a) + } + + val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Foo) + + matchesAndOmits(chirrtl)( + "output a_bore : Integer", + "propassign a_bore, baz.a", + "propassign a_bore, bar.a_bore" + )() + } }