summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormergify[bot]2022-10-23 22:27:06 +0000
committerGitHub2022-10-23 22:27:06 +0000
commitf86c1ff7b39146f23cd1959bcc63dcb3b0b27125 (patch)
tree4f443eb311ca5c0fb0fcd769cdee545ef49413a3
parentd997acb05e5a307afb7c9ad4c136b9b4e1506efc (diff)
Fix for <> to BlackBox.IO with Compatibility Bundles (#2801) (#2803)
MonoConnect.traceFlow now properly handles coerced directions. Also minor improvement to getClassName especially useful in test case printf debugging. (cherry picked from commit 3aba755bdcf996c0fbd846d13268fd6641b29e96) Co-authored-by: Megan Wachs <megan@sifive.com>
-rw-r--r--core/src/main/scala/chisel3/Aggregate.scala20
-rw-r--r--core/src/main/scala/chisel3/internal/BiConnect.scala9
-rw-r--r--core/src/main/scala/chisel3/internal/MonoConnect.scala30
-rw-r--r--src/test/scala/chiselTests/BulkConnectSpec.scala15
-rw-r--r--src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala155
5 files changed, 212 insertions, 17 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala
index aacf0b1c..4fc9b20f 100644
--- a/core/src/main/scala/chisel3/Aggregate.scala
+++ b/core/src/main/scala/chisel3/Aggregate.scala
@@ -1116,7 +1116,12 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
def elements: SeqMap[String, Data]
/** Name for Pretty Printing */
- def className: String = this.getClass.getSimpleName
+ def className: String = try {
+ this.getClass.getSimpleName
+ } catch {
+ // This happens if your class is defined in an object and is anonymous
+ case e: java.lang.InternalError if e.getMessage == "Malformed class name" => this.getClass.toString
+ }
private[chisel3] override def typeEquivalent(that: Data): Boolean = that match {
case that: Record =>
@@ -1243,10 +1248,15 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record wi
"Please see https://github.com/chipsalliance/chisel3#build-your-own-chisel-projects."
)
- override def className: String = this.getClass.getSimpleName match {
- case name if name.startsWith("$anon$") => "AnonymousBundle" // fallback for anonymous Bundle case
- case "" => "AnonymousBundle" // ditto, but on other platforms
- case name => name
+ override def className: String = try {
+ this.getClass.getSimpleName match {
+ case name if name.startsWith("$anon$") => "AnonymousBundle" // fallback for anonymous Bundle case
+ case "" => "AnonymousBundle" // ditto, but on other platforms
+ case name => name
+ }
+ } catch {
+ // This happens if you have nested objects which your class is defined in
+ case e: java.lang.InternalError if e.getMessage == "Malformed class name" => this.getClass.toString
}
/** The collection of [[Data]]
diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala
index e8fb2361..74376598 100644
--- a/core/src/main/scala/chisel3/internal/BiConnect.scala
+++ b/core/src/main/scala/chisel3/internal/BiConnect.scala
@@ -227,9 +227,12 @@ private[chisel3] object BiConnect {
context_mod: RawModule
): Unit = {
// Verify right has no extra fields that left doesn't have
- for ((field, right_sub) <- right_r.elements) {
- if (!left_r.elements.isDefinedAt(field)) {
- if (connectCompileOptions.connectFieldsMustMatch) {
+
+ // For each field in left, descend with right.
+ // Don't bother doing this check if we don't expect it to necessarily pass.
+ if (connectCompileOptions.connectFieldsMustMatch) {
+ for ((field, right_sub) <- right_r.elements) {
+ if (!left_r.elements.isDefinedAt(field)) {
throw MissingLeftFieldException(field)
}
}
diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala
index 31364804..4e762a7c 100644
--- a/core/src/main/scala/chisel3/internal/MonoConnect.scala
+++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala
@@ -322,21 +322,35 @@ private[chisel3] object MonoConnect {
else false
}
- /** Trace flow from child Data to its parent. */
- @tailrec private[chisel3] def traceFlow(currentlyFlipped: Boolean, data: Data, context_mod: RawModule): Boolean = {
- import SpecifiedDirection.{Input => SInput, Flip => SFlip}
+ /** Trace flow from child Data to its parent.
+ *
+ * Returns true if, given the context,
+ * this signal can be a sink when wantsToBeSink = true,
+ * or if it can be a source when wantsToBeSink = false.
+ * Always returns true if the Data does not actually correspond
+ * to a Port.
+ */
+ @tailrec private[chisel3] def traceFlow(
+ wantToBeSink: Boolean,
+ currentlyFlipped: Boolean,
+ data: Data,
+ context_mod: RawModule
+ ): Boolean = {
val sdir = data.specifiedDirection
- val flipped = sdir == SInput || sdir == SFlip
+ val coercedFlip = sdir == SpecifiedDirection.Input
+ val coercedAlign = sdir == SpecifiedDirection.Output
+ val flipped = sdir == SpecifiedDirection.Flip
+ val traceFlipped = ((flipped ^ currentlyFlipped) || coercedFlip) && (!coercedAlign)
data.binding.get match {
- case ChildBinding(parent) => traceFlow(flipped ^ currentlyFlipped, parent, context_mod)
+ case ChildBinding(parent) => traceFlow(wantToBeSink, traceFlipped, parent, context_mod)
case PortBinding(enclosure) =>
val childPort = enclosure != context_mod
- childPort ^ flipped ^ currentlyFlipped
+ wantToBeSink ^ childPort ^ traceFlipped
case _ => true
}
}
- def canBeSink(data: Data, context_mod: RawModule): Boolean = traceFlow(true, data, context_mod)
- def canBeSource(data: Data, context_mod: RawModule): Boolean = traceFlow(false, data, context_mod)
+ def canBeSink(data: Data, context_mod: RawModule): Boolean = traceFlow(true, false, data, context_mod)
+ def canBeSource(data: Data, context_mod: RawModule): Boolean = traceFlow(false, false, data, context_mod)
/** Check whether two aggregates can be bulk connected (<=) in FIRRTL. (MonoConnect case)
*
diff --git a/src/test/scala/chiselTests/BulkConnectSpec.scala b/src/test/scala/chiselTests/BulkConnectSpec.scala
index 281890d4..0a1616d3 100644
--- a/src/test/scala/chiselTests/BulkConnectSpec.scala
+++ b/src/test/scala/chiselTests/BulkConnectSpec.scala
@@ -54,7 +54,15 @@ class BulkConnectSpec extends ChiselPropSpec {
})
chirrtl should include("out.buzz.foo <= in.buzz.foo")
+ chirrtl should include("out.fizz <= in.fizz")
+ chirrtl should include("deq.bits <- enq.bits")
+ chirrtl should include("deq.valid <= enq.valid")
+ chirrtl should include("enq.ready <= deq.ready")
+
chirrtl shouldNot include("deq <= enq")
+ chirrtl shouldNot include("deq.bits.foo <= enq.bits.foo")
+ chirrtl shouldNot include("deq.bits.foo <- enq.bits.foo")
+ chirrtl shouldNot include("deq.bits.bar")
}
property("Chisel connects should not emit FIRRTL bulk connects between differing FIRRTL types") {
@@ -74,7 +82,9 @@ class BulkConnectSpec extends ChiselPropSpec {
out <> in
})
// out <- in is illegal FIRRTL
- chirrtl should include("out.foo.bar <= in.foo.bar")
+ exactly(2, chirrtl.split('\n')) should include("out.foo.bar <= in.foo.bar")
+ chirrtl shouldNot include("out <= in")
+ chirrtl shouldNot include("out <- in")
}
property("Chisel connects should not emit a FIRRTL bulk connect for a bidirectional MonoConnect") {
@@ -91,6 +101,9 @@ class BulkConnectSpec extends ChiselPropSpec {
})
chirrtl shouldNot include("wire <= enq")
+ chirrtl should include("wire.bits <= enq.bits")
+ chirrtl should include("wire.valid <= enq.valid")
+ chirrtl should include("wire.ready <= enq.ready")
chirrtl should include("deq <= enq")
}
diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
index 1e199297..e2fb2179 100644
--- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
+++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
@@ -3,6 +3,7 @@
package chiselTests
import scala.collection.immutable.ListMap
+import chisel3.stage.ChiselStage.emitChirrtl
// Keep Chisel._ separate from chisel3._ below
object CompatibilityComponents {
@@ -390,4 +391,158 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec {
compile(new Top(true))
compile(new Top(false))
}
+
+ "A BlackBox with Chisel._ fields in its IO" should "bulk connect in import chisel3._ code correctly" in {
+ object Compat {
+ import Chisel._
+ class LegacyChiselIO extends Bundle {
+ val foo = Output(Bool())
+ val bar = Output(Bool())
+ }
+ }
+ object Chisel3 {
+ import chisel3._
+ import chisel3.util.Valid
+
+ class FooModuleIO extends Bundle {
+ val quz = Input(new QuzIO)
+ val foo = Output(Bool())
+ val bar = Input(Bool())
+ }
+ class QuzIO extends Bundle {
+ val q = Flipped(Valid(new Compat.LegacyChiselIO))
+ }
+ class FooModule extends Module {
+ val io = IO(new FooModuleIO)
+ io <> DontCare
+ }
+ class FooMirrorBlackBox extends BlackBox {
+ val io = IO(Flipped(new FooModuleIO))
+ }
+ class Top extends Module {
+ val foo = Module(new FooModule)
+ val mirror = Module(new FooMirrorBlackBox)
+ foo.io <> mirror.io
+ }
+ }
+ val chirrtl = emitChirrtl(new Chisel3.Top)
+ chirrtl should include("foo.io.bar <= mirror.bar")
+ chirrtl should include("mirror.foo <= foo.io.foo")
+ chirrtl should include("foo.io.quz.q.bits <- mirror.quz.q.bits")
+ chirrtl should include("foo.io.quz.q.valid <= mirror.quz.q.valid")
+ }
+
+ "A chisel3.Bundle bulk connected to a Chisel Bundle in either direction" should "work even with mismatched fields" in {
+ object Compat {
+ import Chisel._
+ class FooBundle extends Bundle {
+ val foo = UInt(width = 8)
+ }
+ }
+ object Chisel3 {
+ import chisel3._
+ class BarBundle extends Bundle {
+ val bar = UInt(8.W)
+ }
+ class MyModule(swap: Boolean) extends Module {
+ val in = IO(Input(if (swap) new Compat.FooBundle else new BarBundle))
+ val out = IO(Output(if (swap) new BarBundle else new Compat.FooBundle))
+ out <> DontCare
+ out <> in
+ }
+ }
+ val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true))
+ chirrtl0 shouldNot include("<=")
+ chirrtl0 should include("out <- in")
+ val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true))
+ chirrtl1 shouldNot include("<=")
+ chirrtl1 should include("out <- in")
+ }
+
+ it should "work with missing fields in the Chisel._" in {
+ object Compat {
+ import Chisel._
+ class FooBundle extends Bundle {
+ val foo = UInt(width = 8)
+ }
+ }
+ object Chisel3 {
+ import chisel3._
+ class FooBarBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = UInt(8.W)
+ }
+
+ class MyModule(swap: Boolean) extends Module {
+ val in = IO(Input(if (swap) new Compat.FooBundle else new FooBarBundle))
+ val out = IO(Output(if (swap) new FooBarBundle else new Compat.FooBundle))
+ out <> DontCare
+ out <> in
+ }
+ }
+ val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true))
+ chirrtl0 shouldNot include("<=")
+ chirrtl0 should include("out <- in")
+ val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true))
+ chirrtl1 shouldNot include("<=")
+ chirrtl1 should include("out <- in")
+ }
+
+ it should "work with missing fields in the chisel3._" in {
+ object Compat {
+ import Chisel._
+ class FooBundle extends Bundle {
+ val foo = UInt(width = 8)
+ }
+ }
+ object Chisel3 {
+ import chisel3._
+ class FooBarBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = UInt(8.W)
+ }
+
+ class MyModule(swap: Boolean) extends Module {
+ val in = IO(Input(if (swap) new Compat.FooBundle else new FooBarBundle))
+ val out = IO(Output(if (swap) new FooBarBundle else new Compat.FooBundle))
+ out <> DontCare
+ out <> in
+ }
+ }
+ val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true))
+ chirrtl0 shouldNot include("<=")
+ chirrtl0 should include("out <- in")
+ val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true))
+ chirrtl1 shouldNot include("<=")
+ chirrtl1 should include("out <- in")
+ }
+
+ it should "emit FIRRTL connects if possible" in {
+ object Compat {
+ import Chisel._
+ class FooBarBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = Flipped(UInt(8.W))
+ }
+ }
+ object Chisel3 {
+ import chisel3._
+ class FooBarBundle extends Bundle {
+ val foo = Output(UInt(8.W))
+ val bar = Input(UInt(8.W))
+ }
+ class MyModule(swap: Boolean) extends Module {
+ val in = IO(Flipped((if (swap) new Compat.FooBarBundle else new FooBarBundle)))
+ val out = IO(if (swap) new FooBarBundle else new Compat.FooBarBundle)
+ out <> DontCare
+ out <> in
+ }
+ }
+ val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true))
+ chirrtl0 should include("out <= in")
+ chirrtl0 shouldNot include("out <- in")
+ val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true))
+ chirrtl1 should include("out <= in")
+ chirrtl1 shouldNot include("out <- in")
+ }
}