summaryrefslogtreecommitdiff
path: root/core/src/main/scala/chisel3/internal/MonoConnect.scala
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/scala/chisel3/internal/MonoConnect.scala')
-rw-r--r--core/src/main/scala/chisel3/internal/MonoConnect.scala206
1 files changed, 188 insertions, 18 deletions
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,