summaryrefslogtreecommitdiff
path: root/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
diff options
context:
space:
mode:
authorRichard Lin2017-06-26 18:35:46 -0700
committerGitHub2017-06-26 18:35:46 -0700
commitaaee58d374dc3f3a088856da13a6a59ecffb2cac (patch)
tree790828de9a9fbb1dfd26635eebaf27668a4cc8df /chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
parent91850ec917a432e9e3db588e7711c8a82801eb49 (diff)
Directions internals mega-refactor (#617)
Part 1 of mega-change in #578 Major notes: - Input(...) and Output(...) now (effectively) recursively override their elements' directions - Nodes given userDirection (Input, Output, Flip - what the user assigned to _that_ node) and actualDirection (Input, Output, None, but also Bidirectional and BidirectionalFlip for mostly Aggregates), because of the above (since a higher-level Input(...) can override the locally specified user direction). - DataMirror (node reflection APIs) added to chisel3.experimental. This provides ways to query the user given direction of a node as well as the actual direction. - checkSynthesizable replaced with requireIsHardware and requireIsChiselType and made available in chisel3.experimental. Internal changes notes: - toType moved into Emitter, this makes the implementation cleaner especially considering that Vec types can't be flipped in FIRRTL. This also more clearly separates Chisel frontend from FIRRTL emission. - Direction separated from Bindings, both are now fields in Data, and all nodes are given hierarchical directions (Aggregates may be Bidirectional). The actualDirection at the Element (leaf) level should be the same as binding directions previously. - Bindings are hierarchical, children (of a, for example, Bundle) have a ChildBinding that points to their parent. This is different than the previous scheme where Bindings only applied at the Element (leaf) level. - Lots of small misc clean up. Future PRs will address other parts of #578, including stricter direction checks that aren't a side-effect of this internal refactor, stricter checks and splitting of binding operations (Wire vs. WireInit), and node operations not introduced here (getType and deprecation of chiselCloneType). Since those shouldn't mess with internals, those should be much smaller.
Diffstat (limited to 'chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala111
1 files changed, 77 insertions, 34 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
index 3f81de9f..701cf892 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
@@ -15,11 +15,58 @@ import chisel3.internal.sourceinfo._
* of) other Data objects.
*/
sealed abstract class Aggregate extends Data {
+ private[chisel3] override def bind(target: Binding, parentDirection: UserDirection) {
+ binding = target
+
+ val resolvedDirection = UserDirection.fromParent(parentDirection, userDirection)
+ for (child <- getElements) {
+ child.bind(ChildBinding(this), resolvedDirection)
+ }
+
+ // Check that children obey the directionality rules.
+ val childDirections = getElements.map(_.direction).toSet
+ direction = if (childDirections == Set()) { // Sadly, Scala can't do set matching
+ // If empty, use my assigned direction
+ resolvedDirection match {
+ case UserDirection.Unspecified | UserDirection.Flip => ActualDirection.Unspecified
+ case UserDirection.Output => ActualDirection.Output
+ case UserDirection.Input => ActualDirection.Input
+ }
+ } else if (childDirections == Set(ActualDirection.Unspecified)) {
+ ActualDirection.Unspecified
+ } else if (childDirections == Set(ActualDirection.Input)) {
+ ActualDirection.Input
+ } else if (childDirections == Set(ActualDirection.Output)) {
+ ActualDirection.Output
+ } else if (childDirections subsetOf
+ Set(ActualDirection.Output, ActualDirection.Input,
+ ActualDirection.Bidirectional(ActualDirection.Default),
+ ActualDirection.Bidirectional(ActualDirection.Flipped))) {
+ resolvedDirection match {
+ case UserDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default)
+ case UserDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped)
+ case _ => throw new RuntimeException("Unexpected forced Input / Output")
+ }
+ } else {
+ this match {
+ // Anything flies in compatibility mode
+ case t: Record if !t.compileOptions.dontAssumeDirectionality => resolvedDirection match {
+ case UserDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default)
+ case UserDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped)
+ case _ => ActualDirection.Bidirectional(ActualDirection.Default)
+ }
+ case _ =>
+ val childWithDirections = getElements zip getElements.map(_.direction)
+ throw Binding.MixedDirectionAggregateException(s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections")
+ }
+ }
+ }
+
/** Returns a Seq of the immediate contents of this Aggregate, in order.
*/
def getElements: Seq[Data]
- private[core] def width: Width = getElements.map(_.width).foldLeft(0.W)(_ + _)
+ private[chisel3] def width: Width = getElements.map(_.width).foldLeft(0.W)(_ + _)
private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit =
pushCommand(BulkConnect(sourceInfo, this.lref, that.lref))
@@ -68,25 +115,21 @@ object Vec {
// Check that types are homogeneous. Width mismatch for Elements is safe.
require(!elts.isEmpty)
+ elts.foreach(requireIsHardware(_, "vec element"))
val vec = Wire(new Vec(cloneSupertype(elts, "Vec"), elts.length))
- def doConnect(sink: T, source: T) = {
- // TODO: this looks bad, and should feel bad. Replace with a better abstraction.
- // NOTE: Must use elts.head instead of vec.sample_element because vec.sample_element has
- // WireBinding which does not have a direction
- val hasDirectioned = elts.head match {
- case t: Aggregate => t.flatten.exists(_.dir != Direction.Unspecified)
- case t: Element => t.dir != Direction.Unspecified
- }
- if (hasDirectioned) {
- sink bulkConnect source
- } else {
- sink connect source
- }
- }
- for ((v, e) <- vec zip elts) {
- doConnect(v, e)
+ // TODO: try to remove the logic for this mess
+ elts.head.direction match {
+ case ActualDirection.Input | ActualDirection.Output | ActualDirection.Unspecified =>
+ // When internal wires are involved, driver / sink must be specified explicitly, otherwise
+ // the system is unable to infer which is driver / sink
+ (vec zip elts).foreach(x => x._1 := x._2)
+ case ActualDirection.Bidirectional(_) =>
+ // For bidirectional, must issue a bulk connect so subelements are resolved correctly.
+ // Bulk connecting two wires may not succeed because Chisel frontend does not infer
+ // directions.
+ (vec zip elts).foreach(x => x._1 <> x._2)
}
vec
}
@@ -186,7 +229,7 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int)
*
* Needed specifically for the case when the Vec is length 0.
*/
- private[core] val sample_element: T = gen
+ private[chisel3] val sample_element: T = gen
// allElements current includes sample_element
// This is somewhat weird although I think the best course of action here is
@@ -226,16 +269,25 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int)
override def apply(p: UInt): T = macro CompileOptionsTransform.pArg
def do_apply(p: UInt)(implicit compileOptions: CompileOptions): T = {
- Binding.checkSynthesizable(p ,s"'p' ($p)")
+ requireIsHardware(p, "vec index")
val port = gen
- val i = Vec.truncateIndex(p, length)(UnlocatableSourceInfo, compileOptions)
- port.setRef(this, i)
- // Bind each element of port to being whatever the base type is
- // Using the head element as the sample_element
- for((port_elem, model_elem) <- port.allElements zip sample_element.allElements) {
- port_elem.binding = model_elem.binding
+ // Reconstruct the resolvedDirection (in Aggregate.bind), since it's not stored.
+ // It may not be exactly equal to that value, but the results are the same.
+ val reconstructedResolvedDirection = direction match {
+ case ActualDirection.Input => UserDirection.Input
+ case ActualDirection.Output => UserDirection.Output
+ case ActualDirection.Bidirectional(ActualDirection.Default) | ActualDirection.Unspecified =>
+ UserDirection.Unspecified
+ case ActualDirection.Bidirectional(ActualDirection.Flipped) => UserDirection.Flip
}
+ // TODO port technically isn't directly child of this data structure, but the result of some
+ // muxes / demuxes. However, this does make access consistent with the top-level bindings.
+ // Perhaps there's a cleaner way of accomplishing this...
+ port.bind(ChildBinding(this), reconstructedResolvedDirection)
+
+ val i = Vec.truncateIndex(p, length)(UnlocatableSourceInfo, compileOptions)
+ port.setRef(this, i)
port
}
@@ -256,7 +308,6 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int)
new Vec(gen.cloneType, length).asInstanceOf[this.type]
}
- private[chisel3] def toType: String = s"${sample_element.toType}[$length]"
override def getElements: Seq[Data] =
(0 until length).map(apply(_))
@@ -384,14 +435,6 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
/** Name for Pretty Printing */
def className: String = this.getClass.getSimpleName
- private[chisel3] def toType = {
- def eltPort(elt: Data): String = {
- val flipStr: String = if(Data.isFirrtlFlipped(elt)) "flip " else ""
- s"${flipStr}${elt.getRef.name} : ${elt.toType}"
- }
- elements.toIndexedSeq.reverse.map(e => eltPort(e._2)).mkString("{", ", ", "}")
- }
-
private[core] override def typeEquivalent(that: Data): Boolean = that match {
case that: Record =>
this.getClass == that.getClass &&