Skip to content

Commit

Permalink
Allow the Counter module to be instantiated with a Scala range
Browse files Browse the repository at this point in the history
  • Loading branch information
nullobject committed Jul 15, 2020
1 parent 8fab4d6 commit d83f54e
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 10 deletions.
58 changes: 48 additions & 10 deletions src/main/scala/chisel3/util/Counter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,45 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because
* 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.start >= 0 && r.end >= 0, s"Counter range must be positive, got: $r")

/** Increment the counter
private val delta = math.abs(r.step).U
private val width = math.max(log2Up(r.start), log2Up(r.end)) + 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 n) }

/** The current value of the counter. */
val value = if (r.length > 1) RegInit(r.start.U(width.W)) else r.start.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

when (wrap) {
value := r.start.U
} otherwise {
if (r.step > 0) {
// Increasing range
value := value + delta
} else {
// Decreasing range
value := value - delta
}
}

wrap
} else {
true.B
Expand Down Expand Up @@ -67,4 +90,19 @@ object Counter
when (cond) { wrap := c.inc() }
(c.value, wrap)
}

/** Creates a counter that steps through a specified range of values when a condition is met.
*
* @param r the range of counter values
* @param cond condition that 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, cond: Bool = true.B): (UInt, Bool) = {
val c = new Counter(r)
val wrap = WireInit(false.B)
when (cond) { wrap := c.inc() }
(c.value, wrap)
}
}
14 changes: 14 additions & 0 deletions src/test/scala/chiselTests/Counter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ class WrapTester(max: Int) extends BasicTester {
}
}

class RangeTester(r: Range) extends BasicTester {
val (cnt, wrap) = Counter(r)
when(wrap) {
assert(cnt === r.last.U)
stop()
}
}

class CounterSpec extends ChiselPropSpec {
property("Counter should count up") {
forAll(smallPosInts) { (max: Int) => assertTesterPasses{ new CountTester(max) } }
Expand All @@ -47,4 +55,10 @@ class CounterSpec extends ChiselPropSpec {
property("Counter should wrap") {
forAll(smallPosInts) { (max: Int) => assertTesterPasses{ new WrapTester(max) } }
}

property("Counter should handle a range") {
forAll(posRange) { (r: Range) =>
assertTesterPasses{ new RangeTester(r) }
}
}
}

0 comments on commit d83f54e

Please sign in to comment.