Day 4 · Sequential Logic Fundamentals

Counters & Clock Division

Video 4 of 4 · ~14 minutes

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

Clocks & EdgesNonblockingFF VariantsCounters

🌍 Where This Lives

In Industry

Counters are everywhere. UART baud-rate generators. SPI clock dividers. Refresh counters in DRAM. Frame counters in video pipelines. PWM period counters. Watchdog timers. A typical SoC has hundreds of them. Mastering the counter pattern means mastering a quarter of all digital design.

In This Course

Day 5: debouncer uses a counter. Day 7: FSM timers use counters. Day 11: UART baud generator is a counter. Day 12: SPI shift-clock divider is a counter. This pattern will be reused more than any other for the rest of the semester.

First end-to-end design: Today you take your Verilog from written code all the way to blinking LEDs on physical hardware. That's the full stack: RTL → synthesis → place & route → bitstream → FPGA → photons. It's a big moment.

⚠️ A Counter Is Not a Loop

❌ Wrong Model

“I'll write for (i=0; i<N; i++) count++; and it will count. The clock makes it loop.”

✓ Right Model

A counter is a flop whose D input is its own Q plus one. Each clock edge captures that new value. The “counting” happens in time (cycle by cycle), not in code. There's no loop — the hardware is a single adder feeding a single register, cycling forever.

The receipt: An N-bit counter uses N flops + N LUT/carry cells. Fixed footprint. Counts to 2N-1 then rolls over to 0. The count value changes each cycle because the clock keeps firing, not because the code “runs.”

👁️ I Do — The Free-Running Counter

reg [7:0] r_count;

always @(posedge i_clk) begin
    if (i_reset) r_count <= 8'b0;
    else         r_count <= r_count + 1'b1;
end

// Cycle 0:   r_count = 8'h00
// Cycle 1:   r_count = 8'h01
// ...
// Cycle 255: r_count = 8'hFF
// Cycle 256: r_count = 8'h00  ← rollover!
My thinking: Three elements: the reg declaration (storage), the posedge sensitivity (trigger), the r_count + 1 feedback (increment). The rollover is free — 8-bit arithmetic wraps at 256 with no extra logic. That's why N-bit counters cycle through 2N values.

🤝 We Do — Clock Division: 25 MHz → 1 Hz

// 25 MHz → 1 Hz blink: toggle every 12,500,000 cycles
// (1 Hz period = 1s = 2 half-periods of 500 ms each)
// log₂(12500000) ≈ 24 bits

reg [23:0] r_counter;
reg        r_led;

