summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry Cook2016-09-08 13:02:22 -0700
committerAndrew Waterman2016-09-08 13:02:22 -0700
commitbb240453abf96b4c2d75ebb2cdc7e3159068431d (patch)
treed93c3ad57d6639479d3fc46ab99185333fe356ff
parentb2ededb755c5b4d2ae182252142d97c6a126b6cf (diff)
Add IrrevocableIO alternative to DecoupledIO (#274)
Add IrrevocableIO subclass of DecoupledIO that promises not to change .bits on a cycle after .valid is high and .ready is low
-rw-r--r--src/main/scala/chisel3/util/Decoupled.scala99
1 files changed, 83 insertions, 16 deletions
diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala
index f37a5c31..51f5bd6a 100644
--- a/src/main/scala/chisel3/util/Decoupled.scala
+++ b/src/main/scala/chisel3/util/Decoupled.scala
@@ -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] = {
+ 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
*/
@@ -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)
@@ -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
+ }
}