summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorJosh Bassett2020-07-30 15:11:33 +1000
committerGitHub2020-07-30 05:11:33 +0000
commitfca89f6c8544a1e0b42e1cec34207f74bf238e8a (patch)
treeb6b6ed8c10bb11364ffca807ee41241ca1dbc697 /src/main
parent164490c8fbf132ca65644d05d6ff8d0d7a3beb20 (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')
-rw-r--r--src/main/scala/chisel3/util/Counter.scala65
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)
+ }
}