summaryrefslogtreecommitdiff
path: root/chiselFrontend/src/main/scala/chisel3/internal
diff options
context:
space:
mode:
Diffstat (limited to 'chiselFrontend/src/main/scala/chisel3/internal')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/BiConnect.scala332
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Binding.scala106
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Builder.scala25
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Error.scala4
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala246
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala4
6 files changed, 700 insertions, 17 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/BiConnect.scala b/chiselFrontend/src/main/scala/chisel3/internal/BiConnect.scala
new file mode 100644
index 00000000..e122f13a
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/internal/BiConnect.scala
@@ -0,0 +1,332 @@
+// See LICENSE for license details.
+
+package chisel3.internal
+
+import chisel3._
+import chisel3.experimental.{Analog, BaseModule, RawModule, attach}
+import chisel3.internal.Builder.pushCommand
+import chisel3.internal.firrtl.{Connect, DefInvalid}
+import scala.language.experimental.macros
+import chisel3.internal.sourceinfo._
+
+/**
+* BiConnect.connect executes a bidirectional connection element-wise.
+*
+* Note that the arguments are left and right (not source and sink) so the
+* intent is for the operation to be commutative.
+*
+* The connect operation will recurse down the left Data (with the right Data).
+* An exception will be thrown if a movement through the left cannot be matched
+* in the right (or if the right side has extra fields).
+*
+* See elemConnect for details on how the root connections are issued.
+*
+*/
+
+private[chisel3] object BiConnect {
+ // scalastyle:off method.name public.methods.have.type
+ // These are all the possible exceptions that can be thrown.
+ // These are from element-level connection
+ def BothDriversException =
+ BiConnectException(": Both Left and Right are drivers")
+ def NeitherDriverException =
+ BiConnectException(": Neither Left nor Right is a driver")
+ def UnknownDriverException =
+ BiConnectException(": Locally unclear whether Left or Right (both internal)")
+ def UnknownRelationException =
+ BiConnectException(": Left or Right unavailable to current module.")
+ // These are when recursing down aggregate types
+ def MismatchedVecException =
+ BiConnectException(": Left and Right are different length Vecs.")
+ def MissingLeftFieldException(field: String) =
+ BiConnectException(s".$field: Left Record missing field ($field).")
+ def MissingRightFieldException(field: String) =
+ BiConnectException(s": Right Record missing field ($field).")
+ def MismatchedException(left: String, right: String) =
+ BiConnectException(s": Left ($left) and Right ($right) have different types.")
+ def AttachAlreadyBulkConnectedException(sourceInfo: SourceInfo) =
+ BiConnectException(sourceInfo.makeMessage(": Analog previously bulk connected at " + _))
+ def DontCareCantBeSink =
+ BiConnectException(": DontCare cannot be a connection sink (LHS)")
+ // scalastyle:on method.name public.methods.have.type
+
+ /** This function is what recursively tries to connect a left and right together
+ *
+ * There is some cleverness in the use of internal try-catch to catch exceptions
+ * during the recursive decent and then rethrow them with extra information added.
+ * This gives the user a 'path' to where in the connections things went wrong.
+ */
+ def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Data, right: Data, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit cyclomatic.complexity method.length
+ (left, right) match {
+ // Handle element case (root case)
+ case (left_a: Analog, right_a: Analog) =>
+ try {
+ analogAttach(sourceInfo, left_a, right_a, context_mod)
+ } catch {
+ // If attach fails, convert to BiConnectException
+ case attach.AttachException(message) => throw BiConnectException(message)
+ }
+ case (left_e: Element, right_e: Element) => {
+ elemConnect(sourceInfo, connectCompileOptions, left_e, right_e, context_mod)
+ // TODO(twigg): Verify the element-level classes are connectable
+ }
+ // Handle Vec case
+ case (left_v: Vec[Data@unchecked], right_v: Vec[Data@unchecked]) => {
+ 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")
+ }
+ }
+ }
+ // Handle Vec connected to DontCare
+ case (left_v: Vec[Data@unchecked], DontCare) => {
+ for (idx <- 0 until left_v.length) {
+ try {
+ implicit val compileOptions = connectCompileOptions
+ connect(sourceInfo, connectCompileOptions, left_v(idx), right, context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s"($idx)$message")
+ }
+ }
+ }
+ // Handle DontCare connected to Vec
+ case (DontCare, right_v: Vec[Data@unchecked]) => {
+ for (idx <- 0 until right_v.length) {
+ try {
+ implicit val compileOptions = connectCompileOptions
+ connect(sourceInfo, connectCompileOptions, left, right_v(idx), context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s"($idx)$message")
+ }
+ }
+ }
+ // Handle Records defined in Chisel._ code (change to NotStrict)
+ case (left_r: Record, right_r: Record) => (left_r.compileOptions, right_r.compileOptions) match {
+ case (ExplicitCompileOptions.NotStrict, _) =>
+ left_r.bulkConnect(right_r)(sourceInfo, ExplicitCompileOptions.NotStrict)
+ case (_, ExplicitCompileOptions.NotStrict) =>
+ left_r.bulkConnect(right_r)(sourceInfo, ExplicitCompileOptions.NotStrict)
+ case _ => recordConnect(sourceInfo, connectCompileOptions, left_r, right_r, context_mod)
+ }
+
+ // Handle Records connected to DontCare (change to NotStrict)
+ case (left_r: Record, DontCare) =>
+ left_r.compileOptions match {
+ case ExplicitCompileOptions.NotStrict =>
+ left.bulkConnect(right)(sourceInfo, ExplicitCompileOptions.NotStrict)
+ case _ =>
+ // For each field in left, descend with right
+ for ((field, left_sub) <- left_r.elements) {
+ try {
+ connect(sourceInfo, connectCompileOptions, left_sub, right, context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s".$field$message")
+ }
+ }
+ }
+ case (DontCare, right_r: Record) =>
+ right_r.compileOptions match {
+ case ExplicitCompileOptions.NotStrict =>
+ left.bulkConnect(right)(sourceInfo, ExplicitCompileOptions.NotStrict)
+ case _ =>
+ // For each field in left, descend with right
+ for ((field, right_sub) <- right_r.elements) {
+ try {
+ connect(sourceInfo, connectCompileOptions, left, right_sub, context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s".$field$message")
+ }
+ }
+ }
+
+ // Left and right are different subtypes of Data so fail
+ case (left, right) => throw MismatchedException(left.toString, right.toString)
+ }
+ }
+
+ // Do connection of two Records
+ def recordConnect(sourceInfo: SourceInfo,
+ connectCompileOptions: CompileOptions,
+ left_r: Record,
+ right_r: Record,
+ 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) {
+ throw MissingLeftFieldException(field)
+ }
+ }
+ }
+ // For each field in left, descend with right
+ for((field, left_sub) <- left_r.elements) {
+ try {
+ right_r.elements.get(field) match {
+ case Some(right_sub) => connect(sourceInfo, connectCompileOptions, left_sub, right_sub, context_mod)
+ case None => {
+ if (connectCompileOptions.connectFieldsMustMatch) {
+ throw MissingRightFieldException(field)
+ }
+ }
+ }
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s".$field$message")
+ }
+ }
+ }
+
+
+ // 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 = {
+ // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>).
+ // If either is a DontCareBinding, just issue a DefInvalid for the other,
+ // otherwise, issue a Connect.
+ (left.topBinding, right.topBinding) match {
+ case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref))
+ case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref))
+ case (_, _) => pushCommand(Connect(sourceInfo, right.lref, left.ref))
+ }
+ }
+ // Issue with left as sink, right as source
+ private def issueConnectR2L(left: Element, right: Element)(implicit sourceInfo: SourceInfo): Unit = {
+ // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>).
+ // If either is a DontCareBinding, just issue a DefInvalid for the other,
+ // otherwise, issue a Connect.
+ (left.topBinding, right.topBinding) match {
+ case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref))
+ case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref))
+ case (_, _) => pushCommand(Connect(sourceInfo, left.lref, right.ref))
+ }
+ }
+
+ // This function checks if element-level connection operation allowed.
+ // Then it either issues it or throws the appropriate exception.
+ def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit cyclomatic.complexity method.length
+ import BindingDirection.{Internal, Input, Output} // Using extensively so import these
+ // If left or right have no location, assume in context module
+ // This can occur if one of them is a literal, unbound will error previously
+ val left_mod: BaseModule = left.topBinding.location.getOrElse(context_mod)
+ val right_mod: BaseModule = right.topBinding.location.getOrElse(context_mod)
+
+ val left_direction = BindingDirection.from(left.topBinding, left.direction)
+ val right_direction = BindingDirection.from(right.topBinding, right.direction)
+
+ // CASE: Context is same module as left node and right node is in a child module
+ if( (left_mod == context_mod) &&
+ (right_mod._parent.map(_ == context_mod).getOrElse(false)) ) {
+ // Thus, right node better be a port node and thus have a direction hint
+ ((left_direction, right_direction): @unchecked) match {
+ // CURRENT MOD CHILD MOD
+ case (Input, Input) => issueConnectL2R(left, right)
+ case (Internal, Input) => issueConnectL2R(left, right)
+
+ case (Output, Output) => issueConnectR2L(left, right)
+ case (Internal, Output) => issueConnectR2L(left, right)
+
+ case (Input, Output) => throw BothDriversException
+ case (Output, Input) => throw NeitherDriverException
+ case (_, Internal) => throw UnknownRelationException
+ }
+ }
+
+ // CASE: Context is same module as right node and left node is in child module
+ else if( (right_mod == context_mod) &&
+ (left_mod._parent.map(_ == context_mod).getOrElse(false)) ) {
+ // Thus, left node better be a port node and thus have a direction hint
+ ((left_direction, right_direction): @unchecked) match {
+ // CHILD MOD CURRENT MOD
+ case (Input, Input) => issueConnectR2L(left, right)
+ case (Input, Internal) => issueConnectR2L(left, right)
+
+ case (Output, Output) => issueConnectL2R(left, right)
+ case (Output, Internal) => issueConnectL2R(left, right)
+
+ case (Input, Output) => throw NeitherDriverException
+ case (Output, Input) => throw BothDriversException
+ case (Internal, _) => throw UnknownRelationException
+ }
+ }
+
+ // CASE: Context is same module that both left node and right node are in
+ else if( (context_mod == left_mod) && (context_mod == right_mod) ) {
+ ((left_direction, right_direction): @unchecked) match {
+ // CURRENT MOD CURRENT MOD
+ case (Input, Output) => issueConnectL2R(left, right)
+ case (Input, Internal) => issueConnectL2R(left, right)
+ case (Internal, Output) => issueConnectL2R(left, right)
+
+ case (Output, Input) => issueConnectR2L(left, right)
+ case (Output, Internal) => issueConnectR2L(left, right)
+ case (Internal, Input) => issueConnectR2L(left, right)
+
+ case (Input, Input) => throw BothDriversException
+ case (Output, Output) => throw BothDriversException
+ case (Internal, Internal) => {
+ if (connectCompileOptions.dontAssumeDirectionality) {
+ throw UnknownDriverException
+ } else {
+ issueConnectR2L(left, right)
+ }
+ }
+ }
+ }
+
+ // CASE: Context is the parent module of both the module containing left node
+ // and the module containing right node
+ // Note: This includes case when left and right in same module but in parent
+ else if( (left_mod._parent.map(_ == context_mod).getOrElse(false)) &&
+ (right_mod._parent.map(_ == context_mod).getOrElse(false))
+ ) {
+ // Thus both nodes must be ports and have a direction hint
+ ((left_direction, right_direction): @unchecked) match {
+ // CHILD MOD CHILD MOD
+ case (Input, Output) => issueConnectR2L(left, right)
+ case (Output, Input) => issueConnectL2R(left, right)
+
+ case (Input, Input) => throw NeitherDriverException
+ case (Output, Output) => throw BothDriversException
+ case (_, Internal) =>
+ if (connectCompileOptions.dontAssumeDirectionality) {
+ throw UnknownRelationException
+ } else {
+ issueConnectR2L(left, right)
+ }
+ case (Internal, _) =>
+ if (connectCompileOptions.dontAssumeDirectionality) {
+ throw UnknownRelationException
+ } else {
+ issueConnectR2L(left, right)
+ }
+ }
+ }
+
+ // Not quite sure where left and right are compared to current module
+ // so just error out
+ else throw UnknownRelationException
+ }
+
+ // This function checks if analog element-level attaching is allowed
+ // Then it either issues it or throws the appropriate exception.
+ def analogAttach(implicit sourceInfo: SourceInfo, left: Analog, right: Analog, contextModule: RawModule): Unit = {
+ // Error if left or right is BICONNECTED in the current module already
+ for (elt <- left :: right :: Nil) {
+ elt.biConnectLocs.get(contextModule) match {
+ case Some(sl) => throw AttachAlreadyBulkConnectedException(sl)
+ case None => // Do nothing
+ }
+ }
+
+ // Do the attachment
+ attach.impl(Seq(left, right), contextModule)
+ // Mark bulk connected
+ left.biConnectLocs(contextModule) = sourceInfo
+ right.biConnectLocs(contextModule) = sourceInfo
+ }
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Binding.scala b/chiselFrontend/src/main/scala/chisel3/internal/Binding.scala
new file mode 100644
index 00000000..34de36a3
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Binding.scala
@@ -0,0 +1,106 @@
+// See LICENSE for license details.
+
+package chisel3.internal
+
+import chisel3._
+import chisel3.experimental.{BaseModule, RawModule}
+import chisel3.internal.firrtl.LitArg
+
+/** Requires that a node is hardware ("bound")
+ */
+object requireIsHardware {
+ def apply(node: Data, msg: String = ""): Unit = {
+ node._parent match { // Compatibility layer hack
+ case Some(x: BaseModule) => x._compatAutoWrapPorts
+ case _ =>
+ }
+ if (!node.isSynthesizable) {
+ val prefix = if (msg.nonEmpty) s"$msg " else ""
+ throw ExpectedHardwareException(s"$prefix'$node' must be hardware, " +
+ "not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?")
+ }
+ }
+}
+
+/** Requires that a node is a chisel type (not hardware, "unbound")
+ */
+object requireIsChiselType {
+ def apply(node: Data, msg: String = ""): Unit = if (node.isSynthesizable) {
+ val prefix = if (msg.nonEmpty) s"$msg " else ""
+ throw ExpectedChiselTypeException(s"$prefix'$node' must be a Chisel type, not hardware")
+ }
+}
+
+// Element only direction used for the Binding system only.
+private[chisel3] sealed abstract class BindingDirection
+private[chisel3] object BindingDirection {
+ /** Internal type or wire
+ */
+ case object Internal extends BindingDirection
+ /** Module port with output direction
+ */
+ case object Output extends BindingDirection
+ /** Module port with input direction
+ */
+ case object Input extends BindingDirection
+
+ /** Determine the BindingDirection of an Element given its top binding and resolved direction.
+ */
+ def from(binding: TopBinding, direction: ActualDirection): BindingDirection = {
+ binding match {
+ case PortBinding(_) => direction match {
+ case ActualDirection.Output => Output
+ case ActualDirection.Input => Input
+ case dir => throw new RuntimeException(s"Unexpected port element direction '$dir'")
+ }
+ case _ => Internal
+ }
+ }
+}
+
+// Location refers to 'where' in the Module hierarchy this lives
+sealed trait Binding {
+ def location: Option[BaseModule]
+}
+// Top-level binding representing hardware, not a pointer to another binding (like ChildBinding)
+sealed trait TopBinding extends Binding
+
+// Constrained-ness refers to whether 'bound by Module boundaries'
+// An unconstrained binding, like a literal, can be read by everyone
+sealed trait UnconstrainedBinding extends TopBinding {
+ def location: Option[BaseModule] = None
+}
+// A constrained binding can only be read/written by specific modules
+// Location will track where this Module is, and the bound object can be referenced in FIRRTL
+sealed trait ConstrainedBinding extends TopBinding {
+ def enclosure: BaseModule
+ def location: Option[BaseModule] = Some(enclosure)
+}
+
+// A binding representing a data that cannot be (re)assigned to.
+sealed trait ReadOnlyBinding extends TopBinding
+
+// TODO(twigg): Ops between unenclosed nodes can also be unenclosed
+// However, Chisel currently binds all op results to a module
+case class OpBinding(enclosure: RawModule) extends ConstrainedBinding with ReadOnlyBinding
+case class MemoryPortBinding(enclosure: RawModule) extends ConstrainedBinding
+case class PortBinding(enclosure: BaseModule) extends ConstrainedBinding
+case class RegBinding(enclosure: RawModule) extends ConstrainedBinding
+case class WireBinding(enclosure: RawModule) extends ConstrainedBinding
+
+case class ChildBinding(parent: Data) extends Binding {
+ def location: Option[BaseModule] = parent.topBinding.location
+}
+/** Special binding for Vec.sample_element */
+case class SampleElementBinding[T <: Data](parent: Vec[T]) extends Binding {
+ def location = parent.topBinding.location
+}
+// A DontCare element has a specific Binding, somewhat like a literal.
+// It is a source (RHS). It may only be connected/applied to sinks.
+case class DontCareBinding() extends UnconstrainedBinding
+
+sealed trait LitBinding extends UnconstrainedBinding with ReadOnlyBinding
+// Literal binding attached to a element that is not part of a Bundle.
+case class ElementLitBinding(litArg: LitArg) extends LitBinding
+// Literal binding attached to the root of a Bundle, containing literal values of its children.
+case class BundleLitBinding(litMap: Map[Data, LitArg]) extends LitBinding
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
index d825f39d..664813f7 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
@@ -3,10 +3,11 @@
package chisel3.internal
import scala.util.DynamicVariable
-import scala.collection.mutable.{ArrayBuffer, HashMap}
+import scala.collection.mutable.ArrayBuffer
import chisel3._
-import core._
-import firrtl._
+import chisel3.experimental._
+import chisel3.internal.firrtl._
+import chisel3.internal.naming._
import _root_.firrtl.annotations.{CircuitName, ComponentName, ModuleName, Named}
private[chisel3] class Namespace(keywords: Set[String]) {
@@ -186,7 +187,7 @@ private[chisel3] class DynamicContext() {
var currentClock: Option[Clock] = None
var currentReset: Option[Reset] = None
val errors = new ErrorLog
- val namingStack = new internal.naming.NamingStack
+ val namingStack = new NamingStack
}
//scalastyle:off number.of.methods
@@ -204,7 +205,7 @@ private[chisel3] object Builder {
private def initializeSingletons(): Unit = {
// This used to contain:
// val dummy = core.DontCare
- // but this would occasionally produce hangs dues to static initialization deadlock
+ // but this would occasionally produce hangs due to static initialization deadlock
// when Builder initialization collided with chisel3.package initialization of the DontCare value.
// See:
// http://ternarysearch.blogspot.com/2013/07/static-initialization-deadlock.html
@@ -212,14 +213,14 @@ private[chisel3] object Builder {
// https://stackoverflow.com/questions/28631656/runnable-thread-state-but-in-object-wait
}
- def namingStackOption: Option[internal.naming.NamingStack] = dynamicContextVar.value.map(_.namingStack)
+ def namingStackOption: Option[NamingStack] = dynamicContextVar.value.map(_.namingStack)
def idGen: IdGen = chiselContext.value.idGen
def globalNamespace: Namespace = dynamicContext.globalNamespace
def components: ArrayBuffer[Component] = dynamicContext.components
def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations
- def namingStack: internal.naming.NamingStack = dynamicContext.namingStack
+ def namingStack: NamingStack = dynamicContext.namingStack
def currentModule: Option[BaseModule] = dynamicContextVar.value match {
case Some(dyanmicContext) => dynamicContext.currentModule
@@ -366,19 +367,19 @@ private[chisel3] object Builder {
* objects.
*/
object DynamicNamingStack {
- def pushContext(): internal.naming.NamingContextInterface = {
+ def pushContext(): NamingContextInterface = {
Builder.namingStackOption match {
case Some(namingStack) => namingStack.pushContext()
- case None => internal.naming.DummyNamer
+ case None => DummyNamer
}
}
- def popReturnContext[T <: Any](prefixRef: T, until: internal.naming.NamingContextInterface): T = {
+ def popReturnContext[T <: Any](prefixRef: T, until: NamingContextInterface): T = {
until match {
- case internal.naming.DummyNamer =>
+ case DummyNamer =>
require(Builder.namingStackOption.isEmpty,
"Builder context must remain stable throughout a chiselName-annotated function invocation")
- case context: internal.naming.NamingContext =>
+ case context: NamingContext =>
require(Builder.namingStackOption.isDefined,
"Builder context must remain stable throughout a chiselName-annotated function invocation")
Builder.namingStackOption.get.popContext(prefixRef, context)
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
index 59f32542..91d9d7de 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
@@ -4,8 +4,6 @@ package chisel3.internal
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}
-import chisel3.core._
-
class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause) {
val blacklistPackages = Set("chisel3", "scala", "java", "sun", "sbt")
@@ -126,7 +124,7 @@ private[chisel3] class ErrorLog {
"java.",
"scala.",
"chisel3.internal.",
- "chisel3.core.",
+ "chisel3.experimental.",
"chisel3.package$" // for some compatibility / deprecated types
)
!chiselPrefixes.filter(className.startsWith(_)).isEmpty
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala
new file mode 100644
index 00000000..91f1bfd8
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala
@@ -0,0 +1,246 @@
+// See LICENSE for license details.
+
+package chisel3.internal
+
+import chisel3._
+import chisel3.experimental.{BaseModule, EnumType, FixedPoint, RawModule, UnsafeEnum}
+import chisel3.internal.Builder.pushCommand
+import chisel3.internal.firrtl.{Connect, DefInvalid}
+import scala.language.experimental.macros
+import chisel3.internal.sourceinfo.SourceInfo
+
+/**
+* MonoConnect.connect executes a mono-directional connection element-wise.
+*
+* Note that this isn't commutative. There is an explicit source and sink
+* already determined before this function is called.
+*
+* The connect operation will recurse down the left Data (with the right Data).
+* An exception will be thrown if a movement through the left cannot be matched
+* in the right. The right side is allowed to have extra Record fields.
+* Vecs must still be exactly the same size.
+*
+* See elemConnect for details on how the root connections are issued.
+*
+* Note that a valid sink must be writable so, one of these must hold:
+* - Is an internal writable node (Reg or Wire)
+* - Is an output of the current module
+* - Is an input of a submodule of the current module
+*
+* Note that a valid source must be readable so, one of these must hold:
+* - Is an internal readable node (Reg, Wire, Op)
+* - Is a literal
+* - Is a port of the current module or submodule of the current module
+*/
+
+private[chisel3] object MonoConnect {
+ // scalastyle:off method.name public.methods.have.type
+ // These are all the possible exceptions that can be thrown.
+ // These are from element-level connection
+ def UnreadableSourceException =
+ MonoConnectException(": Source is unreadable from current module.")
+ def UnwritableSinkException =
+ MonoConnectException(": Sink is unwriteable by current module.")
+ def UnknownRelationException =
+ MonoConnectException(": Sink or source unavailable to current module.")
+ // These are when recursing down aggregate types
+ def MismatchedVecException =
+ MonoConnectException(": Sink and Source are different length Vecs.")
+ def MissingFieldException(field: String) =
+ MonoConnectException(s": Source Record missing field ($field).")
+ def MismatchedException(sink: String, source: String) =
+ MonoConnectException(s": Sink ($sink) and Source ($source) have different types.")
+ def DontCareCantBeSink =
+ MonoConnectException(": DontCare cannot be a connection sink (LHS)")
+ // scalastyle:on method.name public.methods.have.type
+
+ /** This function is what recursively tries to connect a sink and source together
+ *
+ * There is some cleverness in the use of internal try-catch to catch exceptions
+ * during the recursive decent and then rethrow them with extra information added.
+ * This gives the user a 'path' to where in the connections things went wrong.
+ */
+ def connect( //scalastyle:off cyclomatic.complexity method.length
+ sourceInfo: SourceInfo,
+ connectCompileOptions: CompileOptions,
+ sink: Data,
+ source: Data,
+ context_mod: RawModule): Unit =
+ (sink, source) match {
+
+ // Handle legal element cases, note (Bool, Bool) is caught by the first two, as Bool is a UInt
+ case (sink_e: Bool, source_e: UInt) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+ case (sink_e: UInt, source_e: Bool) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+ case (sink_e: UInt, source_e: UInt) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+ case (sink_e: SInt, source_e: SInt) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+ case (sink_e: FixedPoint, source_e: FixedPoint) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+ case (sink_e: Clock, source_e: Clock) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+ case (sink_e: EnumType, source_e: UnsafeEnum) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+ case (sink_e: EnumType, source_e: EnumType) if sink_e.typeEquivalent(source_e) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+ case (sink_e: UnsafeEnum, source_e: UInt) =>
+ elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod)
+
+ // 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")
+ }
+ }
+ // Handle Vec connected to DontCare. Apply the DontCare to individual elements.
+ case (sink_v: Vec[Data @unchecked], DontCare) =>
+ for(idx <- 0 until sink_v.length) {
+ try {
+ implicit val compileOptions = connectCompileOptions
+ connect(sourceInfo, connectCompileOptions, sink_v(idx), source, context_mod)
+ } catch {
+ case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message")
+ }
+ }
+
+ // 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)
+ }
+ }
+ }
+ } catch {
+ case MonoConnectException(message) => throw MonoConnectException(s".$field$message")
+ }
+ }
+ // Handle Record connected to DontCare. Apply the DontCare to individual elements.
+ case (sink_r: Record, DontCare) =>
+ // For each field, descend with right
+ for((field, sink_sub) <- sink_r.elements) {
+ try {
+ connect(sourceInfo, connectCompileOptions, sink_sub, source, context_mod)
+ } catch {
+ case MonoConnectException(message) => throw MonoConnectException(s".$field$message")
+ }
+ }
+
+ // Source is DontCare - it may be connected to anything. It generates a defInvalid for the sink.
+ case (sink, DontCare) => pushCommand(DefInvalid(sourceInfo, sink.lref))
+ // DontCare as a sink is illegal.
+ case (DontCare, _) => throw DontCareCantBeSink
+ // Sink and source are different subtypes of data so fail
+ case (sink, source) => throw MismatchedException(sink.toString, source.toString)
+ }
+
+ // 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,
+ // otherwise, issue a Connect.
+ source.topBinding match {
+ case b: DontCareBinding => pushCommand(DefInvalid(sourceInfo, sink.lref))
+ case _ => pushCommand(Connect(sourceInfo, sink.lref, source.ref))
+ }
+ }
+
+ // This function checks if element-level connection operation allowed.
+ // Then it either issues it or throws the appropriate exception.
+ def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Element, source: Element, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit
+ import BindingDirection.{Internal, Input, Output} // Using extensively so import these
+ // 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)
+ val source_mod: BaseModule = source.topBinding.location.getOrElse(context_mod)
+
+ val sink_direction = BindingDirection.from(sink.topBinding, sink.direction)
+ val source_direction = BindingDirection.from(source.topBinding, source.direction)
+
+ // CASE: Context is same module that both left node and right node are in
+ if( (context_mod == sink_mod) && (context_mod == source_mod) ) {
+ ((sink_direction, source_direction): @unchecked) match {
+ // SINK SOURCE
+ // CURRENT MOD CURRENT MOD
+ case (Output, _) => issueConnect(sink, source)
+ case (Internal, _) => issueConnect(sink, source)
+ case (Input, _) => throw UnwritableSinkException
+ }
+ }
+
+ // CASE: Context is same module as sink node and right node is in a child module
+ else if( (sink_mod == context_mod) &&
+ (source_mod._parent.map(_ == context_mod).getOrElse(false)) ) {
+ // Thus, right node better be a port node and thus have a direction
+ ((sink_direction, source_direction): @unchecked) match {
+ // SINK SOURCE
+ // CURRENT MOD CHILD MOD
+ case (Internal, Output) => issueConnect(sink, source)
+ case (Internal, Input) => issueConnect(sink, source)
+ case (Output, Output) => issueConnect(sink, source)
+ case (Output, Input) => issueConnect(sink, source)
+ case (_, Internal) => {
+ if (!(connectCompileOptions.dontAssumeDirectionality)) {
+ issueConnect(sink, source)
+ } else {
+ throw UnreadableSourceException
+ }
+ }
+ case (Input, Output) if (!(connectCompileOptions.dontTryConnectionsSwapped)) => issueConnect(source, sink) // scalastyle:ignore line.size.limit
+ case (Input, _) => throw UnwritableSinkException
+ }
+ }
+
+ // CASE: Context is same module as source node and sink node is in child module
+ else if( (source_mod == context_mod) &&
+ (sink_mod._parent.map(_ == context_mod).getOrElse(false)) ) {
+ // Thus, left node better be a port node and thus have a direction
+ ((sink_direction, source_direction): @unchecked) match {
+ // SINK SOURCE
+ // CHILD MOD CURRENT MOD
+ case (Input, _) => issueConnect(sink, source)
+ case (Output, _) => throw UnwritableSinkException
+ case (Internal, _) => throw UnwritableSinkException
+ }
+ }
+
+ // 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_mod._parent.map(_ == context_mod).getOrElse(false)) &&
+ (source_mod._parent.map(_ == context_mod).getOrElse(false))
+ ) {
+ // Thus both nodes must be ports and have a direction
+ ((sink_direction, source_direction): @unchecked) match {
+ // SINK SOURCE
+ // CHILD MOD CHILD MOD
+ case (Input, Input) => issueConnect(sink, source)
+ case (Input, Output) => issueConnect(sink, source)
+ case (Output, _) => throw UnwritableSinkException
+ case (_, Internal) => {
+ if (!(connectCompileOptions.dontAssumeDirectionality)) {
+ issueConnect(sink, source)
+ } else {
+ throw UnreadableSourceException
+ }
+ }
+ case (Internal, _) => throw UnwritableSinkException
+ }
+ }
+
+ // Not quite sure where left and right are compared to current module
+ // so just error out
+ else throw UnknownRelationException
+ }
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
index 7854efdb..2cb4d092 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -3,9 +3,9 @@
package chisel3.internal.firrtl
import chisel3._
-import core._
import chisel3.internal._
-import chisel3.internal.sourceinfo.{SourceInfo, NoSourceInfo}
+import chisel3.internal.sourceinfo.SourceInfo
+import chisel3.experimental.{BaseModule, ChiselAnnotation, Param, RawModule}
// scalastyle:off number.of.types