summaryrefslogtreecommitdiff
path: root/src/main/scala/chisel3/util/Valid.scala
blob: 85948f37a6f1b7fe5a7cced150bf7a0d19c5c30c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// SPDX-License-Identifier: Apache-2.0

/** Wrappers for valid interfaces and associated circuit generators using them.
  */

package chisel3.util

import chisel3._
import chisel3.experimental.prefix
import chisel3.util.simpleClassName

/** A [[Bundle]] that adds a `valid` bit to some data. This indicates that the user expects a "valid" interface between
  * a producer and a consumer. Here, the producer asserts the `valid` bit when data on the `bits` line contains valid
  * data. This differs from [[DecoupledIO]] or [[IrrevocableIO]] as there is no `ready` line that the consumer can use
  * to put back pressure on the producer.
  *
  * In most scenarios, the `Valid` class will ''not'' be used directly. Instead, users will create `Valid` interfaces
  * using the [[Valid$ Valid factory]].
  * @tparam T the type of the data
  * @param gen some data
  * @see [[Valid$ Valid factory]] for concrete examples
  * @groupdesc Signals The actual hardware fields of the Bundle
  */
class Valid[+T <: Data](gen: T) extends Bundle {

  /** A bit that will be asserted when `bits` is valid
    * @group Signals
    */
  val valid = Output(Bool())

  /** The data to be transferred, qualified by `valid`
    * @group Signals
    */
  val bits = Output(gen)

  /** True when `valid` is asserted
    * @return a Chisel [[Bool]] true if `valid` is asserted
    */
  def fire: Bool = valid

  /** A non-ambiguous name of this `Valid` instance for use in generated Verilog names
    * Inserts the parameterized generator's typeName, e.g. Valid_UInt4
    */
  // todo scala3 fix
  // override def typeName = s"${simpleClassName(this.getClass)}_${gen.typeName}"

  /** Applies the supplied functor to the bits of this interface, returning a new typed Valid interface.
    * @param f The function to apply to this Valid's 'bits' with return type B
    * @return a new Valid of type B
    */
  def map[B <: Data](f: T => B): Valid[B] = {
    val _map_bits = f(bits)
    val _map = Wire(Valid(chiselTypeOf(_map_bits)))
    _map.bits := _map_bits
    _map.valid := valid
    _map.readOnly
  }
}

/** Factory for generating "valid" interfaces. A "valid" interface is a data-communicating interface between a producer
  * and a consumer where the producer does not wait for the consumer. Concretely, this means that one additional bit is
  * added to the data indicating its validity.
  *
  * As an example, consider the following [[Bundle]], `MyBundle`:
  * {{{
  *   class MyBundle extends Bundle {
  *     val foo = Output(UInt(8.W))
  *   }
  * }}}
  *
  * To convert this to a `valid` interface, you wrap it with a call to the `Valid` companion object's apply method:
  * {{{
  *   val bar = Valid(new MyBundle)
  * }}}
  *
  * The resulting interface is ''structurally'' equivalent to the following:
  * {{{
  *   class MyValidBundle extends Bundle {
  *     val valid = Output(Bool())
  *     val bits = Output(new MyBundle)
  *   }
  * }}}
  *
  * In addition to adding the `valid` bit, a `Valid.fire` method is also added that returns the `valid` bit. This
  *
  * provides a similarly named interface to [[DecoupledIO]]'s fire.
  *
  * @see [[Decoupled$ DecoupledIO Factory]]
  * @see [[Irrevocable$ IrrevocableIO Factory]]
  */
object Valid {

  /** Wrap some [[Data]] in a valid interface
    * @tparam T the type of the data to wrap
    * @param gen the data to wrap
    * @return the wrapped input data
    */
  def apply[T <: Data](gen: T): Valid[T] = new Valid(gen)
}

/** A factory to generate a hardware pipe. This can be used to delay [[Valid]] data by a design-time configurable number
  * of cycles.
  *
  * Here, we construct three different pipes using the different provided `apply` methods and hook them up together. The
  * types are explicitly specified to show that these all communicate using [[Valid]] interfaces:
  * {{{
  *   val in: Valid[UInt]  = Wire(Valid(UInt(2.W)))
  *
  *   /* A zero latency (combinational) pipe is connected to 'in' */
  *   val foo: Valid[UInt] = Pipe(in.valid, in.bits, 0)
  *
  *   /* A one-cycle pipe is connected to the output of 'foo' */
  *   val bar: Valid[UInt] = Pipe(foo.valid, foo.bits)
  *
  *   /* A two-cycle pipe is connected to the output of 'bar' */
  *   val baz: Valid[UInt] = Pipe(bar, 2)
  * }}}
  *
  * @see [[Pipe Pipe class]] for an alternative API
  * @see [[Valid]] interface
  * @see [[Queue]] and the [[Queue$ Queue factory]] for actual queues
  * @see The [[ShiftRegister$ ShiftRegister factory]] to generate a pipe without a [[Valid]] interface
  * @define returnType the [[Valid]] output of the final pipeline stage
  */
