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
|
// SPDX-License-Identifier: Apache-2.0
package chisel3.util
import chisel3._
import chisel3.experimental.AffectsChiselPrefix
/** Used to generate an inline (logic directly in the containing Module, no internal Module is created)
* hardware counter.
*
* Typically instantiated with apply methods in [[Counter$ object Counter]]
*
* Does not create a new Chisel Module
*
* @example {{{
* val countOn = true.B // increment counter every clock cycle
* val (counterValue, counterWrap) = Counter(countOn, 4)
* when (counterValue === 3.U) {
* ...
* }
* }}}
* @example {{{
* // Using Scala Range API
* val (counterValue, counterWrap) = Counter(0 until 10 by 2)
* when (counterValue === 4.U) {
* ...
* }
* }}}
*/
class Counter private (r: Range, oldN: Option[Int] = None) extends AffectsChiselPrefix {
require(r.length > 0, s"Counter range cannot be empty, got: $r")
require(r.start >= 0 && r.end >= 0, s"Counter range must be positive, got: $r")
private lazy val delta = math.abs(r.step)
private lazy val width = math.max(log2Up(r.last + 1), log2Up(r.head + 1))
/** Number of counts before the counter resets
*
* @note Only defined for ranges starting at zero with steps of size 1. Use [[range]] for other
* use cases.
*/
def n: Int = oldN match {
case Some(x) => x
case None =>
// Reasonable for typical ranges
require(
r.start == 0 && r.step == 1,
s"Counter.n only defined on ranges starting at 0 with step == 1, got $r. " +
"Use underlying range."
)
r.last + 1
}
/** Creates a counter with the specified number of steps.
*
* @param n number of steps before the counter resets
*/
def this(n: Int) = {
this(0 until math.max(1, n), Some(n))
}
/** The current value of the counter. */
val value = if (r.length > 1) RegInit(r.head.U(width.W)) else WireInit(r.head.U)
/** The range of the counter values. */
def range: Range = r
/** Increments the counter by a step.
*
* @note The incremented value is registered and will be visible on the next clock cycle
* @return whether the counter will wrap on the next clock cycle
*/
def inc(): Bool = {
if (r.length > 1) {
val wrap = value === r.last.U
if (r.step > 0) {
// Increasing range
value := value + delta.U
} else {
// Decreasing range
value := value - delta.U
}
// We only need to explicitly wrap counters that don't start at zero, or
// end on a power of two. Otherwise we just let the counter overflow
// naturally to avoid wasting an extra mux.
if (!(r.head == 0 && isPow2(r.last + delta))) {
when(wrap) { value := r.head.U }
}
wrap
} else {
true.B
}
}
/** Resets the counter to its initial value */
def reset(): Unit = {
value := r.head.U
}
}
object Counter {
/** Instantiate a [[Counter! counter]] with the specified number of counts.
*/
def apply(n: Int): Counter = new Counter(n)
/** Instantiate a [[Counter! counter]] with the specified number of counts and a gate.
*
* @param cond condition that controls whether the counter increments this cycle
* @param n number of counts before the counter resets
* @return tuple of the counter value and whether the counter will wrap (the value is at
* maximum and the condition is true).
*/
def apply(cond: Bool, n: Int): (UInt, Bool) = {
val c = new Counter(n)
val wrap = WireInit(false.B)
when(cond) { wrap := c.inc() }
(c.value, wrap)
}
/** Creates a counter that steps through a specified range of values.
*
* @param r the range of counter values
* @param enable controls whether the counter increments this cycle
* @param reset resets the counter to its initial value during this cycle
* @return tuple of the counter value and whether the counter will wrap (the value is at
* maximum and the condition is true).
*/
def apply(r: Range, enable: Bool = true.B, reset: Bool = false.B): (UInt, Bool) = {
val c = new Counter(r)
val wrap = WireInit(false.B)
when(reset) {
c.reset()
}.elsewhen(enable) {
wrap := c.inc()
}
(c.value, wrap)
}
}
|