24Verification
Size the counter for its limit
Problem
Intended behavior. WrapCounter(cfg) counts 0, 1, ..., cfg.limit on
cycles where en is high, then wraps to 0. atLimit is 1 exactly while the
count equals the limit. WrapConfig holds the limit and derives the register
width from it, so every user of the configuration agrees on that width.
CounterBank builds three counters with limits 5, 8, and 16 that share one
en.
Observed behavior. The starter compiles and the 5-limit counter behaves, but the other two never reach their limits.
Task. Find the faulty parameter derivation and correct it. Keep the case
class, its require, WrapCounter, and CounterBank as they are otherwise.
Run and compare
Run the starter. The public run holds en high and prints the three counts and
atLimit flags for eleven cycles. Mark the first cycle at which the 8-limit
counter departs from the specification and what it does instead. The 16-limit
counter needs more cycles than the run prints to show its symptom; predict what
it would do at that point.
Diagnose
Every width in WrapCounter comes from cfg.width. For each of the three
limits, compute that width by hand and the largest value a register of that
width can hold. Compare the largest value with the value the counter must reach.
Ask why limit 5 hides the problem while limits 8 and 16 expose it.
Fix, run, and explain
Correct the derivation and run again. The 8-limit counter must now show 8 with
its atLimit flag, then 0 on the next line. The 16-limit counter still cannot
reach its limit within eleven cycles, so the public run cannot prove that part
of the fix. Explain from your width table why it now works.
Completion check
- Each counter reaches its own limit, reports
atLimitthere, and wraps to 0 on the next enabled cycle. - Counters hold their values while
enis low. - Reset returns all three to 0.
- Derive the width from the configuration rather than choosing a fixed width that happens to fit the three supplied limits.
For additional configurations, keep limit positive and limit + 1 within
Scala's Int range. The bank's unchanged 8-bit count outputs must also be able
to represent the chosen limits; they are separate from the internal register
width.
Progressive hints
Hint 1
The counter must hold limit + 1 distinct values, 0 through limit.
Hint 2
log2Ceil(n) is the number of bits needed to tell n values apart, which
means the largest representable value is one less than a power of two. Look at
which number is passed in.
Concept
A derived parameter in a case class is computed once and trusted by every
module that reads it. An off-by-one there produces a circuit that elaborates
cleanly and is wrong only for some configurations: here, limits that are a
power of two. The comparison count === cfg.limit.U widens silently, so no
width error points at the problem.
Extension experiment
Add a limit of 1 to the bank in your head. Compute the width with the starter's derivation and with yours, and predict what the starter would build for that counter.