Skip to content

Add Incoming/Outgoing as alternatives to IO #3622

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
78 changes: 76 additions & 2 deletions core/src/main/scala/chisel3/Data.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ object SpecifiedDirection {
case Input => Output
}

/** Returns true if specified direction manifests as a flipped field
*
* @param dir provided specified direction
* @return
*/
private[chisel3] def isFlipped(dir: SpecifiedDirection): Boolean = dir match {
case Unspecified => false
case Flip => true
case Output => false
case Input => true
}

/** Returns the effective SpecifiedDirection of this node given the parent's effective SpecifiedDirection
* and the user-specified SpecifiedDirection of this node.
*/
Expand Down Expand Up @@ -251,8 +263,17 @@ object chiselTypeOf {
}
}

/**
* Input, Output, and Flipped are used to define the directions of Module IOs.
/** Creates a field of a parent [[Aggregate]] which is
* - flipped relative to that parent
* - coerced all members of the field to be aligned
*
* E.g. The following will create a field `i` of `b` where all recursive sub-elements of `i` are aligned, and where `i` flipped relative to `b`
*
* ```scala
* val b = new Bundle {
* val i = Input(new Decoupled(UInt(32.W))
* }
* ```
*
* Note that they currently clone their source argument, including its bindings.
*
Expand All @@ -263,18 +284,71 @@ object Input {
SpecifiedDirection.specifiedDirection(source)(_ => SpecifiedDirection.Input)
}
}

/** Creates a field of a parent [[Aggregate]] which is
* - aligned relative to that parent
* - coerced all members of the field to be aligned
*
* E.g. The following will create a field `i` of `b` where all recursive sub-elements of `i` are aligned, and where `i` is also aligned relative to `b`
*
* ```scala
* val b = new Bundle {
* val i = Output(new Decoupled(UInt(32.W))
* }
* ```
*
* Note that they currently clone their source argument, including its bindings.
*
* Thus, an error will be thrown if these are used on bound Data
*/
object Output {
def apply[T <: Data](source: => T): T = {
SpecifiedDirection.specifiedDirection(source)(_ => SpecifiedDirection.Output)
}
}

/** Creates a field of a parent [[Aggregate]] which is
* - flipped relative to that parent
*
* E.g. The following will create a field `i` of `b` where `i` is flipped relative to `b`
*
* ```scala
* val b = new Bundle {
* val i = Flipped(new Decoupled(UInt(32.W))
* }
* ```
*
* Note that they currently clone their source argument, including its bindings.
*
* Thus, an error will be thrown if these are used on bound Data
*/
object Flipped {
def apply[T <: Data](source: => T): T = {
SpecifiedDirection.specifiedDirection(source)(x => SpecifiedDirection.flip(x.specifiedDirection))
}
}

/** Creates a field of a parent [[Aggregate]] which is
* - aligned relative to that parent
*
* E.g. The following will create a field `i` of `b` where `i` is aligned (not flipped) relative to `b`
*
* ```scala
* val b = new Bundle {
* val i = Aligned(new Decoupled(UInt(32.W))
* }
* ```
*
* Note that they currently clone their source argument, including its bindings.
*
* Thus, an error will be thrown if these are used on bound Data
*/
object Aligned {
def apply[T <: Data](source: => T): T = {
SpecifiedDirection.specifiedDirection(source)(x => x.specifiedDirection)
}
}

/** This forms the root of the type system for wire data types. The data value
* must be representable as some number (need not be known at Chisel compile
* time) of bits, and must have methods to pack / unpack structured data to /
Expand Down
60 changes: 55 additions & 5 deletions core/src/main/scala/chisel3/IO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ object IO {

/** Constructs a port for the current Module
*
* This must wrap the datatype used to set the io field of any Module.
* i.e. All concrete modules must have defined io in this form:
* [lazy] val io[: io type] = IO(...[: io type])
*
* Items in [] are optional.
* This must wrap the datatype used to create an io port of any Module.
*
* The granted iodef must be a chisel type and not be bound to hardware.
*
Expand Down Expand Up @@ -62,3 +58,57 @@ object IO {
iodefClone
}
}

object Incoming {

/** Constructs an incoming port of the provided `iodef` chisel type for the current Module
*
* The following example creates a input (and mixed-alignment) aggregate port in the current module
*
* ```scala
* val i = Incoming(new Decoupled(UInt(32.W)))
* ```
* In this example, the sub-elements of `i` are ports of the following direction:
* * `i.bits` is an input port
* * `i.valid` is an input port
* * `i.ready` is an output port
*
* Note that `Incoming(foo)` is equivalent to `IO(Flipped(foo))`
*
* This must wrap the datatype used to create an io port of any Module.
*
* The granted iodef must be a non-flipped chisel type and not be bound to hardware.
*/
def apply[T <: Data](iodef: => T)(implicit sourceInfo: SourceInfo): T = {
if (SpecifiedDirection.isFlipped(iodef.specifiedDirection))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should require iodef is not coerced yet.

Builder.error("Incoming(..) cannot accept a chisel typed which is flipped")
IO(Flipped(iodef))
}
}

