Digital Design / Module 5: Counters and frequency dividers
27. Up, down and loadable counters
Write the most-used sequential block.
Lab: Counter lab
The idea
A counter is a register plus an adder, and it may be the most-used sequential block in chips. It handles timers, addresses, baud rates, and any "wait N cycles" requirement.
Mod-N counters
Binary counters wrap naturally at 2^N. To count to anything else, reset at N-1.
module mod10 (
input clk, rst, en, up, load,
input [3:0] d,
output reg [3:0] q,
output reg tc
);
always_ff @(posedge clk) begin
tc <= 1'b0;
if (rst) q <= 4'd0;
else if (load) q <= d;
else if (en) begin
if (up) begin
q <= (q == 4'd9) ? 4'd0 : q + 4'd1;
tc <= (q == 4'd9);
end else begin
q <= (q == 4'd0) ? 4'd9 : q - 4'd1;
tc <= (q == 4'd0);
end
end
end
endmoduleSizing
A count up to N-1 needs $clog2(N) bits. Mod-10 needs four flops, and six of the sixteen possible values are never used.
Terminal count
Other logic often needs a one-cycle pulse at the end of the count. If you compute it combinationally from the counter bits, it can while those bits change, and it adds delay at the start of the next path. A registered terminal count avoids both, though you must account for the extra cycle.
Timer math
To divide 100 MHz down to a 1 kHz tick, count from 0 to 99,999, and pulse once per wrap. That takes 17 bits, because 2^17 is 131,072. Use the pulse as an enable for slower logic, and keep everything on the fast clock.
Common mistake
An off-by-one in the wrap. A mod-10 counter resets at 9, not 10. Also watch the width: q + 1 on a 4-bit q at 15 wraps to 0 in a 4-bit assignment, but in a wider comparison, it becomes 16. Width rules matter here.
Key ideas
- Add one on enable, then define wrap-around.
- Mod-N counters reset at N-1.
- Terminal count is often a registered pulse.
Your exercise
Find the bug
This code has one deliberate bug. Read the symptom, then click the line you think is wrong.
The fix
else q <= (q == 4'd9) ? 4'd0 : q + 4'd1;
Why it failed
A mod-10 counter has states 0 to 9, so the next value after 9 is 0. Comparing against 10 lets the counter reach 10 first. This off-by-one is especially easy to miss, because 10 fits in 4 bits and nothing looks broken.
Interview questions
coreHow many flops for a mod-10 counter?
Four.
basicFrom a 50 MHz clock you need a one-pulse-per-second tick. What does the counter count to, and how many bits does it need?
One second is 50,000,000 cycles, so the counter runs from 0 to 49,999,999 and pulses once per wrap. 2^25 is 33,554,432, which is too small, and 2^26 is 67,108,864, so it needs 26 bits. The tick should be used as an enable on the 50 MHz clock, not as a new clock.
basicIn a counter written as if (rst) ... else if (load) ... else if (en) ..., what happens when rst and load are both high, and why?
The counter resets, because the order of the if-else chain is the order of priority. rst is tested first, so load and en are only looked at when rst is low. Writing the chain in the intended priority order is part of the specification of the counter.
coreThis is meant to be a mod-10 counter. What sequence does it actually produce, and how do you fix it?
always @(posedge clk) if (rst) q <= 4'd0; else q <= (q == 4'd10) ? 4'd0 : q + 4'd1;
It counts 0, 1, ... 9, 10 and then back to 0, which is 11 states, so it is a mod-11 counter. A mod-N counter must wrap when it reaches N-1, so the compare should be q == 4'd9. This off-by-one is easy to see in a waveform as a stray 10 in the sequence.
coreIn this mod-10 counter, tc is a registered terminal-count pulse. On which value of q is tc high, and why does that matter to the logic that uses it?
always @(posedge clk) begin
tc <= 1'b0;
if (rst) q <= 4'd0;
else if (en) begin
q <= (q == 4'd9) ? 4'd0 : q + 4'd1;
tc <= (q == 4'd9);
end
endtc is high in the cycle when q already shows 0, one cycle after q was 9. It is set at the same edge that wraps q from 9 to 0, so it lines up with the new value, not the old one. The registered pulse cannot glitch and has clean timing, but the user must allow for the extra cycle, or compare with 8 instead of 9 if the pulse is needed while q is 9.
corew is a 4-bit reg holding 4'hF. Which of these two comparisons is true, and why do they differ?
reg [3:0] w = 4'hF; // A: (w + 4'd1 == 4'd0) // B: (w + 1'b1 == 0)
A is true and B is false. In A every operand is 4 bits wide, so w + 4'd1 is worked out in 4 bits and wraps to 0. In B the unsized literal 0 is 32 bits, so the whole comparison is evaluated at 32 bits and w + 1'b1 becomes 16, which is not 0. Verilog sizes an expression from its widest operand, so a wrap check can silently fail when an unsized literal sneaks in.
coreWrite a 4-bit counter that counts up and stops at 15 instead of wrapping. What is the key difference from a normal counter?
always @(posedge clk) if (rst) q <= 4'd0; else if (q != 4'hF) q <= q + 4'd1;
Only increment while the counter is below its maximum, for example if (q != 4'hF) q <= q + 4'd1; inside the clocked block. When q reaches 15 the condition fails and the flop holds, so the count saturates. This is common for error or event counters, where wrapping back to a small number would hide how many events really happened.
advancedThe mod-10 up counter has a load input, and someone loads the value 12. What does the counter do next, if it compares only with 9 to wrap?
It counts 12, 13, 14, 15, and then 15 + 1 wraps to 0 in 4 bits, after which it runs 0 to 9 normally. It never matches 9 on the way, so it spends four cycles outside its legal range and the period of that first pass is wrong. Robust designs either check the load value or wrap on q >= 9, so an out-of-range value is corrected on the next step.
advancedA 64-bit free-running counter must run at a clock frequency where a full 64-bit increment does not meet timing. How can you build it?
Split it into a low part and a high part, for example a 16-bit low counter and a 48-bit high counter. The high part increments only when a registered carry flag is set, and to keep the count exact the flag is set one cycle early, when the low part equals all ones minus one, so it is ready on the cycle the low part wraps. The low part's carry chain is then no longer in series with the high part's, and the high part can be split again the same way if it is still too slow. A small simulation of this split, 4 plus 4 bits, matched a plain 8-bit counter on every cycle.