12Implementation
Checkpoint B: a counter with clear and enable
Problem
Intended behavior. ClearEnableCounter holds a 4-bit count q. At every
rising edge it picks its next value from two control inputs:
clear | enable | q after the edge |
|---|---|---|
| 1 | any | 0 |
| 0 | 1 | q + 1, and 0 after 15 |
| 0 | 0 | unchanged |
q is 0 after the module's implicit synchronous reset. clear is a
different signal: an ordinary input, part of the design's function, that
zeroes the count at the next edge whenever it is high. Reset returns the whole
module to its starting state; clear is what the surrounding logic uses to
restart the count during normal operation, so it must work while reset is
low.
This contract differs from the other problems in this chapter. EdgeCounter
counted events and only reset could zero it; ErrorMonitor had a flag that
only reset cleared. Here a functional input zeroes the register, and it takes
priority over counting.
Current behavior. The starter declares the register and connects it to
q, but never gives it a next value, so q is 0 on every line of the public
run.
Task. Write the update. The declaration val q = RegInit(0.U(4.W)) and the
connection io.q := q stay; the TODO line between them is where the next
value goes. Keep the ports and Main unchanged.
Read the starter
RegInit(0.U(4.W)) already fixes two parts of the contract: the reset value
and the width. A 4-bit register cannot hold 16: it stores only the low four
bits of the assigned sum, so 15 wraps to 0. The
three rows of the table are three mutually exclusive cases with a fixed
priority.
Predict before running
The public run drives enable for four cycles, holds for two, raises clear
together with enable for one cycle, then counts for two more. Each line
prints the inputs and q before that cycle's edge. Write the q column for
the starter and for a correct design: the correct column is
0 1 2 3 4 4 4 0 1, and the last line shows q=2.
Make the change and explain
Write the three cases in priority order with the conditional connection the
Learn chapter uses for RegInit: the first case that applies wins, and a
register that no case connects in a cycle keeps its value. Run, compare with
your prediction, and point to the line that makes the clear row win when
both inputs are high.
Completion check
- With
resetlow, twenty enabled cycles from 0 pass 15 and continue from 0; 16 never appears. clearhigh gives 0 at the next edge, whateverenableandqare.- With
resetandclearlow,enablelow keepsqunchanged for as many cycles as it stays low. - While
resetis high,qis 0 at every edge even withenablehigh. - Any interleaving of
clearandenablefollows the table, not only the public sequence.
Progressive hints
Hint 1
The table has three rows and a priority: the first row applies whatever
enable says. The sequential circuits chapter updates a RegInit register
with := inside a condition. What happens to that register in a cycle where
no condition connects it?
Hint 2
A chain of when and .elsewhen with no .otherwise says exactly "the
first matching row wins, otherwise hold". Put the clear row first.
Hint 3
Adding 1.U with + keeps the register's width, so 15 + 1 is 0 without a
comparison. Compare that with the Learn chapter's wrapCounter, which needs an
explicit comparison because its maximum is not one less than a power of two.
Variation
Change the register to 3 bits and predict whether the public sequence reaches
the first wrap. How many consecutive enabled edges from 0, with clear low,
would reach it? Then try 5 bits and explain which bits of the count io.q
shows and whether this public sequence reveals the truncation. Finally, give
clear the lower priority and find every public line that changes.