Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directions internals mega-refactor #617

Merged
merged 25 commits into from
Jun 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 77 additions & 34 deletions chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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(_))

Expand Down Expand Up @@ -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 &&
Expand Down
91 changes: 34 additions & 57 deletions chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -138,31 +138,30 @@ object BiConnect {
// 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: UserModule): Unit = {
import Direction.{Input, Output} // Using extensively so import these
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.binding.location.getOrElse(context_mod)
val right_mod: BaseModule = right.binding.location.getOrElse(context_mod)
val left_mod: BaseModule = left.topBinding.location.getOrElse(context_mod)
val right_mod: BaseModule = right.topBinding.location.getOrElse(context_mod)

val left_direction: Option[Direction] = left.binding.direction
val right_direction: Option[Direction] = right.binding.direction
// None means internal
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 (Some(Input), Some(Input)) => issueConnectL2R(left, right)
case (None, Some(Input)) => issueConnectL2R(left, right)
case (Input, Input) => issueConnectL2R(left, right)
case (Internal, Input) => issueConnectL2R(left, right)

case (Some(Output), Some(Output)) => issueConnectR2L(left, right)
case (None, Some(Output)) => issueConnectR2L(left, right)
case (Output, Output) => issueConnectR2L(left, right)
case (Internal, Output) => issueConnectR2L(left, right)

case (Some(Input), Some(Output)) => throw BothDriversException
case (Some(Output), Some(Input)) => throw NeitherDriverException
case (_, None) => throw UnknownRelationException
case (Input, Output) => throw BothDriversException
case (Output, Input) => throw NeitherDriverException
case (_, Internal) => throw UnknownRelationException
}
}

Expand All @@ -172,55 +171,33 @@ object BiConnect {
// 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 (Some(Input), Some(Input)) => issueConnectR2L(left, right)
case (Some(Input), None) => issueConnectR2L(left, right)
case (Input, Input) => issueConnectR2L(left, right)
case (Input, Internal) => issueConnectR2L(left, right)

case (Some(Output), Some(Output)) => issueConnectL2R(left, right)
case (Some(Output), None) => issueConnectL2R(left, right)
case (Output, Output) => issueConnectL2R(left, right)
case (Output, Internal) => issueConnectL2R(left, right)

case (Some(Input), Some(Output)) => throw NeitherDriverException
case (Some(Output), Some(Input)) => throw BothDriversException
case (None, _) => throw UnknownRelationException
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 (Some(Input), Some(Output)) => issueConnectL2R(left, right)
case (Some(Input), None) => issueConnectL2R(left, right)
case (None, Some(Output)) => issueConnectL2R(left, right)
case (Input, Output) => issueConnectL2R(left, right)
case (Input, Internal) => issueConnectL2R(left, right)
case (Internal, Output) => issueConnectL2R(left, right)

case (Some(Output), Some(Input)) => issueConnectR2L(left, right)
case (Some(Output), None) => issueConnectR2L(left, right)
case (None, Some(Input)) => issueConnectR2L(left, right)
case (Output, Input) => issueConnectR2L(left, right)
case (Output, Internal) => issueConnectR2L(left, right)
case (Internal, Input) => issueConnectR2L(left, right)

case (Some(Input), Some(Input)) => {
if (connectCompileOptions.dontAssumeDirectionality) {
throw BothDriversException
} else {
(left.binding, right.binding) match {
case (PortBinding(_, _), PortBinding(_, _)) => throw BothDriversException
case (PortBinding(_, _), _) => issueConnectL2R(left, right)
case (_, PortBinding(_, _)) => issueConnectR2L(left, right)
case _ => throw BothDriversException
}
}
}
case (Some(Output), Some(Output)) => {
if (connectCompileOptions.dontAssumeDirectionality) {
throw BothDriversException
} else {
(left.binding, right.binding) match {
case (PortBinding(_, _), PortBinding(_, _)) => throw BothDriversException
case (PortBinding(_, _), _) => issueConnectR2L(left, right)
case (_, PortBinding(_, _)) => issueConnectL2R(left, right)
case _ => throw BothDriversException
}
}
}
case (None, None) => {
case (Input, Input) => throw BothDriversException
case (Output, Output) => throw BothDriversException
case (Internal, Internal) => {
if (connectCompileOptions.dontAssumeDirectionality) {
throw UnknownDriverException
} else {
Expand All @@ -239,18 +216,18 @@ object BiConnect {
// Thus both nodes must be ports and have a direction hint
((left_direction, right_direction): @unchecked) match {
// CHILD MOD CHILD MOD
case (Some(Input), Some(Output)) => issueConnectR2L(left, right)
case (Some(Output), Some(Input)) => issueConnectL2R(left, right)
case (Input, Output) => issueConnectR2L(left, right)
case (Output, Input) => issueConnectL2R(left, right)

case (Some(Input), Some(Input)) => throw NeitherDriverException
case (Some(Output), Some(Output)) => throw BothDriversException
case (_, None) =>
case (Input, Input) => throw NeitherDriverException
case (Output, Output) => throw BothDriversException
case (_, Internal) =>
if (connectCompileOptions.dontAssumeDirectionality) {
throw UnknownRelationException
} else {
issueConnectR2L(left, right)
}
case (None, _) =>
case (Internal, _) =>
if (connectCompileOptions.dontAssumeDirectionality) {
throw UnknownRelationException
} else {
Expand Down
64 changes: 0 additions & 64 deletions chiselFrontend/src/main/scala/chisel3/core/Binder.scala

This file was deleted.

Loading