Counters are registers that advance through a numeric sequence — free-running, gated, loadable, or up/down.
Basic binary counter
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) cnt <= '0;
else if (clear) cnt <= '0;
else if (enable) cnt <= cnt + 1'b1;
end
Decide wrap behavior explicitly:
else if (enable) cnt <= (cnt == MAX) ? '0 : cnt + 1'b1;
Useful variants
| Variant | Idea |
|---|---|
| Loadable | if (load) cnt <= load_val |
| Up/down | Add ±1 from a direction flag |
| Prescaler | Count clocks, pulse at terminal count |
| Gray | Adjacent codes differ by 1 bit — friendlier for some CDC |
Terminal count
Generate a single-cycle (or level) flag:
assign tc = enable && (cnt == MAX);
If tc must be clean for async consumers, register it.
Width & overflow
- Size
cntforMAX($clog2(MAX+1)). cnt + 1on an all-ones vector wraps in two’s complement — intentional for free-running timers, bugs for “stop at MAX” designs.
Test ideas
- Reset then count N cycles; check value.
- Enable low → hold.
- Load mid-count.
- Wrap boundary (
MAX → 0).