object Pipe {

  /** Generate a pipe from an explicit valid bit and some data
    * @param enqValid the valid bit (must be a hardware type)
    * @param enqBits the data (must be a hardware type)
    * @param latency the number of pipeline stages
    * @return $returnType
    */
  def apply[T <: Data](enqValid: Bool, enqBits: T, latency: Int): Valid[T] = {
    require(latency >= 0, "Pipe latency must be greater than or equal to zero!")
    if (latency == 0) {
      val out = Wire(Valid(chiselTypeOf(enqBits)))
      out.valid := enqValid
      out.bits := enqBits
      out
    } else
      prefix("pipe") {
        val v = RegNext(enqValid, false.B)
        val b = RegEnable(enqBits, enqValid)
        apply(v, b, latency - 1)
      }
  }

  /** Generate a one-stage pipe from an explicit valid bit and some data
    * @param enqValid the valid bit (must be a hardware type)
    * @param enqBits the data (must be a hardware type)
    * @return $returnType
    */
  def apply[T <: Data](enqValid: Bool, enqBits: T): Valid[T] = {
    apply(enqValid, enqBits, 1)
  }

  /** Generate a pipe for a [[Valid]] interface
    * @param enq a [[Valid]] interface (must be a hardware type)
    * @param latency the number of pipeline stages
    * @return $returnType
    */
  def apply[T <: Data](enq: Valid[T], latency: Int = 1): Valid[T] = {
    apply(enq.valid, enq.bits, latency)
  }
}

/** Pipeline module generator parameterized by data type and latency.
  *
  * This defines a module with one input, `enq`, and one output, `deq`. The input and output are [[Valid]] interfaces
  * that wrap some Chisel type, e.g., a [[UInt]] or a [[Bundle]]. This generator will then chain together a number of
  * pipeline stages that all advance when the input [[Valid]] `enq` fires. The output `deq` [[Valid]] will fire only
  * when valid data has made it all the way through the pipeline.
  *
  * 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
  * the following:
  * {{{
  *   val foo = Module(new Pipe(UInt(8.W)), 4)
  *   pipe.io.enq := producer.io
  *   consumer.io := pipe.io.deq
  * }}}
  *
  * If you already have the [[Valid]] input or the components of a [[Valid]] interface, it may be simpler to use the
  * [[Pipe$ Pipe factory]] companion object. This, which [[Pipe]] internally utilizes, will automatically connect the
  * input for you.
  *
  * @param gen a Chisel type
  * @param latency the number of pipeline stages
  * @see [[Pipe$ Pipe factory]] for an alternative API
  * @see [[Valid]] interface
  * @see [[Queue]] and the [[Queue$ Queue factory]] for actual queues
  * @see The [[ShiftRegister$ ShiftRegister factory]] to generate a pipe without a [[Valid]] interface
  */
class Pipe[T <: Data](val gen: T, val latency: Int = 1) extends Module {

  /** A non-ambiguous name of this `Pipe` for use in generated Verilog names.
    * Includes the latency cycle count in the name as well as the parameterized
    * generator's `typeName`, e.g. `Pipe4_UInt4`
    */
  // todo scala3 fix
  // override def desiredName = s"${simpleClassName(this.getClass)}${latency}_${gen.typeName}"

  /** Interface for [[Pipe]]s composed of a [[Valid]] input and [[Valid]] output
    * @define notAQueue
    * @groupdesc Signals Hardware fields of the Bundle
    */
  class PipeIO extends Bundle {

    /** [[Valid]] input
      * @group Signals
      */
    val enq = Input(Valid(gen))

    /** [[Valid]] output. Data will appear here `latency` cycles after being valid at `enq`.
      * @group Signals
      */
    val deq = Output(Valid(gen))
  }

  val io = IO(new PipeIO)

  io.deq <> Pipe(io.enq, latency)
}