Video 4 of 4 · ~14 minutes
Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF
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.
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.
“I'll write for (i=0; i<N; i++) count++; and it will count. The clock makes it loop.”
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.
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!
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.
// 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;
You want a 4 Hz blink. What's the target count?
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).
F, target = (25_000_000 / (2*F)) - 1. Parameterize with a parameter! (Preview of Day 8.)
~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.
$ 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
== 12_499_999 comparator. Total: 2% of the iCE40 HX1K. Your first real design fits comfortably.
| Counter Bit | Approx. Frequency (25 MHz) | Use Case |
|---|---|---|
r_counter[23] | ~1.5 Hz | Visible blink |
r_counter[22] | ~3 Hz | Fast blink |
r_counter[20] | ~12 Hz | Activity indicator |
r_counter[16] | ~190 Hz | Display refresh |
r_counter[10] | ~12 kHz | Audio tone |
[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.
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.
① 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.
Q1: What happens if you use = instead of <= in a sequential always @(posedge clk) block with two registers?
Q2: Write a D flip-flop with synchronous reset from memory.
Q3: Is a missing else in always @(posedge clk) a latch?
always @(*).Q4: You want ~2 Hz blink from 25 MHz. What do you count to?
🎉 End of Week 1
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.