summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/scala/chisel3/Aggregate.scala42
-rw-r--r--core/src/main/scala/chisel3/Module.scala27
-rw-r--r--src/test/scala/chiselTests/Direction.scala84
3 files changed, 99 insertions, 54 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala
index f22f5e63..b6836ea7 100644
--- a/core/src/main/scala/chisel3/Aggregate.scala
+++ b/core/src/main/scala/chisel3/Aggregate.scala
@@ -936,37 +936,29 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
}
private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = {
- try {
- _parent.foreach(_.addId(this))
- binding = target
+ _parent.foreach(_.addId(this))
+ binding = target
- val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
+ val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
- checkForAndReportDuplicates()
+ checkForAndReportDuplicates()
- for ((child, sameChild) <- this.elementsIterator.zip(this.elementsIterator)) {
- if (child != sameChild) {
- throwException(
- s"${this.className} does not return the same objects when calling .elements multiple times. Did you make it a def by mistake?"
- )
- }
- child.bind(ChildBinding(this), resolvedDirection)
+ for ((child, sameChild) <- this.elementsIterator.zip(this.elementsIterator)) {
+ if (child != sameChild) {
+ throwException(
+ s"${this.className} does not return the same objects when calling .elements multiple times. Did you make it a def by mistake?"
+ )
}
+ child.bind(ChildBinding(this), resolvedDirection)
+ }
- // Check that children obey the directionality rules.
- val childDirections = elementsIterator.map(_.direction).toSet - ActualDirection.Empty
- direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match {
- case Some(dir) => dir
- case None =>
- val childWithDirections = getElements.zip(getElements.map(_.direction))
- throw MixedDirectionAggregateException(
- s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections"
- )
- }
- } catch { // nasty compatibility mode shim, where anything flies
- case e: MixedDirectionAggregateException if !compileOptions.dontAssumeDirectionality =>
+ // Check that children obey the directionality rules.
+ val childDirections = elementsIterator.map(_.direction).toSet - ActualDirection.Empty
+ direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match {
+ case Some(dir) => dir
+ case None =>
val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection)
- direction = resolvedDirection match {
+ resolvedDirection match {
case SpecifiedDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default)
case SpecifiedDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped)
case _ => ActualDirection.Bidirectional(ActualDirection.Default)
diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala
index 9315a44b..48c33083 100644
--- a/core/src/main/scala/chisel3/Module.scala
+++ b/core/src/main/scala/chisel3/Module.scala
@@ -692,33 +692,34 @@ package experimental {
*/
protected def _bindIoInPlace(iodef: Data): Unit = {
// Compatibility code: Chisel2 did not require explicit direction on nodes
- // (unspecified treated as output, and flip on nothing was input).
- // This sets assigns the explicit directions required by newer semantics on
- // Bundles defined in compatibility mode.
+ // (unspecified treated as output, and flip on nothing was input).
+ // However, we are going to go back to Chisel2 semantics, so we need to make it work
+ // even for chisel3 code.
+ // This assigns the explicit directions required by both semantics on all Bundles.
// This recursively walks the tree, and assigns directions if no explicit
- // direction given by upper-levels (override Input / Output) AND element is
- // directly inside a compatibility Bundle determined by compile options.
- def assignCompatDir(data: Data, insideCompat: Boolean): Unit = {
+ // direction given by upper-levels (override Input / Output)
+ def assignCompatDir(data: Data): Unit = {
data match {
- case data: Element if insideCompat => data._assignCompatibilityExplicitDirection
- case data: Element => // Not inside a compatibility Bundle, nothing to be done
+ case data: Element => data._assignCompatibilityExplicitDirection
case data: Aggregate =>
data.specifiedDirection match {
// Recurse into children to ensure explicit direction set somewhere
case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip =>
data match {
case record: Record =>
- val compatRecord = !record.compileOptions.dontAssumeDirectionality
- record.elementsIterator.foreach(assignCompatDir(_, compatRecord))
+ record.elementsIterator.foreach(assignCompatDir(_))
case vec: Vec[_] =>
- vec.elementsIterator.foreach(assignCompatDir(_, insideCompat))
+ vec.elementsIterator.foreach(assignCompatDir(_))
}
- case SpecifiedDirection.Input | SpecifiedDirection.Output => // forced assign, nothing to do
+ case SpecifiedDirection.Input | SpecifiedDirection.Output =>
+ // forced assign, nothing to do
+ // Note this is because Input and Output recurse down their types to align all fields to that SpecifiedDirection
+ // Thus, no implicit assigment is necessary.
}
}
}
- assignCompatDir(iodef, false)
+ assignCompatDir(iodef)
iodef.bind(PortBinding(this))
_ports += iodef
diff --git a/src/test/scala/chiselTests/Direction.scala b/src/test/scala/chiselTests/Direction.scala
index 642a507c..ddbd99d2 100644
--- a/src/test/scala/chiselTests/Direction.scala
+++ b/src/test/scala/chiselTests/Direction.scala
@@ -86,15 +86,15 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils {
})
}
- property("Empty Vecs with no direction on the sample_element *should* cause direction errors") {
- an[Exception] should be thrownBy extractCause[Exception] {
- ChiselStage.elaborate(new Module {
- val io = IO(new Bundle {
- val foo = Input(UInt(8.W))
- val x = Vec(0, UInt(8.W))
- })
+ property(
+ "Empty Vecs with no direction on the sample_element should not cause direction errors, as Chisel and chisel3 directions are merged"
+ ) {
+ ChiselStage.elaborate(new Module {
+ val io = IO(new Bundle {
+ val foo = Input(UInt(8.W))
+ val x = Vec(0, UInt(8.W))
})
- }
+ })
}
property("Empty Bundles should not cause direction errors") {
@@ -120,15 +120,15 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils {
})
}
- property("Explicitly directioned but empty Bundles should cause direction errors") {
- an[Exception] should be thrownBy extractCause[Exception] {
- ChiselStage.elaborate(new Module {
- val io = IO(new Bundle {
- val foo = UInt(8.W)
- val x = Input(new Bundle {})
- })
+ property(
+ "Explicitly directioned but empty Bundles should not cause direction errors because Chisel and chisel3 directionality are merged"
+ ) {
+ ChiselStage.elaborate(new Module {
+ val io = IO(new Bundle {
+ val foo = UInt(8.W)
+ val x = Input(new Bundle {})
})
- }
+ })
}
import chisel3.experimental.{DataMirror, Direction}
@@ -330,6 +330,58 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils {
}
}
}
+ property("Can now describe a Decoupled bundle using Flipped, not Input/Output in chisel3") {
+ class Decoupled extends Bundle {
+ val bits = UInt(3.W)
+ val valid = Bool()
+ val ready = Flipped(Bool())
+ }
+ class MyModule extends RawModule {
+ val incoming = IO(Flipped(new Decoupled))
+ val outgoing = IO(new Decoupled)
+
+ outgoing <> incoming
+ }
+
+ val emitted: String = ChiselStage.emitChirrtl(new MyModule)
+
+ // Check that emitted directions are correct.
+ assert(emitted.contains("input incoming : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}"))
+ assert(emitted.contains("output outgoing : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}"))
+ assert(emitted.contains("outgoing <= incoming"))
+ }
+ property("Can now mix Input/Output and Flipped within the same bundle") {
+ class Decoupled extends Bundle {
+ val bits = UInt(3.W)
+ val valid = Bool()
+ val ready = Flipped(Bool())
+ }
+ class DecoupledAndMonitor extends Bundle {
+ val producer = new Decoupled()
+ val consumer = Flipped(new Decoupled())
+ val monitor = Input(new Decoupled()) // Same as Flipped(stripFlipsIn(..))
+ val driver = Output(new Decoupled()) // Same as stripFlipsIn(..)
+ }
+ class MyModule extends RawModule {
+ val io = IO(Flipped(new DecoupledAndMonitor()))
+ io.consumer <> io.producer
+ io.monitor.bits := io.driver.bits
+ io.monitor.valid := io.driver.valid
+ io.monitor.ready := io.driver.ready
+ }
+
+ val emitted: String = ChiselStage.emitChirrtl(new MyModule)
+
+ assert(
+ emitted.contains(
+ "input io : { producer : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}, flip consumer : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}, flip monitor : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}, driver : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}}"
+ )
+ )
+ assert(emitted.contains("io.consumer <= io.producer"))
+ assert(emitted.contains("io.monitor.bits <= io.driver.bits"))
+ assert(emitted.contains("io.monitor.valid <= io.driver.valid"))
+ assert(emitted.contains("io.monitor.ready <= io.driver.ready"))
+ }
property("Bugfix: marking Vec fields with mixed directionality as Output/Input clears inner directions") {
class Decoupled extends Bundle {
val bits = UInt(3.W)