object Outgoing {

/** Constructs an outgoing port of the provided `iodef` chisel type for the current Module
*
* The following example creates a output (and mixed-alignment) aggregate port in the current module
*
* ```scala
* val i = Outgoing(new Decoupled(UInt(32.W)))
* ```
* In this example, the sub-elements of `i` are ports of the following direction:
* * `i.bits` is an output port
* * `i.valid` is an output port
* * `i.ready` is an input port
*
* Note that `Outgoing(foo)` is equivalent to `IO(foo)`
*
* This must wrap the datatype used to create an io port of any Module.
*
* The granted iodef must be a non-flipped chisel type and not be bound to hardware.
*/
def apply[T <: Data](iodef: => T)(implicit sourceInfo: SourceInfo): T = {
if (SpecifiedDirection.isFlipped(iodef.specifiedDirection))
Builder.error("Outgoing(..) cannot accept a chisel typed which is flipped")
IO(iodef)
}
}
12 changes: 6 additions & 6 deletions src/test/scala/chiselTests/ConnectableSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object ConnectableSpec {
extends Module {
val io = IO(new Bundle {
val in = Flipped(inType)
val out = Flipped(Flipped(outType)) // no clonetype, no Aligned (yet)
val out = Aligned(outType) // no clonetype, no Aligned (yet)
val monitor = monitorOp.map(mop => {
Output(inType)
})
Expand All @@ -44,22 +44,22 @@ object ConnectableSpec {

def vec[T <: Data](tpe: T, n: Int = 3) = Vec(n, tpe)
def alignedBundle[T <: Data](fieldType: T) = new Bundle {
val foo = Flipped(Flipped(fieldType))
val bar = Flipped(Flipped(fieldType))
val foo = Aligned(fieldType)
val bar = Aligned(fieldType)
}
def mixedBundle[T <: Data](fieldType: T) = new Bundle {
val foo = Flipped(Flipped(fieldType))
val foo = Aligned(fieldType)
val bar = Flipped(fieldType)
}

def alignedFooBundle[T <: Data](fieldType: T) = new Bundle {
val foo = Flipped(Flipped(fieldType))
val foo = Aligned(fieldType)
}
def flippedBarBundle[T <: Data](fieldType: T) = new Bundle {
val bar = Flipped(fieldType)
}
def opaqueType[T <: Data](fieldType: T) = new Record with OpaqueType {
lazy val elements = SeqMap("" -> Flipped(Flipped(fieldType)))
lazy val elements = SeqMap("" -> Aligned(fieldType))
}

def allElementTypes(): Seq[() => Data] = Seq(() => UInt(3.W))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class IOCModuleWire extends Module {
io.out := inc.out
}

class IOCompatibilitySpec extends ChiselPropSpec with Matchers with Utils {
class IOSpec extends ChiselPropSpec with Matchers with Utils with MatchesAndOmits {

property("IOCModuleVec should elaborate") {
ChiselStage.emitCHIRRTL { new IOCModuleVec(2) }
Expand All @@ -55,4 +55,27 @@ class IOCompatibilitySpec extends ChiselPropSpec with Matchers with Utils {
ChiselStage.emitCHIRRTL(new IOUnwrapped)
}
}

property("Incoming/Outgoing should generate an input/output without coercing alignment") {
class Test extends Module {
val in = Incoming(new util.DecoupledIO(UInt(32.W)))
val out = Outgoing(new util.DecoupledIO(UInt(32.W)))
out :<>= in
}
val result = ChiselStage.emitCHIRRTL { new Test }
matchesAndOmits(result)("input in", "output out", "flip ready")()
}

property("Incoming/Outgoing should fail an input/output") {
class Test extends Module {
val in = Incoming(Flipped(new util.DecoupledIO(UInt(32.W))))
val out = Outgoing(Flipped(new util.DecoupledIO(UInt(32.W))))
in :<>= out
}
val (err, _) = grabLog { intercept[ChiselException] { ChiselStage.emitCHIRRTL { new Test } } }
matchesAndOmits(err)(
"Incoming(..) cannot accept a chisel typed which is flipped",
"Outgoing(..) cannot accept a chisel typed which is flipped"
)()
}
}