|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | + |
| 3 | +package chisel3.util |
| 4 | + |
| 5 | +import chisel3._ |
| 6 | +import chisel3.experimental.prefix |
| 7 | +import chisel3.util.simpleClassName |
| 8 | + |
| 9 | +/** A factory to generate a hardware pipe. This can be used to delay [[Valid]] data by a design-time configurable number |
| 10 | + * of cycles. |
| 11 | + * |
| 12 | + * Here, we construct three different pipes using the different provided `apply` methods and hook them up together. The |
| 13 | + * types are explicitly specified to show that these all communicate using [[Valid]] interfaces: |
| 14 | + * {{{ |
| 15 | + * val in: Valid[UInt] = Wire(Valid(UInt(2.W))) |
| 16 | + * |
| 17 | + * /* A zero latency (combinational) pipe is connected to 'in' */ |
| 18 | + * val foo: Valid[UInt] = Pipe(in.valid, in.bits, 0) |
| 19 | + * |
| 20 | + * /* A one-cycle pipe is connected to the output of 'foo' */ |
| 21 | + * val bar: Valid[UInt] = Pipe(foo.valid, foo.bits) |
| 22 | + * |
| 23 | + * /* A two-cycle pipe is connected to the output of 'bar' */ |
| 24 | + * val baz: Valid[UInt] = Pipe(bar, 2) |
| 25 | + * }}} |
| 26 | + * |
| 27 | + * @see [[Pipe Pipe class]] for an alternative API |
| 28 | + * @see [[Valid]] interface |
| 29 | + * @see [[Queue]] and the [[Queue$ Queue factory]] for actual queues |
| 30 | + * @see The [[ShiftRegister$ ShiftRegister factory]] to generate a pipe without a [[Valid]] interface |
| 31 | + * @define returnType the [[Valid]] output of the final pipeline stage |
| 32 | + */ |
| 33 | +object Pipe { |
| 34 | + |
| 35 | + /** Generate a pipe from an explicit valid bit and some data |
| 36 | + * @param enqValid the valid bit (must be a hardware type) |
| 37 | + * @param enqBits the data (must be a hardware type) |
| 38 | + * @param latency the number of pipeline stages |
| 39 | + * @return $returnType |
| 40 | + */ |
| 41 | + def apply[T <: Data](enqValid: Bool, enqBits: T, latency: Int): Valid[T] = { |
| 42 | + require(latency >= 0, "Pipe latency must be greater than or equal to zero!") |
| 43 | + if (latency == 0) { |
| 44 | + val out = Wire(Valid(chiselTypeOf(enqBits))) |
| 45 | + out.valid := enqValid |
| 46 | + out.bits := enqBits |
| 47 | + out |
| 48 | + } else |
| 49 | + prefix("pipe") { |
| 50 | + val v = RegNext(enqValid, false.B) |
| 51 | + val b = RegEnable(enqBits, enqValid) |
| 52 | + apply(v, b, latency - 1) |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + /** Generate a one-stage pipe from an explicit valid bit and some data |
| 57 | + * @param enqValid the valid bit (must be a hardware type) |
| 58 | + * @param enqBits the data (must be a hardware type) |
| 59 | + * @return $returnType |
| 60 | + */ |
| 61 | + def apply[T <: Data](enqValid: Bool, enqBits: T): Valid[T] = { |
| 62 | + apply(enqValid, enqBits, 1) |
| 63 | + } |
| 64 | + |
| 65 | + /** Generate a pipe for a [[Valid]] interface |
| 66 | + * @param enq a [[Valid]] interface (must be a hardware type) |
| 67 | + * @param latency the number of pipeline stages |
| 68 | + * @return $returnType |
| 69 | + */ |
| 70 | + def apply[T <: Data](enq: Valid[T], latency: Int = 1): Valid[T] = { |
| 71 | + apply(enq.valid, enq.bits, latency) |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +/** Pipeline module generator parameterized by data type and latency. |
| 76 | + * |
| 77 | + * This defines a module with one input, `enq`, and one output, `deq`. The input and output are [[Valid]] interfaces |
| 78 | + * that wrap some Chisel type, e.g., a [[UInt]] or a [[Bundle]]. This generator will then chain together a number of |
| 79 | + * pipeline stages that all advance when the input [[Valid]] `enq` fires. The output `deq` [[Valid]] will fire only |
| 80 | + * when valid data has made it all the way through the pipeline. |
| 81 | + * |
| 82 | + * As an example, to construct a 4-stage pipe of 8-bit [[UInt]]s and connect it to a producer and consumer, you can use |
| 83 | + * the following: |
| 84 | + * {{{ |
| 85 | + * val foo = Module(new Pipe(UInt(8.W)), 4) |
| 86 | + * pipe.io.enq := producer.io |
| 87 | + * consumer.io := pipe.io.deq |
| 88 | + * }}} |
| 89 | + * |
| 90 | + * If you already have the [[Valid]] input or the components of a [[Valid]] interface, it may be simpler to use the |
| 91 | + * [[Pipe$ Pipe factory]] companion object. This, which [[Pipe]] internally utilizes, will automatically connect the |
| 92 | + * input for you. |
| 93 | + * |
| 94 | + * @param gen a Chisel type |
| 95 | + * @param latency the number of pipeline stages |
| 96 | + * @see [[Pipe$ Pipe factory]] for an alternative API |
| 97 | + * @see [[Valid]] interface |
| 98 | + * @see [[Queue]] and the [[Queue$ Queue factory]] for actual queues |
| 99 | + * @see The [[ShiftRegister$ ShiftRegister factory]] to generate a pipe without a [[Valid]] interface |
| 100 | + */ |
| 101 | +class Pipe[T <: Data](val gen: T, val latency: Int = 1) extends Module { |
| 102 | + |
| 103 | + /** A non-ambiguous name of this `Pipe` for use in generated Verilog names. |
| 104 | + * Includes the latency cycle count in the name as well as the parameterized |
| 105 | + * generator's `typeName`, e.g. `Pipe4_UInt4` |
| 106 | + */ |
| 107 | + override def desiredName = s"${simpleClassName(this.getClass)}${latency}_${gen.typeName}" |
| 108 | + |
| 109 | + /** Interface for [[Pipe]]s composed of a [[Valid]] input and [[Valid]] output |
| 110 | + * @define notAQueue |
| 111 | + * @groupdesc Signals Hardware fields of the Bundle |
| 112 | + */ |
| 113 | + class PipeIO extends Bundle { |
| 114 | + |
| 115 | + /** [[Valid]] input |
| 116 | + * @group Signals |
| 117 | + */ |
| 118 | + val enq = Input(Valid(gen)) |
| 119 | + |
| 120 | + /** [[Valid]] output. Data will appear here `latency` cycles after being valid at `enq`. |
| 121 | + * @group Signals |
| 122 | + */ |
| 123 | + val deq = Output(Valid(gen)) |
| 124 | + } |
| 125 | + |
| 126 | + val io = IO(new PipeIO) |
| 127 | + |
| 128 | + io.deq <> Pipe(io.enq, latency) |
| 129 | +} |
0 commit comments