always @(posedge i_clk) begin
    if (i_reset) begin
        r_counter <= 0;
        r_led     <= 1'b0;
    end else if (r_counter == 24'd12_499_999) begin
        r_counter <= 0;
        r_led     <= ~r_led;    // toggle
    end else begin
        r_counter <= r_counter + 1'b1;
    end
end

assign o_led1 = r_led;
The math: 1 Hz means LED spends 0.5 s ON and 0.5 s OFF. In 0.5 s, a 25 MHz clock ticks 12,500,000 times. So toggle every 12.5M cycles. 24 bits hold values up to 16.7M — enough.

🧪 You Do — Custom Frequency

You want a 4 Hz blink. What's the target count?

Answer: 4 Hz = 0.125 s half-period. 25,000,000 × 0.125 = 3,125,000. Toggle every 3,125,000 cycles → count to 24'd3_124_999. 22 bits would also suffice (2^22 = 4,194,304 > 3,125,000).
Follow-up: For any frequency F, target = (25_000_000 / (2*F)) - 1. Parameterize with a parameter! (Preview of Day 8.)
▶ LIVE DEMO

LED Blinker: Simulate → Flash → Behold

~6 minutes — full flow

▸ COMMANDS

cd labs/week1_day04/ex4_led_blinker/
make sim      # test the FSM logic
make wave     # sanity check
make prog     # yosys → nextpnr → iceprog
# LED D1 now blinks at 1 Hz. Look at the board.

▸ EXPECTED OUTPUT

PASS: counter increments
PASS: counter rolls over
PASS: LED toggles @ target
=== 8 passed, 0 failed ===

# make prog:
Yosys 0.36 → synth_ice40
nextpnr-ice40 → hx1k
FPGA programmed OK.

▸ ON THE BOARD

After make prog, LED D1 on the Go Board blinks steadily at 1 Hz — one second on, one second off. You're seeing a 25 MHz oscillator divided down 25 million times, in real time, from Verilog you wrote.

🔧 What Did the Tool Build?

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

=== led_blinker ===
   Number of wires:                 41
   Number of cells:                 49
     SB_CARRY                       23    ← adder carry chain for +1
     SB_DFF                          0
     SB_DFFESR                      25    ← 24 counter flops + 1 LED flop
     SB_LUT4                         1    ← the terminal-count comparator
What you're looking at: 25 flops (24 counter bits + 1 LED), 23 carry cells for the +1 adder chain, 1 LUT for the == 12_499_999 comparator. Total: 2% of the iCE40 HX1K. Your first real design fits comfortably.
Scaling insight: A 32-bit counter would be 32 flops + 31 carry = trivially small. The iCE40 HX1K can host dozens of independent counters simultaneously — which is exactly what a multi-peripheral SoC does.

Quick Trick: Counter Bit Taps

Counter BitApprox. Frequency (25 MHz)Use Case
r_counter[23]~1.5 HzVisible blink
r_counter[22]~3 HzFast blink
r_counter[20]~12 HzActivity indicator
r_counter[16]~190 HzDisplay refresh
r_counter[10]~12 kHzAudio tone
Quick trick: Use MSBs of a free-running counter for approximate division. Bit [N] of a binary counter toggles at F_clk / 2^(N+1). No target-value logic needed — just wire r_counter[23] to the LED.
Caveat: Bit-tap division gives power-of-2 divisions only. For exact frequencies (UART baud rates, audio sample rates), you need a terminal-count comparator. Use bit-taps for LEDs; use targets for protocols.

🤖 Check the Machine

Ask AI: “Design a clock divider to produce a 9600 Hz output from a 25 MHz input. Write parameterized Verilog.”

TASK

Ask AI for a 9600 Hz divider (UART-ish frequency).

BEFORE

Predict: target = 25e6/(2·9600) - 1 ≈ 1301. Counter 11 bits minimum.

AFTER

Good AI parameterizes. Weak AI hardcodes the target. Watch for sized literals.

TAKEAWAY

This exact module appears in Day 11 (UART). AI can be a starting point — verify width and parameterization.

Key Takeaways

 Counters are the fundamental sequential building block.

 N-bit counter rolls over every 2N cycles.

 Clock divider: count to target, toggle, reset.

 Quick trick: counter MSBs for approximate clock division.

Counter + comparator + toggle = any frequency you want. The pattern is yours now.

Pre-Class Self-Check

Q1: What happens if you use = instead of <= in a sequential always @(posedge clk) block with two registers?

Both registers get the same value in one cycle because blocking assignment updates immediately. The pipeline behavior is destroyed.

Q2: Write a D flip-flop with synchronous reset from memory.

always @(posedge i_clk) if (i_reset) r_q <= 1'b0; else r_q <= r_d;

Pre-Class Self-Check (cont.)

Q3: Is a missing else in always @(posedge clk) a latch?

No! Flip-flops inherently hold their value. Latch inference only occurs in always @(*).

Q4: You want ~2 Hz blink from 25 MHz. What do you count to?

2 Hz = toggle every 0.25s = 6,250,000 cycles per half-period. Count to 6,249,999, then toggle and reset.

🎉 End of Week 1

Next Week: Patterns & Verification

Day 5-8 · Counters in depth, testbenches, FSMs, hierarchy

▸ WHAT YOU CAN DO NOW

You have the full vocabulary of single-module RTL: modules, data types, operators, combinational blocks (with latch awareness), sequential blocks (with proper nonblocking), flip-flops with reset and enable, and counters with clock division. Week 2 builds on this with: counter variations & shift registers, systematic testbenches, finite state machines, and module hierarchy. By Day 8 you'll be composing multi-module designs.