@@ -3,6 +3,9 @@ package chisel3.util
33
44import chisel3 ._
55import chisel3 .experimental .requireIsChiselType
6+ import chisel3 .layer .{block , Layer }
7+ import chisel3 .probe .{define , Probe , ProbeValue }
8+ import chisel3 .util .experimental .BoringUtils
69
710/** An I/O Bundle for Queues
811 * @param gen The type of data to queue
@@ -139,6 +142,27 @@ class Queue[T <: Data](
139142 * generator's `typeName`
140143 */
141144 override def desiredName = s " Queue ${entries}_ ${gen.typeName}"
145+
146+ /** Create a "shadow" `Queue` in a specific layer that will be queued and
147+ * dequeued in lockstep with an original `Queue`. Connections are made using
148+ * `BoringUtils.tapAndRead` which allows this method to be called anywhere in
149+ * the hierarchy.
150+ *
151+ * An intended use case of this is as a building block of a "shadow" design
152+ * verification datapath which augments an existing design datapath with
153+ * additional information. E.g., a shadow datapath that tracks transations
154+ * in an interconnect.
155+ *
156+ * @param data a hardware data that should be enqueued together with the
157+ * original `Queue`'s data
158+ * @param layer the `Layer` in which this queue should be created
159+ * @return a layer-colored `Valid` interface of probe type
160+ */
161+ def shadow [A <: Data ](data : A , layer : Layer ): Valid [A ] =
162+ withClockAndReset(BoringUtils .tapAndRead(clock), BoringUtils .tapAndRead(reset)) {
163+ val shadow = new Queue .ShadowFactory (enq = io.enq, deq = io.deq, entries, pipe, flow, useSyncReadMem, io.flush)
164+ shadow(data, layer)
165+ }
142166}
143167
144168/** Factory for a generic hardware queue. */
@@ -185,6 +209,94 @@ object Queue {
185209 }
186210 }
187211
212+ /** A factory for creating shadow queues. This is created using the
213+ * `withShadow` method.
214+ */
215+ class ShadowFactory private [Queue ] (
216+ enq : ReadyValidIO [Data ],
217+ deq : ReadyValidIO [Data ],
218+ entries : Int ,
219+ pipe : Boolean ,
220+ flow : Boolean ,
221+ useSyncReadMem : Boolean ,
222+ flush : Option [Bool ]) {
223+
224+ /** The clock used when building the original Queue. */
225+ private val clock = Module .clock
226+
227+ /** The reset used when elaborating the original Queue. */
228+ private val reset = Module .reset
229+
230+ /** Create a "shadow" `Queue` in a specific layer that will be queued and
231+ * dequeued in lockstep with an original `Queue`. Connections are made
232+ * using `BoringUtils.tapAndRead` which allows this method to be called
233+ * anywhere in the hierarchy.
234+ *
235+ * An intended use case of this is as a building block of a "shadow" design
236+ * verification datapath which augments an existing design datapath with
237+ * additional information. E.g., a shadow datapath that tracks transations
238+ * in an interconnect.
239+ *
240+ * @param data a hardware data that should be enqueued together with the
241+ * original `Queue`'s data
242+ * @param layer the `Layer` in which this queue should be created
243+ * @return a layer-colored `Valid` interface of probe type
244+ */
245+ def apply [A <: Data ](data : A , layer : Layer ): Valid [A ] =
246+ withClockAndReset(BoringUtils .tapAndRead(clock), BoringUtils .tapAndRead(reset)) {
247+ val shadowDeq = Wire (Probe (Valid (chiselTypeOf(data)), layer))
248+
249+ block(layer) {
250+ val shadowEnq = Wire (Decoupled (chiselTypeOf(data)))
251+ val probeEnq = BoringUtils .tapAndRead(enq)
252+ shadowEnq.valid :<= probeEnq.valid
253+ shadowEnq.bits :<= data
254+
255+ val shadowQueue = Queue (shadowEnq, entries, pipe, flow, useSyncReadMem, flush.map(BoringUtils .tapAndRead))
256+
257+ val _shadowDeq = Wire (Valid (chiselTypeOf(data)))
258+ _shadowDeq.valid :<= shadowQueue.valid
259+ _shadowDeq.bits :<= shadowQueue.bits
260+ shadowQueue.ready :<= BoringUtils .tapAndRead(deq).ready
261+ define(shadowDeq, ProbeValue (_shadowDeq))
262+ }
263+
264+ shadowDeq
265+ }
266+ }
267+
268+ /** Create a [[Queue ]] and supply a [[DecoupledIO ]] containing the product.
269+ * This additionally returns a [[ShadowFactory ]] which can be used to build
270+ * shadow datapaths that work in lockstep with this [[Queue ]].
271+ *
272+ * @param enq input (enqueue) interface to the queue, also determines type of
273+ * queue elements.
274+ * @param entries depth (number of elements) of the queue
275+ * @param pipe True if a single entry queue can run at full throughput (like
276+ * a pipeline). The `ready` signals are combinationally coupled.
277+ * @param flow True if the inputs can be consumed on the same cycle (the
278+ * inputs "flow" through the queue immediately). The `valid`
279+ * signals are coupled.
280+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal
281+ * memory element.
282+ * @param flush Optional [[Bool ]] signal, if defined, the [[Queue.hasFlush ]]
283+ * will be true, and connect correspond signal to [[Queue ]]
284+ * instance.
285+ * @return output (dequeue) interface from the queue and a [[ShadowFactory ]]
286+ * for creating shadow [[Queue ]]s
287+ */
288+ def withShadow [T <: Data ](
289+ enq : ReadyValidIO [T ],
290+ entries : Int = 2 ,
291+ pipe : Boolean = false ,
292+ flow : Boolean = false ,
293+ useSyncReadMem : Boolean = false ,
294+ flush : Option [Bool ] = None
295+ ): (DecoupledIO [T ], ShadowFactory ) = {
296+ val deq = apply(enq, entries, pipe, flow, useSyncReadMem, flush)
297+ (deq, new ShadowFactory (enq, deq, entries, pipe, flow, useSyncReadMem, flush))
298+ }
299+
188300 /** Create a queue and supply a [[IrrevocableIO ]] containing the product.
189301 * Casting from [[DecoupledIO ]] is safe here because we know the [[Queue ]] has
190302 * Irrevocable semantics.
0 commit comments