diff options
| author | Josh Bassett | 2020-07-30 15:11:33 +1000 |
|---|---|---|
| committer | GitHub | 2020-07-30 05:11:33 +0000 |
| commit | fca89f6c8544a1e0b42e1cec34207f74bf238e8a (patch) | |
| tree | b6b6ed8c10bb11364ffca807ee41241ca1dbc697 /src/main/scala/chisel3/util | |
| parent | 164490c8fbf132ca65644d05d6ff8d0d7a3beb20 (diff) | |
Allow a counter to be instantiated using a Scala range (#1515)
* Add positive range generator
* Allow the Counter module to be instantiated with a Scala range
* Use head/last to determine counter width
Co-authored-by: Jack Koenig <jack.koenig3@gmail.com>
* Let counter overflow naturally when appropriate
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.
* Require counter range to be non-empty
Co-authored-by: Jack Koenig <jack.koenig3@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Diffstat (limited to 'src/main/scala/chisel3/util')
| -rw-r--r-- | src/main/scala/chisel3/util/Counter.scala | 65 |
1 files changed, 52 insertions, 13 deletions
diff --git a/src/main/scala/chisel3/util/Counter.scala b/src/main/scala/chisel3/util/Counter.scala index cb503055..11daa515 100644 --- a/src/main/scala/chisel3/util/Counter.scala +++ b/src/main/scala/chisel3/util/Counter.scala @@ -19,27 +19,51 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because * ... * } * }}} - * - * @param n number of counts before the counter resets (or one more than the - * maximum output value of the counter), need not be a power of two */ @chiselName -class Counter(val n: Int) { - require(n >= 0, s"Counter value must be nonnegative, got: $n") - val value = if (n > 1) RegInit(0.U(log2Ceil(n).W)) else 0.U +class Counter private (r: Range) { + 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)) + + /** 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)) } - /** Increment the counter + /** The current value of the counter. */ + val value = if (r.length > 1) RegInit(r.head.U(width.W)) else 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 to zero on the next cycle + * @return whether the counter will wrap on the next clock cycle */ def inc(): Bool = { - if (n > 1) { - val wrap = value === (n-1).U - value := value + 1.U - if (!isPow2(n)) { - when (wrap) { value := 0.U } + 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 @@ -67,4 +91,19 @@ object Counter 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 + * @return tuple of the counter value and whether the counter will wrap (the value is at + * maximum and the condition is true). + */ + @chiselName + def apply(r: Range, enable: Bool = true.B): (UInt, Bool) = { + val c = new Counter(r) + val wrap = WireInit(false.B) + when (enable) { wrap := c.inc() } + (c.value, wrap) + } } |
