Topic 5 · Counters, Shifters & Sync

Counter Variations

Video 1 of 4 · ~12 minutes

Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF

Counter VariationsShift RegistersMetastabilityDebouncing

🌍 Where This Lives

Where it shows up

The microwave that knows “30 seconds” means 30 seconds. The metronome ticking in a piano app. The blinking cursor in this editor. The 60Hz refresh of your monitor. Every WiFi slot, every USB heartbeat, every audio sample at 48kHz — somewhere underneath, something is keeping score, wrapping back to zero, and starting again.

When it goes wrong

Dhahran, February 1991: a Patriot battery missed an incoming Scud. Twenty-eight soldiers died. The intercept math relied on a tally that had been running for 100 hours and had drifted by a fraction of a second. The 2015 Boeing 787 “reboot every 248 days” advisory was the same family of bug: a number that ticks up forever, in a place its designer thought would always be reset first.

Career tag: “Parameterized counter” is a staple interview question. Know the modulo-N, up/down, and loadable patterns cold.

⚠️ Two Kinds of Wraparound

❌ Wrong Model

“A modulo-N counter is just a free-running counter that I mask with % N. The synthesizer will figure it out.”

✓ Right Model

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.

The problem with %: Verilog % by a non-power-of-2 synthesizes to a divider — a multi-cycle, multi-LUT, multi-carry-chain monster. On iCE40, count % 10 can cost 30–60 LUTs and miss timing at moderate clock rates. An explicit comparator + conditional reset costs ~4 LUTs and meets timing easily. You always write the comparator pattern; never the %.

📘 Modulo-N Counter — Definition

A modulo-N counter is a counter that takes values from $\{0, 1, 2, \ldots, N-1\}$ and wraps back to $0$ on the cycle after reaching $N-1$. It cycles forever through exactly $N$ distinct states.

Required width: to represent $N$ distinct values we need $$W \;=\; \lceil \log_2 N \rceil \;=\; \texttt{\$clog2}(N)$$ bits. The SystemVerilog primitive $clog2(N) computes the ceiling of the base-2 log at elaboration time — no off-by-one bugs.

Examples: $N{=}10 \Rightarrow W{=}4$ (since $2^4{=}16 \ge 10$); $N{=}1000 \Rightarrow W{=}10$; $N{=}500{,}000 \Rightarrow W{=}19$.

Modulo-N counter behavior overview
Three signals you almost always want: the count value o_count, a 1-cycle o_wrap pulse at the rollover, and (optionally) an enable input i_enable so the counter can be paused.

🔧 Modulo-N Counter — RTL View

Modulo-N counter RTL block diagram with adder, register, and equality comparator
Three pieces of hardware: (1) a $W$-bit register holding the state, (2) an increment-by-one adder feeding back into the register, (3) an equality comparator that detects o_count == N-1 and forces the next state to $0$. Everything else — enable, reset — is just steering logic on the register's D input.

👁️ I Do — Modulo-N Counter

module counter_mod_n #(parameter N = 10) (
    input  wire i_clk, i_reset, i_enable,
    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 (i_enable && o_count == N-1) o_count <= 0;          // wrap
        else if (i_enable)                    o_count <= o_count + 1'b1;
    end
    assign o_wrap = i_enable && (o_count == N-1);                    // pulse at rollover
endmodule
My thinking: the port width comes straight from the math definition, $$W \;=\; \lceil \log_2 N \rceil \;=\; \texttt{\$clog2}(N),$$ which is the smallest $W$ satisfying $2^W \ge N$. Using $clog2(N) lets the elaborator size the bus — no hand-counted bit widths, no off-by-one errors when $N$ changes. The o_wrap pulse goes high for exactly one cycle per cycle-of-$N$; perfect for cascading counters or triggering periodic events.

🤝 We Do — Up/Down Counter

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
Up/Down counter RTL: +1 and -1 adders feed a 2:1 MUX, i_up selects which result enters the register
Together: What happens when i_up=0 and o_count=0? Rollover to 2W-1 — unsigned wraparound. If you want saturation instead, add a conditional guard.

🧪 You Do — Write the Loadable Counter

Extend the basic counter: synchronous load of i_load_val when i_load=1, otherwise increment.

Answer:
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
Loadable counter RTL: 2:1 MUX picks i_load_val or count+1, register holds o_count, reset has top priority on the register Priority: reset > load > count. This is the preload pattern used in timers and PWM period registers.

🎛️ Where Loadable Counters Live — A PWM Period Register

We've name-dropped “PWM period register” in a few decks now. Here's what one actually looks like — and why the loadable counter you just wrote is the period register.

PWM block diagram: period register feeds the loadable counter, duty register feeds the comparator, comparator output drives o_pwm; below, three sample waveforms at fixed N=8 with duty=2/4/6 (25/50/75%) showing how the duty register sets high-time within a fixed period
How it fits: the i_load_val port of your loadable counter is the period register; it sets $N$. A separate duty register and comparator then split each period into a high-time and low-time. Change the period register → frequency changes. Change the duty register → brightness/speed changes.

🧩 PWM Period Register — RTL Sketch

Not the full peripheral — just the three pieces that turn the previous diagram into Verilog. The middle block is literally the loadable counter you just wrote, with the wrap event playing the role of i_load.

// 1) Period & duty registers — software-loadable (CSR-style)
reg [W-1:0] period, duty;          // period = N-1; duty = high-time
always @(posedge i_clk) begin
    if (i_we_period) period <= i_wdata;
    if (i_we_duty)   duty   <= i_wdata;
end

// 2) Period counter — same priority chain as the previous slide.
//    The "load" event is the natural end-of-period wrap.
reg [W-1:0] count;
always @(posedge i_clk) begin
    if      (i_reset)         count <= 0;
    else if (count == period) count <= 0;        // ← reload @ wrap
    else                      count <= count + 1'b1;
end

// 3) Duty comparator drives the PWM output
assign o_pwm = (count < duty);
PWM period register RTL: write enables gate CSR registers (period and duty), period counter wraps when count==period, count<duty drives o_pwm
Three signals to keep straight: period sets $N$ (frequency), duty sets the high-time (brightness), count is the loadable counter sweeping 0..N-1. Topic 10's PWM lab is this skeleton plus bus glue.
▶ LIVE DEMO

Modulo-N Counter + Cascade

~4 minutes

▸ COMMANDS

cd lecture_examples/week2_day05/d05_s1_ex1/
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.

🔧 What Did the Tool Build?

$ yosys -p "read_verilog day05_ex01_counter_mod_n.v; synth_ice40 -top counter_mod_n; stat" -q

=== counter_mod_n ===  (N=10, so width = $clog2(10) = 4 bits)
   Number of wires:                 14
   Number of cells:                 15
     SB_CARRY                        2     ← carry chain for +1
     SB_DFF                          0
     SB_DFFESR                       4     ← 4 counter flops
     SB_LUT4                         9     ← comparator logic + combinational
What to notice: 4 flops (counter state) + 2 carry cells (increment) + comparator LUTs (for == 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.
Scaling: 32-bit modulo with large N ≈ 32 flops + ~16 carry + ~6 LUTs for the comparator. Still tiny.

🤖 Check the Machine

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.

Key Takeaways

 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.

Never write % in synthesizable code. Write the comparator.

🔗 Transfer

Shift Registers

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.