Skip to content
Merged
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
99 changes: 83 additions & 16 deletions src/main/scala/chisel3/util/Decoupled.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,68 @@ package chisel3.util

import chisel3._

/** An I/O Bundle with simple handshaking using valid and ready signals for data 'bits'*/
class DecoupledIO[+T <: Data](gen: T) extends Bundle
/** An I/O Bundle containing 'valid' and 'ready' signals that handshake
* the transfer of data stored in the 'bits' subfield.
* The base protocol implied by the directionality is that the consumer
* uses the flipped interface. Actual semantics of ready/valid are
* enforced via use of concrete subclasses.
*/
abstract class ReadyValidIO[+T <: Data](gen: T) extends Bundle
{
val ready = Bool(INPUT)
val valid = Bool(OUTPUT)
val bits = gen.cloneType.asOutput
def fire(dummy: Int = 0): Bool = ready && valid
override def cloneType: this.type = new DecoupledIO(gen).asInstanceOf[this.type]
}

/** Adds a ready-valid handshaking protocol to any interface.
* The standard used is that the consumer uses the flipped interface.
/** A concrete subclass of ReadyValidIO signalling that the user expects a
* "decoupled" interface: 'valid' indicates that the producer has
* put valid data in 'bits', and 'ready' indicates that the consumer is ready
* to accept the data this cycle. No requirements are placed on the signalling
* of ready or valid.
*/
object Decoupled {
class DecoupledIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen)
{
override def cloneType: this.type = new DecoupledIO(gen).asInstanceOf[this.type]
}

/** This factory adds a decoupled handshaking protocol to a data bundle. */
object Decoupled
{
/** Take any Data and wrap it in a DecoupledIO interface */
def apply[T <: Data](gen: T): DecoupledIO[T] = new DecoupledIO(gen)

/** Take an IrrevocableIO and cast it to a DecoupledIO.
* This cast is only safe to do in cases where the IrrevocableIO
* is being produced as an output.
*/
def apply[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = {
Copy link
Member

Choose a reason for hiding this comment

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

can you add scaladoc for the two apply methods? the new one should explain the use case and the invariant

require(irr.bits.dir == OUTPUT, "Only safe to cast produced Irrevocable bits to Decoupled.")
val d = Wire(new DecoupledIO(irr.bits))
d.bits := irr.bits
d.valid := irr.valid
irr.ready := d.ready
d
}
}

/** A concrete subclass of ReadyValidIO that promises to not change
* the value of 'bits' after a cycle where 'valid' is high and 'ready' is low.
* Additionally, once 'valid' is raised it will never be lowered until after
* 'ready' has also been raised.
*/
class IrrevocableIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen)
{
override def cloneType: this.type = new IrrevocableIO(gen).asInstanceOf[this.type]
}

/** Factory adds an irrevocable handshaking protocol to a data bundle. */
object Irrevocable
{
def apply[T <: Data](gen: T): IrrevocableIO[T] = new IrrevocableIO(gen)
}


/** An I/O bundle for enqueuing data with valid/ready handshaking
* Initialization must be handled, if necessary, by the parent circuit
*/
Expand Down Expand Up @@ -103,7 +148,8 @@ class QueueIO[T <: Data](gen: T, entries: Int) extends Bundle
* q.io.enq <> producer.io.out
* consumer.io.in <> q.io.deq }}}
*/
class Queue[T <: Data](gen: T, val entries: Int,
class Queue[T <: Data](gen: T,
val entries: Int,
pipe: Boolean = false,
flow: Boolean = false,
override_reset: Option[Bool] = None)
Expand Down Expand Up @@ -164,22 +210,43 @@ extends Module(override_reset=override_reset) {
}
}

/** Generic hardware queue. Required parameter entries controls
the depth of the queues. The width of the queue is determined
from the inputs.

Example usage:
{{{ val q = Queue(Decoupled(UInt()), 16)
q.io.enq <> producer.io.out
consumer.io.in <> q.io.deq }}}
/** Factory for a generic hardware queue. Required parameter 'entries' controls
* the depth of the queues. The width of the queue is determined
* from the input 'enq'.
*
* Example usage:
* {{{ consumer.io.in <> Queue(producer.io.out, 16) }}}
*/
object Queue
{
def apply[T <: Data](enq: DecoupledIO[T], entries: Int = 2, pipe: Boolean = false): DecoupledIO[T] = {
/** Create a queue and supply a DecoupledIO containing the product. */
def apply[T <: Data](
enq: ReadyValidIO[T],
entries: Int = 2,
pipe: Boolean = false,
flow: Boolean = false): DecoupledIO[T] = {
val q = Module(new Queue(enq.bits.cloneType, entries, pipe))
q.io.enq.valid := enq.valid // not using <> so that override is allowed
q.io.enq.bits := enq.bits
enq.ready := q.io.enq.ready
TransitName(q.io.deq, q)
}

/** Create a queue and supply a IrrevocableIO containing the product.
* Casting from Decoupled is safe here because we know the Queue has
* Irrevocable semantics; we didn't want to change the return type of
* apply() for backwards compatibility reasons.
*/
def irrevocable[T <: Data](
enq: ReadyValidIO[T],
entries: Int = 2,
pipe: Boolean = false,
flow: Boolean = false): IrrevocableIO[T] = {
val deq = apply(enq, entries, pipe)
val irr = Wire(new IrrevocableIO(deq.bits))
irr.bits := deq.bits
irr.valid := deq.valid
deq.ready := irr.ready
irr
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

While we're at it, could we have 'flow' added as another defaulted argument on the Queue.methods?