summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormergify[bot]2022-03-10 01:10:30 +0000
committerGitHub2022-03-10 01:10:30 +0000
commit741761cfbac8d8b7e297666c66d91cb773a6f109 (patch)
treeee5c63cd117b8e8bc93ad3383c6d0981f077f6a9
parent4ee545d7706a2d2ba59902fb86a4393287327a9a (diff)
Emit FIRRTL bulkconnects whenever possible (#2381) (#2440)
Chisel <> semantics differ somewhat from FIRRTL <= semantics, so we only emit <= when it would be legal. Otherwise we continue the old behavior of emitting a connection for every leaf-level Element. Co-authored-by: Deborah Soung <debs@sifive.com> Co-authored-by: Jack Koenig <koenig@sifive.com> (cherry picked from commit 3553a1583403824718923a6cc530cec3b38f5704) Co-authored-by: Jared Barocsi <82000041+jared-barocsi@users.noreply.github.com> Co-authored-by: Jack Koenig <koenig@sifive.com>
-rw-r--r--core/src/main/scala/chisel3/experimental/dataview/package.scala10
-rw-r--r--core/src/main/scala/chisel3/internal/BiConnect.scala130
-rw-r--r--core/src/main/scala/chisel3/internal/MonoConnect.scala206
-rw-r--r--src/test/scala/chiselTests/BulkConnectSpec.scala106
-rw-r--r--src/test/scala/chiselTests/BundleSpec.scala1
-rw-r--r--src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala2
-rw-r--r--src/test/scala/chiselTests/MixedVecSpec.scala16
-rw-r--r--src/test/scala/chiselTests/RecordSpec.scala19
-rw-r--r--src/test/scala/chiselTests/VecLiteralSpec.scala5
-rw-r--r--src/test/scala/chiselTests/experimental/DataView.scala8
10 files changed, 447 insertions, 56 deletions
diff --git a/core/src/main/scala/chisel3/experimental/dataview/package.scala b/core/src/main/scala/chisel3/experimental/dataview/package.scala
index 3278d82c..891ecb81 100644
--- a/core/src/main/scala/chisel3/experimental/dataview/package.scala
+++ b/core/src/main/scala/chisel3/experimental/dataview/package.scala
@@ -262,4 +262,14 @@ package object dataview {
}
}
+ /** Determine the target of a View if it is a single Target
+ *
+ * @note An Aggregate may be a view of unrelated [[Data]] (eg. like a Seq or tuple) and thus this
+ * there is no single Data representing the Target and this function will return None
+ * @return The single Data target of this view or None if a single Data doesn't exist
+ */
+ private[chisel3] def reifyToAggregate(data: Data): Option[Aggregate] = reifySingleData(data) match {
+ case Some(a: Aggregate) => Some(a)
+ case other => None
+ }
}
diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala
index 5b4ad1b9..a8b425f5 100644
--- a/core/src/main/scala/chisel3/internal/BiConnect.scala
+++ b/core/src/main/scala/chisel3/internal/BiConnect.scala
@@ -3,15 +3,14 @@
package chisel3.internal
import chisel3._
-import chisel3.experimental.dataview.reify
+import chisel3.experimental.dataview.{isView, reify, reifyToAggregate}
import chisel3.experimental.{attach, Analog, BaseModule}
import chisel3.internal.Builder.pushCommand
-import chisel3.internal.firrtl.{Connect, DefInvalid}
+import chisel3.internal.firrtl.{Connect, Converter, DefInvalid}
import scala.language.experimental.macros
import chisel3.internal.sourceinfo._
-
-import scala.annotation.tailrec
+import _root_.firrtl.passes.CheckTypes
/**
* BiConnect.connect executes a bidirectional connection element-wise.
@@ -91,12 +90,28 @@ private[chisel3] object BiConnect {
if (left_v.length != right_v.length) {
throw MismatchedVecException
}
- for (idx <- 0 until left_v.length) {
- try {
- implicit val compileOptions = connectCompileOptions
- connect(sourceInfo, connectCompileOptions, left_v(idx), right_v(idx), context_mod)
- } catch {
- case BiConnectException(message) => throw BiConnectException(s"($idx)$message")
+
+ val leftReified: Option[Aggregate] = if (isView(left_v)) reifyToAggregate(left_v) else Some(left_v)
+ val rightReified: Option[Aggregate] = if (isView(right_v)) reifyToAggregate(right_v) else Some(right_v)
+
+ if (
+ leftReified.nonEmpty && rightReified.nonEmpty && canBulkConnectAggregates(
+ leftReified.get,
+ rightReified.get,
+ sourceInfo,
+ connectCompileOptions,
+ context_mod
+ )
+ ) {
+ pushCommand(Connect(sourceInfo, leftReified.get.lref, rightReified.get.lref))
+ } else {
+ for (idx <- 0 until left_v.length) {
+ try {
+ implicit val compileOptions = connectCompileOptions
+ connect(sourceInfo, connectCompileOptions, left_v(idx), right_v(idx), context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s"($idx)$message")
+ }
}
}
}
@@ -122,29 +137,31 @@ private[chisel3] object BiConnect {
}
}
}
- // Handle Records defined in Chisel._ code by emitting a FIRRTL partial connect
+ // Handle Records defined in Chisel._ code by emitting a FIRRTL bulk
+ // connect when possible and a partial connect otherwise
case pair @ (left_r: Record, right_r: Record) =>
val notStrict =
Seq(left_r.compileOptions, right_r.compileOptions).contains(ExplicitCompileOptions.NotStrict)
- if (notStrict) {
- // Traces flow from a child Data to its parent
- @tailrec def traceFlow(currentlyFlipped: Boolean, data: Data): Boolean = {
- import SpecifiedDirection.{Input => SInput, Flip => SFlip}
- val sdir = data.specifiedDirection
- val flipped = sdir == SInput || sdir == SFlip
- data.binding.get match {
- case ChildBinding(parent) => traceFlow(flipped ^ currentlyFlipped, parent)
- case PortBinding(enclosure) =>
- val childPort = enclosure != context_mod
- childPort ^ flipped ^ currentlyFlipped
- case _ => true
- }
- }
- def canBeSink(data: Data): Boolean = traceFlow(true, data)
- def canBeSource(data: Data): Boolean = traceFlow(false, data)
- // chisel3 <> is commutative but FIRRTL <- is not
- val flipConnection = !canBeSink(left_r) || !canBeSource(right_r)
- val (newLeft, newRight) = if (flipConnection) pair.swap else pair
+
+ // chisel3 <> is commutative but FIRRTL <- is not
+ val flipConnection =
+ !MonoConnect.canBeSink(left_r, context_mod) || !MonoConnect.canBeSource(right_r, context_mod)
+ val (newLeft, newRight) = if (flipConnection) (right_r, left_r) else (left_r, right_r)
+
+ val leftReified: Option[Aggregate] = if (isView(newLeft)) reifyToAggregate(newLeft) else Some(newLeft)
+ val rightReified: Option[Aggregate] = if (isView(newRight)) reifyToAggregate(newRight) else Some(newRight)
+
+ if (
+ leftReified.nonEmpty && rightReified.nonEmpty && canBulkConnectAggregates(
+ leftReified.get,
+ rightReified.get,
+ sourceInfo,
+ connectCompileOptions,
+ context_mod
+ )
+ ) {
+ pushCommand(Connect(sourceInfo, leftReified.get.lref, rightReified.get.lref))
+ } else if (notStrict) {
newLeft.bulkConnect(newRight)(sourceInfo, ExplicitCompileOptions.NotStrict)
} else {
recordConnect(sourceInfo, connectCompileOptions, left_r, right_r, context_mod)
@@ -218,6 +235,59 @@ private[chisel3] object BiConnect {
}
}
+ /** Check whether two aggregates can be bulk connected (<=) in FIRRTL. From the
+ * FIRRTL specification, the following must hold for bulk connection:
+ *
+ * 1. The types of the left-hand and right-hand side expressions must be
+ * equivalent.
+ * 2. The bit widths of the two expressions must allow for data to always
+ * flow from a smaller bit width to an equal size or larger bit width.
+ * 3. The flow of the left-hand side expression must be sink or duplex
+ * 4. Either the flow of the right-hand side expression is source or duplex,
+ * or the right-hand side expression has a passive type.
+ */
+ private[chisel3] def canBulkConnectAggregates(
+ sink: Aggregate,
+ source: Aggregate,
+ sourceInfo: SourceInfo,
+ connectCompileOptions: CompileOptions,
+ context_mod: RawModule
+ ): Boolean = {
+
+ // check that the aggregates have the same types
+ def typeCheck = CheckTypes.validConnect(
+ Converter.extractType(sink, sourceInfo),
+ Converter.extractType(source, sourceInfo)
+ )
+
+ // check records live in appropriate contexts
+ def contextCheck =
+ MonoConnect.aggregateConnectContextCheck(
+ sourceInfo,
+ connectCompileOptions,
+ sink,
+ source,
+ context_mod
+ )
+
+ // sink must be writable and must also not be a literal
+ def bindingCheck = sink.topBinding match {
+ case _: ReadOnlyBinding => false
+ case _ => true
+ }
+
+ // check data can flow between provided aggregates
+ def flow_check = MonoConnect.canBeSink(sink, context_mod) && MonoConnect.canBeSource(source, context_mod)
+
+ // do not bulk connect source literals (results in infinite recursion from calling .ref)
+ def sourceNotLiteralCheck = source.topBinding match {
+ case _: LitBinding => false
+ case _ => true
+ }
+
+ typeCheck && contextCheck && bindingCheck && flow_check && sourceNotLiteralCheck
+ }
+
// These functions (finally) issue the connection operation
// Issue with right as sink, left as source
private def issueConnectL2R(left: Element, right: Element)(implicit sourceInfo: SourceInfo): Unit = {
diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala
index b4d9aeff..40056c89 100644
--- a/core/src/main/scala/chisel3/internal/MonoConnect.scala
+++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala
@@ -5,11 +5,13 @@ package chisel3.internal
import chisel3._
import chisel3.experimental.{Analog, BaseModule, EnumType, FixedPoint, Interval, UnsafeEnum}
import chisel3.internal.Builder.pushCommand
-import chisel3.experimental.dataview.reify
-import chisel3.internal.firrtl.{Connect, DefInvalid}
+import chisel3.internal.firrtl.{Connect, Converter, DefInvalid}
+import chisel3.experimental.dataview.{isView, reify, reifyToAggregate}
import scala.language.experimental.macros
import chisel3.internal.sourceinfo.SourceInfo
+import _root_.firrtl.passes.CheckTypes
+import scala.annotation.tailrec
/**
* MonoConnect.connect executes a mono-directional connection element-wise.
@@ -129,12 +131,28 @@ private[chisel3] object MonoConnect {
// Handle Vec case
case (sink_v: Vec[Data @unchecked], source_v: Vec[Data @unchecked]) =>
if (sink_v.length != source_v.length) { throw MismatchedVecException }
- for (idx <- 0 until sink_v.length) {
- try {
- implicit val compileOptions = connectCompileOptions
- connect(sourceInfo, connectCompileOptions, sink_v(idx), source_v(idx), context_mod)
- } catch {
- case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message")
+
+ val sinkReified: Option[Aggregate] = if (isView(sink_v)) reifyToAggregate(sink_v) else Some(sink_v)
+ val sourceReified: Option[Aggregate] = if (isView(source_v)) reifyToAggregate(source_v) else Some(source_v)
+
+ if (
+ sinkReified.nonEmpty && sourceReified.nonEmpty && canBulkConnectAggregates(
+ sinkReified.get,
+ sourceReified.get,
+ sourceInfo,
+ connectCompileOptions,
+ context_mod
+ )
+ ) {
+ pushCommand(Connect(sourceInfo, sinkReified.get.lref, sourceReified.get.ref))
+ } else {
+ for (idx <- 0 until sink_v.length) {
+ try {
+ implicit val compileOptions = connectCompileOptions
+ connect(sourceInfo, connectCompileOptions, sink_v(idx), source_v(idx), context_mod)
+ } catch {
+ case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message")
+ }
}
}
// Handle Vec connected to DontCare. Apply the DontCare to individual elements.
@@ -150,19 +168,34 @@ private[chisel3] object MonoConnect {
// Handle Record case
case (sink_r: Record, source_r: Record) =>
- // For each field, descend with right
- for ((field, sink_sub) <- sink_r.elements) {
- try {
- source_r.elements.get(field) match {
- case Some(source_sub) => connect(sourceInfo, connectCompileOptions, sink_sub, source_sub, context_mod)
- case None => {
- if (connectCompileOptions.connectFieldsMustMatch) {
- throw MissingFieldException(field)
+ val sinkReified: Option[Aggregate] = if (isView(sink_r)) reifyToAggregate(sink_r) else Some(sink_r)
+ val sourceReified: Option[Aggregate] = if (isView(source_r)) reifyToAggregate(source_r) else Some(source_r)
+
+ if (
+ sinkReified.nonEmpty && sourceReified.nonEmpty && canBulkConnectAggregates(
+ sinkReified.get,
+ sourceReified.get,
+ sourceInfo,
+ connectCompileOptions,
+ context_mod
+ )
+ ) {
+ pushCommand(Connect(sourceInfo, sinkReified.get.lref, sourceReified.get.ref))
+ } else {
+ // For each field, descend with right
+ for ((field, sink_sub) <- sink_r.elements) {
+ try {
+ source_r.elements.get(field) match {
+ case Some(source_sub) => connect(sourceInfo, connectCompileOptions, sink_sub, source_sub, context_mod)
+ case None => {
+ if (connectCompileOptions.connectFieldsMustMatch) {
+ throw MissingFieldException(field)
+ }
}
}
+ } catch {
+ case MonoConnectException(message) => throw MonoConnectException(s".$field$message")
}
- } catch {
- case MonoConnectException(message) => throw MonoConnectException(s".$field$message")
}
}
// Handle Record connected to DontCare. Apply the DontCare to individual elements.
@@ -190,6 +223,143 @@ private[chisel3] object MonoConnect {
case (sink, source) => throw MismatchedException(sink, source)
}
+ /** Determine if a valid connection can be made between a source [[Aggregate]] and sink
+ * [[Aggregate]] given their parent module and directionality context
+ *
+ * @return whether the source and sink exist in an appropriate context to be connected
+ */
+ private[chisel3] def aggregateConnectContextCheck(
+ implicit sourceInfo: SourceInfo,
+ connectCompileOptions: CompileOptions,
+ sink: Aggregate,
+ source: Aggregate,
+ context_mod: RawModule
+ ): Boolean = {
+ import ActualDirection.{Bidirectional, Input, Output}
+ // If source has no location, assume in context module
+ // This can occur if is a literal, unbound will error previously
+ val sink_mod: BaseModule = sink.topBinding.location.getOrElse(throw UnwritableSinkException(sink, source))
+ val source_mod: BaseModule = source.topBinding.location.getOrElse(context_mod)
+
+ val sink_parent = Builder.retrieveParent(sink_mod, context_mod).getOrElse(None)
+ val source_parent = Builder.retrieveParent(source_mod, context_mod).getOrElse(None)
+
+ val sink_is_port = sink.topBinding match {
+ case PortBinding(_) => true
+ case _ => false
+ }
+ val source_is_port = source.topBinding match {
+ case PortBinding(_) => true
+ case _ => false
+ }
+
+ if (!checkWhenVisibility(sink)) {
+ throw SinkEscapedWhenScopeException(sink)
+ }
+
+ if (!checkWhenVisibility(source)) {
+ throw SourceEscapedWhenScopeException(source)
+ }
+
+ // CASE: Context is same module that both sink node and source node are in
+ if ((context_mod == sink_mod) && (context_mod == source_mod)) {
+ sink.direction != Input
+ }
+
+ // CASE: Context is same module as sink node and source node is in a child module
+ else if ((sink_mod == context_mod) && (source_parent == context_mod)) {
+ // NOTE: Workaround for bulk connecting non-agnostified FIRRTL ports
+ // See: https://github.com/freechipsproject/firrtl/issues/1703
+ // Original behavior should just check if the sink direction is an Input
+ val sinkCanBeInput = sink.direction match {
+ case Input => true
+ case Bidirectional(_) => true
+ case _ => false
+ }
+ // Thus, right node better be a port node and thus have a direction
+ if (!source_is_port) { !connectCompileOptions.dontAssumeDirectionality }
+ else if (sinkCanBeInput) {
+ if (source.direction == Output) {
+ !connectCompileOptions.dontTryConnectionsSwapped
+ } else { false }
+ } else { true }
+ }
+
+ // CASE: Context is same module as source node and sink node is in child module
+ else if ((source_mod == context_mod) && (sink_parent == context_mod)) {
+ // NOTE: Workaround for bulk connecting non-agnostified FIRRTL ports
+ // See: https://github.com/freechipsproject/firrtl/issues/1703
+ // Original behavior should just check if the sink direction is an Input
+ sink.direction match {
+ case Input => true
+ case Bidirectional(_) => true
+ case _ => false
+ }
+ }
+
+ // CASE: Context is the parent module of both the module containing sink node
+ // and the module containing source node
+ // Note: This includes case when sink and source in same module but in parent
+ else if ((sink_parent == context_mod) && (source_parent == context_mod)) {
+ // Thus both nodes must be ports and have a direction
+ if (!source_is_port) { !connectCompileOptions.dontAssumeDirectionality }
+ else if (sink_is_port) {
+ // NOTE: Workaround for bulk connecting non-agnostified FIRRTL ports
+ // See: https://github.com/freechipsproject/firrtl/issues/1703
+ // Original behavior should just check if the sink direction is an Input
+ sink.direction match {
+ case Input => true
+ case Bidirectional(_) => true // NOTE: Workaround for non-agnostified ports
+ case _ => false
+ }
+ } else { false }
+ }
+
+ // Not quite sure where left and right are compared to current module
+ // so just error out
+ 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}
+ val sdir = data.specifiedDirection
+ val flipped = sdir == SInput || sdir == SFlip
+ data.binding.get match {
+ case ChildBinding(parent) => traceFlow(flipped ^ currentlyFlipped, parent, context_mod)
+ case PortBinding(enclosure) =>
+ val childPort = enclosure != context_mod
+ childPort ^ flipped ^ currentlyFlipped
+ 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)
+
+ /** Check whether two aggregates can be bulk connected (<=) in FIRRTL. (MonoConnect case)
+ *
+ * Mono-directional bulk connects only work if all signals of the sink are unidirectional
+ * In the case of a sink aggregate with bidirectional signals, e.g. `Decoupled`,
+ * a `BiConnect` is necessary.
+ */
+ private[chisel3] def canBulkConnectAggregates(
+ sink: Aggregate,
+ source: Aggregate,
+ sourceInfo: SourceInfo,
+ connectCompileOptions: CompileOptions,
+ context_mod: RawModule
+ ): Boolean = {
+ // Assuming we're using a <>, check if a bulk connect is valid in that case
+ def biConnectCheck =
+ BiConnect.canBulkConnectAggregates(sink, source, sourceInfo, connectCompileOptions, context_mod)
+
+ // Check that the Aggregate can be driven (not bidirectional or an input) to match Chisel semantics
+ def sinkCanBeDrivenCheck: Boolean =
+ sink.direction == ActualDirection.Output || sink.direction == ActualDirection.Unspecified
+
+ biConnectCheck && sinkCanBeDrivenCheck
+ }
+
// This function (finally) issues the connection operation
private def issueConnect(sink: Element, source: Element)(implicit sourceInfo: SourceInfo): Unit = {
// If the source is a DontCare, generate a DefInvalid for the sink,
diff --git a/src/test/scala/chiselTests/BulkConnectSpec.scala b/src/test/scala/chiselTests/BulkConnectSpec.scala
new file mode 100644
index 00000000..463122bd
--- /dev/null
+++ b/src/test/scala/chiselTests/BulkConnectSpec.scala
@@ -0,0 +1,106 @@
+package chiselTests
+
+import chisel3._
+import chisel3.util.Decoupled
+import chisel3.stage.ChiselStage
+import chisel3.testers.BasicTester
+
+class BulkConnectSpec extends ChiselPropSpec {
+ property("Chisel connects should emit FIRRTL bulk connects when possible") {
+ val chirrtl = ChiselStage.emitChirrtl(new Module {
+ val io = IO(new Bundle {
+ val inMono = Input(Vec(4, UInt(8.W)))
+ val outMono = Output(Vec(4, UInt(8.W)))
+ val inBi = Input(Vec(4, UInt(8.W)))
+ val outBi = Output(Vec(4, UInt(8.W)))
+ })
+ io.outMono := io.inMono
+ io.outBi <> io.inBi
+ })
+ chirrtl should include("io.outMono <= io.inMono")
+ chirrtl should include("io.outBi <= io.inBi")
+ }
+
+ property("Chisel connects should not emit FIRRTL bulk connects for Stringly-typed connections") {
+ object Foo {
+ import Chisel._
+ // Chisel._ bundle
+ class BundleParent extends Bundle {
+ val foo = UInt(width = 8)
+ }
+ class BundleChild extends BundleParent {
+ val bar = UInt(width = 8)
+ }
+ }
+
+ import Foo._
+
+ // chisel3._ bundle
+ class MyBundle(child: Boolean) extends Bundle {
+ val fizz = UInt(8.W)
+ val buzz = if (child) new BundleChild else new BundleParent
+ }
+
+ val chirrtl = ChiselStage.emitChirrtl(new Module {
+ // Checking MonoConnect
+ val in = IO(Input(new MyBundle(true)))
+ val out = IO(Output(new MyBundle(false)))
+ out := in
+
+ // Checking BulkConnect (with Decoupled)
+ val enq = IO(Flipped(Decoupled(new BundleChild)))
+ val deq = IO(Decoupled(new BundleParent))
+ deq <> enq
+ })
+
+ chirrtl should include("out.buzz.foo <= in.buzz.foo")
+ chirrtl shouldNot include("deq <= enq")
+ }
+
+ property("Chisel connects should not emit FIRRTL bulk connects between differing FIRRTL types") {
+ val chirrtl = ChiselStage.emitChirrtl(new Module {
+ val in = IO(Flipped(new Bundle {
+ val foo = Flipped(new Bundle {
+ val bar = Input(UInt(8.W))
+ })
+ }))
+ val out = IO(Output(new Bundle {
+ val foo = new Bundle {
+ val bar = UInt(8.W)
+ }
+ }))
+ // Both of these connections are legal in Chisel, but in and out do not have the same type
+ out := in
+ out <> in
+ })
+ // out <- in is illegal FIRRTL
+ chirrtl should include("out.foo.bar <= in.foo.bar")
+ }
+
+ property("Chisel connects should not emit a FIRRTL bulk connect for a bidirectional MonoConnect") {
+ val chirrtl = ChiselStage.emitChirrtl(new Module {
+ val enq = IO(Flipped(Decoupled(UInt(8.W))))
+ val deq = IO(Decoupled(UInt(8.W)))
+
+ // Implicitly create a MonoConnect from enq to a wire
+ // enq is a Decoupled and so has input/output signals
+ // We should not bulk connect in this case
+ val wire = WireDefault(enq)
+ dontTouch(wire)
+ deq <> enq
+ })
+
+ chirrtl shouldNot include("wire <= enq")
+ chirrtl should include("deq <= enq")
+ }
+
+ property("MonoConnect should bulk connect undirectioned internal wires") {
+ val chirrtl = ChiselStage.emitChirrtl(new Module {
+ val io = IO(new Bundle {})
+ val w1 = Wire(Vec(2, UInt(8.W)))
+ val w2 = Wire(Vec(2, UInt(8.W)))
+ w2 := w1
+ })
+ chirrtl should include("w2 <= w1")
+ }
+}
diff --git a/src/test/scala/chiselTests/BundleSpec.scala b/src/test/scala/chiselTests/BundleSpec.scala
index 5dcbbefa..2d34b263 100644
--- a/src/test/scala/chiselTests/BundleSpec.scala
+++ b/src/test/scala/chiselTests/BundleSpec.scala
@@ -3,6 +3,7 @@
package chiselTests
import chisel3._
+import chisel3.util.Decoupled
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
index 8210b120..70dcda48 100644
--- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
+++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
@@ -74,7 +74,7 @@ object Chisel3Components {
class Chisel3ModuleChiselRecordB extends Chisel3PassthroughModule(Flipped(new ChiselRecord))
}
-class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec {
+class CompatibilityInteroperabilitySpec extends ChiselFlatSpec {
"Modules defined in the Chisel._" should "successfully bulk connect in chisel3._" in {
import chisel3._
diff --git a/src/test/scala/chiselTests/MixedVecSpec.scala b/src/test/scala/chiselTests/MixedVecSpec.scala
index 16efafd4..ee19d653 100644
--- a/src/test/scala/chiselTests/MixedVecSpec.scala
+++ b/src/test/scala/chiselTests/MixedVecSpec.scala
@@ -280,4 +280,20 @@ class MixedVecSpec extends ChiselPropSpec with Utils {
})
}
}
+
+ property("MixedVec connections should emit FIRRTL bulk connects when possible") {
+ val chirrtl = ChiselStage.emitChirrtl(new Module {
+ val io = IO(new Bundle {
+ val inMono = Input(MixedVec(Seq(UInt(8.W), UInt(16.W), UInt(4.W), UInt(7.W))))
+ val outMono = Output(MixedVec(Seq(UInt(8.W), UInt(16.W), UInt(4.W), UInt(7.W))))
+ val inBi = Input(MixedVec(Seq(UInt(8.W), UInt(16.W), UInt(4.W), UInt(7.W))))
+ val outBi = Output(MixedVec(Seq(UInt(8.W), UInt(16.W), UInt(4.W), UInt(7.W))))
+ })
+ // Explicit upcast avoids weird issue where Scala 2.12 overloading resolution calls version of := accepting Seq[T] instead of normal Data version
+ io.outMono := (io.inMono: Data)
+ io.outBi <> io.inBi
+ })
+ chirrtl should include("io.outMono <= io.inMono @[MixedVecSpec.scala")
+ chirrtl should include("io.outBi <= io.inBi @[MixedVecSpec.scala")
+ }
}
diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala
index da3840dd..30b55812 100644
--- a/src/test/scala/chiselTests/RecordSpec.scala
+++ b/src/test/scala/chiselTests/RecordSpec.scala
@@ -27,6 +27,17 @@ trait RecordSpecUtils {
io.out <> io.in
}
+ class ConnectionTestModule(output: => Record, input: => Record) extends Module {
+ val io = IO(new Bundle {
+ val inMono = Input(input)
+ val outMono = Output(output)
+ val inBi = Input(input)
+ val outBi = Output(output)
+ })
+ io.outMono := io.inMono
+ io.outBi <> io.inBi
+ }
+
class RecordSerializationTest extends BasicTester {
val recordType = new CustomBundle("fizz" -> UInt(16.W), "buzz" -> UInt(16.W))
val record = Wire(recordType)
@@ -110,6 +121,14 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils {
ChiselStage.elaborate { new MyModule(new MyBundle, fooBarType) }
}
+ they should "emit FIRRTL bulk connects when possible" in {
+ val chirrtl = (new ChiselStage).emitChirrtl(
+ gen = new ConnectionTestModule(fooBarType, fooBarType)
+ )
+ chirrtl should include("io.outMono <= io.inMono @[RecordSpec.scala")
+ chirrtl should include("io.outBi <= io.inBi @[RecordSpec.scala")
+ }
+
they should "not allow aliased fields" in {
class AliasedFieldRecord extends Record {
val foo = UInt(8.W)
diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala
index 228f409b..fa97a8c8 100644
--- a/src/test/scala/chiselTests/VecLiteralSpec.scala
+++ b/src/test/scala/chiselTests/VecLiteralSpec.scala
@@ -434,7 +434,7 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils {
exc.getMessage should include("field 0 specified with non-literal value UInt")
}
- "vec literals are instantiated on connect" in {
+ "vec literals are instantiated on connect and are not bulk connected" in {
class VecExample5 extends RawModule {
val out = IO(Output(Vec(2, UInt(4.W))))
val bundle = Vec(2, UInt(4.W)).Lit(
@@ -463,13 +463,12 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils {
out := bundle
}
- "vec literals can contain bundles" in {
+ "vec literals can contain bundles and should not be bulk connected" in {
val chirrtl = (new chisel3.stage.ChiselStage).emitChirrtl(new VecExample, args = Array("--full-stacktrace"))
chirrtl should include("""out[0].bar <= UInt<5>("h16")""")
chirrtl should include("""out[0].foo <= UInt<6>("h2a")""")
chirrtl should include("""out[1].bar <= UInt<2>("h3")""")
chirrtl should include("""out[1].foo <= UInt<3>("h7")""")
-
}
"vec literals can have bundle children" in {
diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala
index 5ef062fa..0285a524 100644
--- a/src/test/scala/chiselTests/experimental/DataView.scala
+++ b/src/test/scala/chiselTests/experimental/DataView.scala
@@ -103,8 +103,8 @@ class DataViewSpec extends ChiselFlatSpec {
buzz.viewAs[MyBundle] := in
}
val chirrtl = ChiselStage.emitChirrtl(new MyModule)
- chirrtl should include("fizz.foo <= in.foo")
- chirrtl should include("buzz.foo <= in.foo")
+ chirrtl should include("fizz <= in")
+ chirrtl should include("buzz <= in")
}
it should "handle viewing Vecs as their same concrete type" in {
@@ -116,8 +116,8 @@ class DataViewSpec extends ChiselFlatSpec {
buzz.viewAs[Vec[UInt]] := in
}
val chirrtl = ChiselStage.emitChirrtl(new MyModule)
- chirrtl should include("fizz[0] <= in[0]")
- chirrtl should include("buzz[0] <= in[0]")
+ chirrtl should include("fizz <= in")
+ chirrtl should include("buzz <= in")
}
it should "handle viewing Vecs as Bundles and vice versa" in {