Video 1 of 4 · ~12 minutes
Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF
Counters are the second-most-instantiated cell in any SoC (flops are first). Refresh counters in DRAM controllers. PC counters in CPUs. Sample counters in DSPs. Baud-rate dividers in every UART. Frame counters in video pipelines. A typical ARM Cortex-A processor has 50+ counter instances across its peripherals.
Your Day 5 debouncer uses a modulo counter. Day 7 FSM timers are counters. Day 11 UART baud generator is a modulo counter. Day 12 SPI clock divider is a loadable counter. Today's patterns appear in every remaining lab.
“A modulo-N counter is just a free-running counter that I mask with % N. The synthesizer will figure it out.”
Two fundamentally different circuits. Free-running: count to 2N-1, wrap via overflow (free in hardware). Modulo-N: count to arbitrary N-1, wrap via explicit comparator + reset. The modulo-N version adds a comparator (~log₂N LUTs) but can count to any value.
% does not synthesize efficiently for non-power-of-2 moduli. You always write the comparator + conditional reset pattern explicitly. The synthesizer builds exactly what you wrote.
module mod_n_counter #(parameter N = 10) (
input wire i_clk, i_reset,
output reg [$clog2(N)-1:0] o_count,
output wire o_wrap
);
always @(posedge i_clk) begin
if (i_reset) o_count <= 0;
else if (o_count == N-1) o_count <= 0; // wrap
else o_count <= o_count + 1'b1;
end
assign o_wrap = (o_count == N-1); // pulse at rollover
endmodule
$clog2(N) auto-sizes the width — no off-by-one errors. The o_wrap pulse goes high for exactly one cycle per cycle-of-N; perfect for cascading counters or triggering periodic events.
module up_down_counter #(parameter W = 8) (
input wire i_clk, i_reset, i_up,
output reg [W-1:0] o_count
);
always @(posedge i_clk) begin
if (i_reset) o_count <= 0;
else if (i_up) o_count <= o_count + 1'b1;
else o_count <= o_count - 1'b1;
end
endmodule
i_up=0 and o_count=0? Rollover to 2W-1 — unsigned wraparound. If you want saturation instead, add a conditional guard.
Extend the basic counter: synchronous load of i_load_val when i_load=1, otherwise increment.
always @(posedge i_clk) begin
if (i_reset) o_count <= 0;
else if (i_load) o_count <= i_load_val;
else o_count <= o_count + 1'b1;
end
Priority: reset > load > count. This is the preload pattern used in timers and PWM period registers.
~4 minutes
▸ COMMANDS
cd labs/week2_day05/ex1_mod_n/
make sim # exhaustive modulo test
make wave # see the wrap pulse
make stat # SB_CARRY count
▸ EXPECTED OUTPUT
PASS: counts 0..N-1
PASS: wraps at N
PASS: o_wrap pulses 1 cycle
=== 20 passed, 0 failed ===
▸ GTKWAVE
Add o_count · o_wrap. Count climbs 0, 1, ..., N-1. At N-1, o_wrap pulses high for exactly one cycle and count resets to 0. The sawtooth pattern is the signature of a modulo counter.
$ yosys -p "read_verilog mod_n_counter.v; synth_ice40 -top mod_n_counter; stat" -q
=== mod_n_counter === (N=10, so width = $clog2(10) = 4 bits)
Number of wires: 13
Number of cells: 15
SB_CARRY 3 ← carry chain for +1
SB_DFF 0
SB_DFFESR 4 ← 4 counter flops
SB_LUT4 8 ← comparator logic + combinational
== N-1). If N were a power of 2, the comparator would vanish — that's the classical iCE40 shortcut: always pick powers of 2 when possible.
Ask AI: “Write a parameterized Verilog counter that counts from 0 to N-1 and pulses a done signal at the end. Then estimate LUT count on an iCE40 for N=1000.”
TASK
Parameterized counter + LUT estimate for N=1000.
BEFORE
Predict: 10 flops ($clog2(1000)=10), 9 carry, ~5 LUTs for compare.
AFTER
AI should use $clog2. Weak AI hardcodes the width or misses parameter.
TAKEAWAY
Verify absolute numbers with Yosys — AI LUT estimates are often 2× off.
① Modulo-N = explicit comparator + conditional reset.
② Use $clog2(N) to auto-size counter width.
③ Up/down, loadable: same core + priority-encoded inputs.
④ Wrap pulse (1 cycle) is often more useful than the count.
% in synthesizable code. Write the comparator.🔗 Transfer
Video 2 of 4 · ~10 minutes
▸ WHY THIS MATTERS NEXT
Counters hold numbers. Shift registers hold streams of bits. Video 2 introduces SIPO and PISO — the two patterns that power every serial protocol. These will be your UART transmitter and receiver in Week 3.