diff options
| -rw-r--r-- | src/main/scala/chisel3/util/Decoupled.scala | 99 |
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 + } } |
