RTL Design Patterns · Chapter 3 · Counters, Timers & Pulse Generators
Clock Dividers & Timers
A board has one fast oscillator, but the design needs many slower time bases: an LED blink, a baud tick, a periodic interrupt, a watchdog. The tempting move, and one of the most damaging mistakes in RTL, is to divide the clock by counting and wire the divided output as a clock into the slow logic. It works in simulation and fails in silicon, because a flip-flop output used as a clock is a new, unanalyzed clock domain with skew and no synchronizer. The right pattern is different: in RTL you do not make new clocks, you make clock-enables. A divide-by-N is a mod-N counter that pulses a one-cycle tick every N cycles, and the slow logic runs on the same clock but advances only when tick is high. This lesson builds clock-enable dividers, the toggle-flop divider, and one-shot and periodic timers, verified in SystemVerilog, Verilog, and VHDL.
Intermediate16 min readRTL Design PatternsClock DividerClock EnableTimerWatchdogClock Domain
Chapter 3 · Section 3.5 · Counters, Timers & Pulse Generators
1. The Engineering Problem
Your board has exactly one crystal. It arrives as a single clock — say 100 MHz — and every flop in your design toggles off it. But nothing your product actually does happens at 100 MHz. You need to blink a status LED at 1 Hz. You need a serial port to sample at 115200 baud. You need a periodic interrupt every millisecond so software can schedule work. You need a watchdog that resets the chip if firmware goes quiet for 30 seconds. Every one of these is a slower time base derived from the one fast clock you were given, and every design you will ever build has a fistful of them.
There are really two shapes of need here. The first is periodic events at a slower rate: a 1 Hz blink is a divide-by-100,000,000; a 115200-baud tick is a divide by roughly 868; a 1 ms interrupt on a 100 MHz clock is a divide-by-100,000. The second is elapsed-time measurement: a timeout that fires 500 µs after it is armed, a watchdog that must be fed every N cycles or it bites, a one-shot delay that pulses once and stops. The first is a frequency problem, the second is a duration problem, and — the point of this page — they are the same circuit pointed in two directions: a counter that reaches a programmed value and does something on the cycle it gets there.
The trap is in how you get from "I need something slower" to hardware. The instinct that ruins designs is to take the phrase "divide the clock" literally: count the fast clock, and when the count reaches its terminal value toggle a flip-flop and use that flip-flop's output as the clock for the slow logic. In an RTL simulator this looks perfect — the divided signal ticks at the right rate, the slow flops update on its edges, the waveform is beautiful. In silicon it is a latent bug: that divided flop output is now a clock with skew relative to the source clock, the boundary between the two is an unanalyzed clock-domain crossing, and static timing analysis never even sees the path because it does not know that net is a clock. It passes simulation, passes STA, and fails intermittently on hardware — the worst possible failure profile.
// One oscillator feeds every flop:
input wire clk; // 100 MHz — the ONLY clock
// The system needs slower time bases derived from it:
// 1 Hz LED blink => divide by 100_000_000
// 115200 baud tick => divide by ~868
// 1 ms periodic tick => divide by 100_000
// 30 s watchdog timeout => count 3_000_000_000 cycles
// a 500 us one-shot => count 50_000 cycles, fire once
// THE DAMAGING INSTINCT — divide, then use the divided flop AS a clock:
// reg clk_div;
// always @(posedge clk) if (count == N-1) clk_div <= ~clk_div;
// always @(posedge clk_div) slow_reg <= ...; // <-- NEW clock domain, skew, unanalyzed
// THE PATTERN THIS PAGE BUILDS — one clock, generate an ENABLE (tick):
// the slow logic stays on clk and advances only on the cycles tick==1.2. Mental Model
3. Pattern Anatomy
The family has three members — the clock-enable divider, the toggle-flop 50%-duty divider, and the loadable timer — plus the anti-pattern that motivates the whole approach. Each is a small structure over one counter.
The clock-enable divider (mod-N → one-cycle tick). This is the 3.2 mod-N counter with its wrap pulse reinterpreted as a divided enable. A WIDTH-bit count register advances every source-clock cycle; an equality comparator fires when count == N-1; on that cycle tick goes high for exactly one clock and the counter wraps to 0. The tick is the deliverable: a clean, registered, one-cycle strobe at f_clk / N. Downstream slow logic consumes it as a clock-enable — a register that updates only when tick is high (the 2.2 pattern). Nothing here is a new clock; tick is data that happens to mean "advance now."
The /2^k toggle-flop divider (50% duty). Sometimes you genuinely want a symmetric square wave at a divided rate — a 50%-duty divided clock signal to drive off-chip or to feed a clock-management cell — not a one-cycle strobe. The classic structure is a toggle flip-flop: a flop whose output inverts every time it is enabled. Chain the enable through a divider and the output is a divide-by-2N square wave with a 50% duty cycle. A single toggle flop clocked every cycle is a divide-by-2 with perfect 50% duty; k of them in series give /2^k. The crucial nuance: an even integer divide with 50% duty is easy (toggle at the half-period), but an odd integer divide with a true 50% duty cannot be made from a single edge — it requires using both the rising and falling edges of the source clock (two toggle structures, one per edge, combined), because half of an odd number of cycles is not a whole cycle. Any time you see "divide by an odd number, exactly 50% duty," expect a both-edges design.
The timer (loadable down-counter, one-shot vs periodic). A timer is a counter that measures a duration. Its structure: a loadable count register (2.2's load semantics — write a start value with a load strobe), a decrement each cycle (or each divider tick), and a comparator that detects the count reaching zero and raises timeout for one cycle. Two modes ride on top of that atom, and they differ only in what happens at zero. In one-shot mode the timer fires timeout once and then stops (holds at zero, disarmed) until it is reloaded — a single delayed pulse. In periodic (auto-reload) mode, on the cycle it hits zero it reloads the period register back into the count and keeps running, so timeout becomes a recurring strobe at a fixed interval — this is exactly how a periodic-interrupt timer or a baud generator's coarse tick is built. A watchdog is a periodic timer whose timeout triggers a reset unless software reloads it (feeds it) before it expires.
The anti-pattern — a divided flop used AS a clock. The structure to recognize and reject: an always @(posedge clk_div) block, where clk_div is a counter bit or a divided toggle-flop output generated in RTL. This introduces a second clock domain that (1) has skew — clk_div arrives through logic and routing, not the balanced clock tree, so its edge is uncertain relative to clk; (2) is an unhandled CDC — signals passing from the clk domain to the clk_div domain cross clocks with no synchronizer, risking metastability; and (3) is untimed — STA does not know clk_div is a clock, so it never analyzes setup/hold on the crossing, and the design reports clean timing while harbouring a real violation. It simulates perfectly because an RTL simulator has zero skew and infinite setup margin. It is the definition of a bug that hides until silicon.
The clock-divider / timer family — one counter, generating enables (not clocks)
data flowTwo closing notes on hardware reality. First, the divided tick is registered and glitch-free because it comes from a comparator on a clocked counter, sampled synchronously — unlike a clock derived by combinational division, which can glitch. Second, when you do need a real second clock frequency (not just a slower enable), that is a physical-design job: a PLL or an integrated clock-gating / clock-divider cell generates it on the clock tree, and any signal crossing between the two frequencies goes through a proper synchronizer. RTL's job is to generate enables and to hand the frequency requirement to the clocking infrastructure — not to fabricate a clock out of a flip-flop.
4. Real RTL Implementation
This is the core of the page. We build three patterns — (a) a clock-enable divider (mod-N → one-cycle tick at f/N, N-generic — the correct divider), (b) a timer (loadable down-counter with one-shot and periodic auto-reload modes and a timeout flag), and (c) the divided-flop-as-clock anti-pattern (buggy) versus the clock-enable fix — and each is shown in SystemVerilog, Verilog, and VHDL with a synthesizable RTL block and a clocked, self-checking testbench that generates a clock and asserts the tick period is exactly N and the timeout timing is exact. The idea is identical across the three languages; seeing them side by side is what makes the pattern language-independent.
One discipline runs through every RTL block: there is exactly one clock, clk, and it is the sensitivity of every always_ff / always @(posedge clk) / rising_edge(clk) process. Everything "slow" is expressed as a clock-enable (a signal that gates whether a register updates), never as a second clock edge. Reset is synchronous, active-high (rst), and <= (non-blocking) is used for all sequential updates — the same conventions inherited from 3.1 and 2.2.
4a. The clock-enable divider — mod-N → one-cycle tick, and slow logic gated by it
Start with the correct divider: a mod-N counter (3.2) whose wrap pulse is the divided tick, plus a tiny piece of downstream logic — a slow LED register — that advances only when tick is high. The whole point is that led toggles at f_clk / (2*N) while living entirely in the clk domain: no second clock, no skew, no CDC.
module clkdiv_en #(
parameter int N = 100, // divide ratio (f_clk / N tick rate)
localparam int WIDTH = (N > 1) ? $clog2(N) : 1 // holds 0 .. N-1
)(
input logic clk, // the ONE clock — nothing else is clocked
input logic rst, // synchronous, active-high
output logic tick, // 1-cycle clock-ENABLE, once every N cycles
output logic led // "slow" output, toggles at f_clk/(2N)
);
logic [WIDTH-1:0] count;
// The mod-N counter from 3.2. tick is its wrap pulse — a clock-ENABLE, not a clock.
always_ff @(posedge clk) begin
if (rst) count <= '0;
else if (count == WIDTH'(N-1)) count <= '0; // reached terminal N-1 → wrap
else count <= count + 1'b1;
end
assign tick = (count == WIDTH'(N-1)); // high for exactly 1 cycle per N
// The "slow" logic is a 2.2 clock-ENABLED register: SAME clk, advances only on tick.
// led therefore changes once per N ticks-worth of cycles — no second clock exists.
always_ff @(posedge clk) begin
if (rst) led <= 1'b0;
else if (tick) led <= ~led; // enable = tick; otherwise HOLD
end
endmoduleThe self-checking testbench proves the two properties that make this a correct divider: (1) the tick-to-tick spacing is exactly N cycles (the divide ratio), and (2) led changes only on a tick and never on a non-tick cycle — the evidence that it lives in the single clk domain rather than a derived one.
module clkdiv_en_tb;
localparam int N = 5; // non-power-of-two on purpose
logic clk = 0, rst, tick, led;
int errors = 0, cyc = 0, last = -1, gap;
logic led_prev;
clkdiv_en #(.N(N)) dut (.clk(clk), .rst(rst), .tick(tick), .led(led));
always #5 clk = ~clk; // 100 MHz source clock
initial begin
rst = 1; @(posedge clk); #1; led_prev = led;
rst = 0;
for (cyc = 0; cyc < 6*N + 3; cyc++) begin
@(posedge clk); #1;
// (1) tick-to-tick period must be EXACTLY N (the divide ratio):
if (tick) begin
if (last >= 0)
assert ((cyc - last) == N)
else begin $error("period=%0d exp=%0d", cyc-last, N); errors++; end
last = cyc;
end
// (2) led may change ONLY on a tick cycle — proof it is enable-gated, not re-clocked:
if (led !== led_prev)
assert (tick)
else begin $error("led moved on a NON-tick cycle (cyc=%0d)", cyc); errors++; end
led_prev = led;
end
if (errors == 0) $display("PASS: tick period == N and slow logic advances only on tick");
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmoduleThe Verilog form is identical in intent — reg/wire typing, always @(posedge clk), the same mod-N tick and the same enable-gated slow register. The self-check uses if (…) $display("FAIL") instead of assert.
module clkdiv_en #(
parameter N = 100,
parameter WIDTH = 7 // must satisfy 2**WIDTH >= N
)(
input wire clk,
input wire rst, // synchronous, active-high
output wire tick,
output reg led
);
reg [WIDTH-1:0] count;
always @(posedge clk) begin
if (rst) count <= {WIDTH{1'b0}};
else if (count == N-1) count <= {WIDTH{1'b0}}; // wrap on terminal N-1
else count <= count + 1'b1;
end
assign tick = (count == N-1); // 1-cycle clock-ENABLE, period N
// Slow logic: same clk, enable = tick, otherwise hold. No second clock domain.
always @(posedge clk) begin
if (rst) led <= 1'b0;
else if (tick) led <= ~led;
end
endmodule module clkdiv_en_tb;
parameter N = 5;
parameter WIDTH = 3;
reg clk, rst;
wire tick, led;
integer errors, cyc, last, gap;
reg led_prev;
clkdiv_en #(.N(N), .WIDTH(WIDTH)) dut (.clk(clk), .rst(rst), .tick(tick), .led(led));
initial clk = 0;
always #5 clk = ~clk;
initial begin
errors = 0; last = -1;
rst = 1; @(posedge clk); #1; led_prev = led;
rst = 0;
for (cyc = 0; cyc < 6*N + 3; cyc = cyc + 1) begin
@(posedge clk); #1;
// period between ticks must be EXACTLY N:
if (tick) begin
if (last >= 0) begin
gap = cyc - last;
if (gap !== N) begin
$display("FAIL: period=%0d exp=%0d", gap, N);
errors = errors + 1;
end
end
last = cyc;
end
// led must change ONLY on a tick cycle:
if (led !== led_prev && tick !== 1'b1) begin
$display("FAIL: led moved on a non-tick cycle (cyc=%0d)", cyc);
errors = errors + 1;
end
led_prev = led;
end
if (errors == 0) $display("PASS: period == N and slow logic advances only on tick");
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmoduleIn VHDL the divide ratio is a generic, the register updates live in a clocked process guarded by rising_edge(clk), and arithmetic goes through numeric_std (unsigned). The tick is en-style data; the slow register's elsif tick = '1' is the clock-enable.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity clkdiv_en is
generic (
N : positive := 100; -- divide ratio
WIDTH : positive := 7 -- must hold N-1
);
port (
clk : in std_logic; -- the ONLY clock
rst : in std_logic; -- synchronous, active-high
tick : out std_logic; -- 1-cycle clock-ENABLE
led : out std_logic
);
end entity;
architecture rtl of clkdiv_en is
signal cnt : unsigned(WIDTH-1 downto 0) := (others => '0');
signal tick_i : std_logic;
signal led_r : std_logic := '0';
begin
tick_i <= '1' when cnt = to_unsigned(N-1, WIDTH) else '0'; -- terminal N-1
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
cnt <= (others => '0');
elsif tick_i = '1' then
cnt <= (others => '0'); -- wrap to 0
else
cnt <= cnt + 1; -- advance
end if;
-- Slow logic: SAME clk, enable = tick_i, otherwise hold.
if rst = '1' then
led_r <= '0';
elsif tick_i = '1' then
led_r <= not led_r;
end if;
end if;
end process;
tick <= tick_i;
led <= led_r;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity clkdiv_en_tb is
end entity;
architecture sim of clkdiv_en_tb is
constant N : positive := 5;
constant WIDTH : positive := 3;
signal clk : std_logic := '0';
signal rst : std_logic := '1';
signal tick : std_logic;
signal led : std_logic;
begin
dut : entity work.clkdiv_en
generic map (N => N, WIDTH => WIDTH)
port map (clk => clk, rst => rst, tick => tick, led => led);
clk <= not clk after 5 ns;
stim : process
variable last : integer := -1;
variable cyc : integer := 0;
variable led_prev : std_logic;
begin
wait until rising_edge(clk); wait for 1 ns;
led_prev := led;
rst <= '0';
for c in 0 to 6*N + 2 loop
wait until rising_edge(clk); wait for 1 ns;
-- period between ticks must be EXACTLY N:
if tick = '1' then
if last >= 0 then
assert (cyc - last) = N
report "tick period /= N" severity error;
end if;
last := cyc;
end if;
-- led must change ONLY on a tick cycle:
if led /= led_prev then
assert tick = '1'
report "led moved on a non-tick cycle" severity error;
end if;
led_prev := led;
cyc := cyc + 1;
end loop;
report "clkdiv_en self-check complete (period = N, enable-gated)" severity note;
wait;
end process;
end architecture;4b. The timer — loadable down-counter, one-shot and periodic auto-reload
Now the duration side: a timer that loads a period, decrements each cycle, and raises timeout for one cycle when it reaches zero. A mode bit selects behaviour at zero — one-shot (fire once, then hold disarmed) or periodic (auto-reload the period and keep going). load writes a new period and (re)arms the timer; one_shot picks the mode.
module timer #(
parameter int WIDTH = 16 // period width (max count)
)(
input logic clk,
input logic rst, // synchronous, active-high
input logic load, // 1-cycle: capture period & arm
input logic [WIDTH-1:0] period, // cycles-1 to count (terminal at 0)
input logic one_shot, // 1 = fire once; 0 = periodic (auto-reload)
output logic timeout // 1-cycle strobe when count hits 0
);
logic [WIDTH-1:0] count;
logic running; // armed & counting
// timeout fires on the cycle the running count is at zero.
assign timeout = running & (count == '0);
always_ff @(posedge clk) begin
if (rst) begin
count <= '0;
running <= 1'b0;
end else if (load) begin
count <= period; // arm with the programmed period
running <= 1'b1;
end else if (running) begin
if (count == '0) begin
// At terminal: one-shot stops; periodic reloads and continues.
if (one_shot) running <= 1'b0; // fire once, then disarm
else count <= period; // auto-reload → recurring timeout
end else begin
count <= count - 1'b1; // decrement toward zero
end
end
end
endmoduleThe self-checking testbench proves the timeout timing in both modes: a one-shot armed with period = P must raise timeout exactly P+1 cycles after load (P decrements from P down through 0) and then never again; a periodic timer must raise timeout every P+1 cycles, repeatedly.
module timer_tb;
localparam int WIDTH = 16;
logic clk = 0, rst, load, one_shot;
logic [WIDTH-1:0] period;
logic timeout;
int errors = 0, cyc, fires, last;
timer #(.WIDTH(WIDTH)) dut
(.clk(clk), .rst(rst), .load(load), .period(period),
.one_shot(one_shot), .timeout(timeout));
always #5 clk = ~clk;
initial begin
rst = 1; load = 0; one_shot = 1; period = 0; @(posedge clk); #1;
rst = 0;
// --- ONE-SHOT: period=4 → timeout exactly ONCE, at 5 cycles after load ---
period = 4; one_shot = 1;
@(posedge clk); load = 1; @(posedge clk); #1; load = 0;
fires = 0; last = -1;
for (cyc = 0; cyc < 20; cyc++) begin
if (timeout) begin fires++; last = cyc; end
@(posedge clk); #1;
end
assert (fires == 1) else begin $error("one-shot fired %0d times, exp 1", fires); errors++; end
assert (last == 4) else begin $error("one-shot fired at cyc=%0d, exp 4", last); errors++; end
// --- PERIODIC: period=3 → timeout every 4 cycles, repeatedly ---
period = 3; one_shot = 0;
@(posedge clk); load = 1; @(posedge clk); #1; load = 0;
fires = 0; last = -1;
for (cyc = 0; cyc < 20; cyc++) begin
if (timeout) begin
if (last >= 0)
assert ((cyc - last) == 4)
else begin $error("periodic interval=%0d exp=4", cyc-last); errors++; end
last = cyc; fires++;
end
@(posedge clk); #1;
end
assert (fires >= 3) else begin $error("periodic fired only %0d times", fires); errors++; end
if (errors == 0) $display("PASS: one-shot fires once; periodic repeats at the right interval");
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmoduleThe Verilog timer is the same loadable down-counter with reg/wire typing; the testbench counts timeout pulses and checks the interval with if (…) $display("FAIL").
module timer #(
parameter WIDTH = 16
)(
input wire clk,
input wire rst,
input wire load, // capture period & arm
input wire [WIDTH-1:0] period,
input wire one_shot, // 1 = fire once, 0 = periodic
output wire timeout
);
reg [WIDTH-1:0] count;
reg running;
assign timeout = running & (count == {WIDTH{1'b0}});
always @(posedge clk) begin
if (rst) begin
count <= {WIDTH{1'b0}};
running <= 1'b0;
end else if (load) begin
count <= period;
running <= 1'b1;
end else if (running) begin
if (count == {WIDTH{1'b0}}) begin
if (one_shot) running <= 1'b0; // fire once, disarm
else count <= period; // auto-reload
end else begin
count <= count - 1'b1;
end
end
end
endmodule module timer_tb;
parameter WIDTH = 16;
reg clk, rst, load, one_shot;
reg [WIDTH-1:0] period;
wire timeout;
integer errors, cyc, fires, last;
timer #(.WIDTH(WIDTH)) dut
(.clk(clk), .rst(rst), .load(load), .period(period),
.one_shot(one_shot), .timeout(timeout));
initial clk = 0;
always #5 clk = ~clk;
initial begin
errors = 0;
rst = 1; load = 0; one_shot = 1; period = 0; @(posedge clk); #1;
rst = 0;
// ONE-SHOT: period=4 → fire once, at cyc 4 after load release
period = 4; one_shot = 1;
@(posedge clk); load = 1; @(posedge clk); #1; load = 0;
fires = 0; last = -1;
for (cyc = 0; cyc < 20; cyc = cyc + 1) begin
if (timeout) begin fires = fires + 1; last = cyc; end
@(posedge clk); #1;
end
if (fires !== 1) begin $display("FAIL: one-shot fired %0d times, exp 1", fires); errors = errors + 1; end
if (last !== 4) begin $display("FAIL: one-shot fired at %0d, exp 4", last); errors = errors + 1; end
// PERIODIC: period=3 → timeout every 4 cycles
period = 3; one_shot = 0;
@(posedge clk); load = 1; @(posedge clk); #1; load = 0;
fires = 0; last = -1;
for (cyc = 0; cyc < 20; cyc = cyc + 1) begin
if (timeout) begin
if (last >= 0 && (cyc - last) !== 4) begin
$display("FAIL: periodic interval=%0d exp=4", cyc-last);
errors = errors + 1;
end
last = cyc; fires = fires + 1;
end
@(posedge clk); #1;
end
if (fires < 3) begin $display("FAIL: periodic fired only %0d times", fires); errors = errors + 1; end
if (errors == 0) $display("PASS: one-shot fires once; periodic repeats at interval");
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmoduleIn VHDL the period is a std_logic_vector port converted to unsigned; load arms, the process decrements, and the one_shot boolean picks stop-vs-reload at zero. The timeout is a combinational decode of running and count = 0.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity timer is
generic ( WIDTH : positive := 16 );
port (
clk : in std_logic;
rst : in std_logic; -- synchronous, active-high
load : in std_logic; -- capture period & arm
period : in std_logic_vector(WIDTH-1 downto 0);
one_shot : in std_logic; -- '1' fire once, '0' periodic
timeout : out std_logic
);
end entity;
architecture rtl of timer is
signal cnt : unsigned(WIDTH-1 downto 0) := (others => '0');
signal running : std_logic := '0';
begin
timeout <= '1' when (running = '1' and cnt = to_unsigned(0, WIDTH)) else '0';
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
cnt <= (others => '0');
running <= '0';
elsif load = '1' then
cnt <= unsigned(period); -- arm with the programmed period
running <= '1';
elsif running = '1' then
if cnt = to_unsigned(0, WIDTH) then
if one_shot = '1' then
running <= '0'; -- fire once, disarm
else
cnt <= unsigned(period); -- auto-reload → periodic
end if;
else
cnt <= cnt - 1; -- decrement toward zero
end if;
end if;
end if;
end process;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity timer_tb is
end entity;
architecture sim of timer_tb is
constant WIDTH : positive := 16;
signal clk : std_logic := '0';
signal rst : std_logic := '1';
signal load : std_logic := '0';
signal period : std_logic_vector(WIDTH-1 downto 0) := (others => '0');
signal one_shot : std_logic := '1';
signal timeout : std_logic;
begin
dut : entity work.timer
generic map (WIDTH => WIDTH)
port map (clk => clk, rst => rst, load => load, period => period,
one_shot => one_shot, timeout => timeout);
clk <= not clk after 5 ns;
stim : process
variable fires : integer;
variable last : integer;
variable cyc : integer;
begin
wait until rising_edge(clk); wait for 1 ns;
rst <= '0';
-- ONE-SHOT: period=4 → fire once, at cyc 4 after load
period <= std_logic_vector(to_unsigned(4, WIDTH));
one_shot <= '1';
wait until rising_edge(clk); load <= '1';
wait until rising_edge(clk); wait for 1 ns; load <= '0';
fires := 0; last := -1; cyc := 0;
for i in 0 to 19 loop
if timeout = '1' then fires := fires + 1; last := cyc; end if;
wait until rising_edge(clk); wait for 1 ns;
cyc := cyc + 1;
end loop;
assert fires = 1 report "one-shot did not fire exactly once" severity error;
assert last = 4 report "one-shot fired at the wrong cycle" severity error;
-- PERIODIC: period=3 → timeout every 4 cycles
period <= std_logic_vector(to_unsigned(3, WIDTH));
one_shot <= '0';
wait until rising_edge(clk); load <= '1';
wait until rising_edge(clk); wait for 1 ns; load <= '0';
fires := 0; last := -1; cyc := 0;
for i in 0 to 19 loop
if timeout = '1' then
if last >= 0 then
assert (cyc - last) = 4
report "periodic interval /= 4" severity error;
end if;
last := cyc; fires := fires + 1;
end if;
wait until rising_edge(clk); wait for 1 ns;
cyc := cyc + 1;
end loop;
assert fires >= 3 report "periodic did not repeat" severity error;
report "timer self-check complete (one-shot + periodic)" severity note;
wait;
end process;
end architecture;4c. The divided-flop-as-clock anti-pattern — buggy vs fixed, in all three HDLs
Pattern (c) is the signature failure of this page, shown as buggy vs fixed RTL in each language and dramatized in §7. The bug is the same everywhere: a counter/toggle-flop output (clk_div) is generated in RTL and then used as a clock — always @(posedge clk_div) — for the slow logic. The fix keeps the single source clock and turns the divided signal into a clock-enable instead. Both "look" like they divide; only the fix is a single, analyzable clock domain.
// BUGGY: clk_div is a divided flip-flop output used AS A CLOCK for slow_reg.
// → clk_div is a NEW clock domain: skew vs clk, an unhandled CDC on data_in,
// and STA never analyzes the clk→clk_div path. Sims perfectly; fails in silicon.
module divider_bad #(
parameter int N = 4
)(
input logic clk, rst, data_in,
output logic slow_reg
);
logic [$clog2(N)-1:0] count;
logic clk_div;
always_ff @(posedge clk) begin // count on the real clock
if (rst) {count, clk_div} <= '0;
else if (count == N-1) begin count <= '0; clk_div <= ~clk_div; end
else count <= count + 1'b1;
end
// THE BUG: a flop clocked by a DERIVED clock — new domain, skew, CDC, untimed.
always_ff @(posedge clk_div) begin
if (rst) slow_reg <= 1'b0;
else slow_reg <= data_in; // data_in crosses clk → clk_div unsynchronized
end
endmodule
// FIXED: ONE clock. The divided signal is a clock-ENABLE (tick); slow_reg is a
// clock-enabled register on clk. One domain, zero skew, every path analyzed.
module divider_good #(
parameter int N = 4
)(
input logic clk, rst, data_in,
output logic slow_reg
);
logic [$clog2(N)-1:0] count;
logic tick;
always_ff @(posedge clk) begin
if (rst) count <= '0;
else if (count == N-1) count <= '0;
else count <= count + 1'b1;
end
assign tick = (count == N-1); // clock-ENABLE, not a clock
always_ff @(posedge clk) begin // SAME clk as the counter
if (rst) slow_reg <= 1'b0;
else if (tick) slow_reg <= data_in; // advance only when enabled
end
endmodule module clkdiv_good_tb;
localparam int N = 4;
logic clk = 0, rst, data_in, slow_reg;
int errors = 0, cyc = 0, changes = 0;
logic q_prev;
// Bind to divider_good (single clock, safe). Point at divider_bad to observe
// the derived-clock domain: in a zero-skew RTL sim it "works" — the hazard is
// silicon skew/CDC/STA, which a functional TB cannot see. That invisibility is
// exactly why the rule is structural: never clock on a divided flop.
divider_good #(.N(N)) dut (.clk(clk), .rst(rst), .data_in(data_in), .slow_reg(slow_reg));
always #5 clk = ~clk;
// Reference divider: a tick every N cycles, on the SAME clock as the DUT.
logic [$clog2(N)-1:0] cnt = 0;
logic ref_tick;
always @(posedge clk) begin
if (rst) cnt <= 0;
else if (cnt == N-1) cnt <= 0;
else cnt <= cnt + 1;
end
assign ref_tick = (cnt == N-1);
initial begin
rst = 1; data_in = 0; @(posedge clk); #1; q_prev = slow_reg;
rst = 0;
for (cyc = 0; cyc < 8*N; cyc++) begin
data_in = $urandom; // change data every cycle
@(posedge clk); #1;
// slow_reg (the correct, enable-gated register) may change ONLY on a tick:
if (slow_reg !== q_prev) begin
changes++;
assert (ref_tick)
else begin $error("slow_reg changed on a NON-tick cycle (cyc=%0d)", cyc); errors++; end
end
q_prev = slow_reg;
end
if (errors == 0 && changes > 0)
$display("PASS: single-clock divider advances only on the enable tick");
else $display("FAIL: %0d mismatches (%0d changes)", errors, changes);
$finish;
end
endmoduleThe Verilog buggy/fixed pair is the same story with reg/wire: the bad module has an always @(posedge clk_div) on a derived clock; the fixed module keeps always @(posedge clk) and gates the update with tick.
// BUGGY: slow_reg is clocked by clk_div, a divided flop output → derived clock
// domain: skew, unhandled CDC on data_in, STA-invisible. Sims fine, fails in HW.
module divider_bad #(
parameter N = 4,
parameter W = 2 // clog2(N)
)(
input wire clk, rst, data_in,
output reg slow_reg
);
reg [W-1:0] count;
reg clk_div;
always @(posedge clk) begin
if (rst) begin count <= 0; clk_div <= 1'b0; end
else if (count == N-1) begin count <= 0; clk_div <= ~clk_div; end
else count <= count + 1'b1;
end
always @(posedge clk_div) begin // THE BUG: derived-clock flop
if (rst) slow_reg <= 1'b0;
else slow_reg <= data_in; // unsynchronized crossing
end
endmodule
// FIXED: single clock; the divided signal is an ENABLE (tick), slow_reg on clk.
module divider_good #(
parameter N = 4,
parameter W = 2
)(
input wire clk, rst, data_in,
output reg slow_reg
);
reg [W-1:0] count;
wire tick;
always @(posedge clk) begin
if (rst) count <= 0;
else if (count == N-1) count <= 0;
else count <= count + 1'b1;
end
assign tick = (count == N-1); // clock-ENABLE
always @(posedge clk) begin // SAME clk
if (rst) slow_reg <= 1'b0;
else if (tick) slow_reg <= data_in; // advance only when enabled
end
endmodule module clkdiv_good_tb;
parameter N = 4;
parameter W = 2;
reg clk, rst, data_in;
wire slow_reg;
integer errors, cyc, changes;
reg q_prev;
// Bind to divider_good (single clock). Point at divider_bad to see that a
// zero-skew RTL sim CANNOT expose the derived-clock hazard — proof the rule
// must be structural, not verification-caught.
divider_good #(.N(N), .W(W)) dut (.clk(clk), .rst(rst), .data_in(data_in), .slow_reg(slow_reg));
initial clk = 0;
always #5 clk = ~clk;
reg [W-1:0] cnt;
wire ref_tick;
always @(posedge clk) begin
if (rst) cnt <= 0;
else if (cnt == N-1) cnt <= 0;
else cnt <= cnt + 1;
end
assign ref_tick = (cnt == N-1);
initial begin
errors = 0; changes = 0;
rst = 1; data_in = 0; @(posedge clk); #1; q_prev = slow_reg;
rst = 0;
for (cyc = 0; cyc < 8*N; cyc = cyc + 1) begin
data_in = $random;
@(posedge clk); #1;
if (slow_reg !== q_prev) begin
changes = changes + 1;
if (ref_tick !== 1'b1) begin
$display("FAIL: slow_reg changed on a non-tick cycle (cyc=%0d)", cyc);
errors = errors + 1;
end
end
q_prev = slow_reg;
end
if (errors == 0 && changes > 0) $display("PASS: single-clock divider advances only on tick");
else $display("FAIL: %0d mismatches (%0d changes)", errors, changes);
$finish;
end
endmoduleIn VHDL the identical bug is a second clocked process sensitive to rising_edge(clk_div), where clk_div is a signal the first process toggles. The fix keeps every register in one rising_edge(clk) process and gates the slow update with a tick enable.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- BUGGY: slow_reg is clocked by clk_div, a divided flop → derived clock domain:
-- skew, unhandled CDC on data_in, STA-invisible. Sims fine; fails in silicon.
entity divider_bad is
generic ( N : positive := 4; W : positive := 2 );
port ( clk, rst, data_in : in std_logic; slow_reg : out std_logic );
end entity;
architecture rtl of divider_bad is
signal cnt : unsigned(W-1 downto 0) := (others => '0');
signal clk_div : std_logic := '0';
begin
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
cnt <= (others => '0'); clk_div <= '0';
elsif cnt = to_unsigned(N-1, W) then
cnt <= (others => '0'); clk_div <= not clk_div;
else
cnt <= cnt + 1;
end if;
end if;
end process;
process (clk_div) -- THE BUG: derived-clock process
begin
if rising_edge(clk_div) then
slow_reg <= data_in; -- unsynchronized clk → clk_div crossing
end if;
end process;
end architecture;
-- FIXED: single clock; the divided signal is an ENABLE (tick), slow_reg on clk.
entity divider_good is
generic ( N : positive := 4; W : positive := 2 );
port ( clk, rst, data_in : in std_logic; slow_reg : out std_logic );
end entity;
architecture rtl of divider_good is
signal cnt : unsigned(W-1 downto 0) := (others => '0');
signal tick : std_logic;
begin
tick <= '1' when cnt = to_unsigned(N-1, W) else '0'; -- clock-ENABLE
process (clk) -- SAME clk for every register
begin
if rising_edge(clk) then
if rst = '1' then
cnt <= (others => '0'); slow_reg <= '0';
else
if tick = '1' then cnt <= (others => '0');
else cnt <= cnt + 1; end if;
if tick = '1' then slow_reg <= data_in; end if; -- advance only when enabled
end if;
end if;
end process;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity clkdiv_good_tb is
end entity;
architecture sim of clkdiv_good_tb is
constant N : positive := 4;
constant W : positive := 2;
signal clk : std_logic := '0';
signal rst : std_logic := '1';
signal data_in : std_logic := '0';
signal slow_reg : std_logic;
signal cnt : unsigned(W-1 downto 0) := (others => '0');
signal ref_tick : std_logic;
begin
-- Bind to divider_good (single clock, safe). Binding divider_bad would still
-- "pass" in a zero-skew sim — proof the derived-clock hazard is a silicon issue
-- a functional TB cannot catch, so the rule must be structural.
dut : entity work.divider_good
generic map (N => N, W => W)
port map (clk => clk, rst => rst, data_in => data_in, slow_reg => slow_reg);
clk <= not clk after 5 ns;
ref : process (clk)
begin
if rising_edge(clk) then
if rst = '1' then cnt <= (others => '0');
elsif cnt = to_unsigned(N-1, W) then cnt <= (others => '0');
else cnt <= cnt + 1; end if;
end if;
end process;
ref_tick <= '1' when cnt = to_unsigned(N-1, W) else '0';
stim : process
variable q_prev : std_logic;
variable changes : integer := 0;
variable seed1, seed2 : positive := 7;
variable r : real;
begin
wait until rising_edge(clk); wait for 1 ns; q_prev := slow_reg;
rst <= '0';
for i in 0 to 8*N - 1 loop
uniform(seed1, seed2, r);
data_in <= '1' when r > 0.5 else '0';
wait until rising_edge(clk); wait for 1 ns;
if slow_reg /= q_prev then
changes := changes + 1;
assert ref_tick = '1'
report "slow_reg changed on a non-tick cycle" severity error;
end if;
q_prev := slow_reg;
end loop;
assert changes > 0 report "slow_reg never changed" severity error;
report "single-clock divider self-check complete (advances only on tick)" severity note;
wait;
end process;
end architecture;Across all three languages the lesson is one sentence: in RTL you generate clock-enables, not clocks — never route a divided flip-flop output as a clock, because that creates an unanalyzed clock domain that simulates perfectly and fails in silicon.
5. Verification Strategy
A clock divider and a timer are sequential, so correctness is a behaviour over time and the testbench must be clocked: generate one source clock, release reset, and sample after each edge with the self-checking discipline from 3.1. Two families of invariants capture this page — a divider's rate and a timer's timing — and both hinge on the fact that everything lives in the single clk domain:
(1) The
tick(divided enable) is high for exactly one cycle every N cycles; (2) the 'slow' logic changes value ONLY on atickcycle — never in between; (3) a one-shot timer raisestimeoutexactly once, N cycles after arm; a periodic timer raises it every N cycles, forever.
- Measure the tick period (the divide ratio). Record the cycle of each
tick; when the next fires, assert the gap is exactly N — the same period measurement that guards the mod-N off-by-one (3.2). Thetickis the divided signal, so its period is the divide ratio; a period of N+1 is a frequency error. The §4a testbenches do this in all three HDLs (SVassert, Verilogif(...)$display("FAIL"), VHDLassert … severity error). - Assert the slow logic advances ONLY on
tick. This is the check that proves the design is a single clock domain rather than a derived one. Snapshot the slow output each cycle; whenever it changes, asserttickwas high on that cycle. A change on a non-tick cycle means the logic is being clocked by something other than the enable — the signature of the divided-clock bug. The §4a and §4c testbenches both include this "changes-only-on-tick" assertion. - Verify timeout timing in both modes. For a one-shot loaded with period P, assert
timeoutfires exactly once and at the expected offset afterload(P decrements P→0), then never again — count the pulses and assert the count is 1. For a periodic timer, asserttimeoutrecurs at a fixed interval (P+1 cycles) across several periods — measure the pulse-to-pulse gap just like the divider period. The §4b testbenches drive both modes and check both properties. - Corner cases: N=1, period=0, and re-arm. A divide-by-1 (
N=1) should tick every cycle (period 1); a timer withperiod=0should time out immediately (the same cycle it is armed) — verify these degenerate points explicitly, because acount == N-1or a decrement-then-compare written assumingN ≥ 2can misbehave when the terminal is already zero. Also exercise re-arm: reload a one-shot mid-run and confirm it fires again from the new period, and reload (feed) a periodic/watchdog timer before it expires and confirm it does not time out — the exact behaviour a watchdog depends on. - The anti-pattern is a review check, not a sim check. The divided-clock bug cannot be caught by a functional testbench, because an RTL simulator has zero clock skew and infinite setup margin — the buggy
posedge clk_divdesign passes simulation. Its detection lives in structural review and CDC/lint tooling: a lint rule that flags "clock generated from a flip-flop / non-primary clock," a CDC checker that flags the unsynchronized crossing, and STA (which will either miss the path or, with clock definitions, flag an unconstrained generated clock). The verification lesson is itself a lesson: some bugs are prevented by construction and tooling, not by testbenches — which is why the rule "generate enables, not clocks" is a hard structural rule. - Expected waveform. A correct divider shows the counter ramping
0 → N-1,tickpulsing high for one cycle at the top, and the slow output stepping only at those pulses — flat between them. A correct one-shot shows the down-counter ramping period→0 once, a singletimeoutpulse at zero, then a flat line; a periodic timer shows that saw-tooth repeating with atimeoutpulse at every zero-crossing. The visual signature of the divided-clock bug is subtler: in RTL sim it looks fine (that is the danger) — you only see it on hardware as intermittent, temperature/voltage-dependent glitches on the crossing.
6. Common Mistakes
Using a divided flip-flop output as a clock (always @(posedge clk_div)). The signature failure and the reason this pattern gets its own page. Counting the source clock and then routing the divided output — a counter bit, or a toggled flop — as the clock of downstream logic creates a new clock domain with three fatal properties: it has skew (it arrives through logic and routing, not the balanced clock tree), it is an unhandled CDC (data crossing into it has no synchronizer and can go metastable), and it is untimed (STA does not recognize it as a clock, so the crossing is never analyzed). It simulates perfectly — zero skew, infinite margin — and then fails intermittently in silicon, the worst failure profile there is. The fix is always the same: keep one clock and turn the divided signal into a clock-enable that gates a register on the source clock. In RTL you generate enables, not clocks.
Treating a /N tick as if it were a 50%-duty divided clock. The mod-N tick is a one-cycle strobe — high for one cycle, low for N-1 — not a symmetric square wave. If you actually need a 50%-duty divided clock signal (to drive an output pin or a clock cell), you need a toggle-flop divider, not the raw tick, and even then the result is a /2N (or /2^k) clock, not a /N one. Confusing the strobe with a clock also feeds straight into the previous bug — a one-cycle pulse used as a clock has a runt high-time and is doubly wrong.
Assuming an odd integer divide can have a 50% duty from one edge. A /2, /4, /6 (even) divide is easy to make 50%-duty by toggling at the half-period. An odd integer divide (/3, /5) at a true 50% duty is not achievable from the rising edge alone, because half of an odd number of clock cycles is not a whole cycle — you must combine logic clocked on the rising edge with logic clocked on the falling edge (a both-edges design) to place the transition at the half-cycle point. Writing a single-edge /3 and expecting exactly 50% duty gives an unbalanced (e.g. 2-high/1-low) waveform instead. If duty does not matter, prefer the clock-enable tick and avoid the issue entirely.
Forgetting to re-arm a one-shot (or to feed a watchdog). A one-shot timer fires timeout once and then holds disarmed — that is the point of "one-shot." A common bug is expecting it to keep firing, or forgetting to reload it before the next expected event, so the second event never triggers. The mirror bug is a watchdog (a periodic/reloadable timer that resets the system on timeout): if firmware forgets to feed it (reload it before it expires), it bites and resets the chip — which is either the intended safety behaviour or a spurious reset from a missed feed. Be explicit about the mode: one-shot needs a re-arm per event; periodic/watchdog needs a periodic reload to prevent the timeout.
Off-by-one in the divide ratio or the timeout period. Inherited straight from 3.2: the divider's terminal value is N-1 (compare count == N-1), not N, or the period becomes N+1 and the divided frequency is subtly wrong. Likewise a down-counting timer loaded with period = P and counting P … 0 takes P+1 cycles to time out — decide deliberately whether you load desired_cycles or desired_cycles - 1, and measure the interval in verification rather than eyeballing it. A divide ratio and a timeout are both counts of cycles, and the only proof is to count them.
7. DebugLab
The blink that worked on the bench and glitched in the field — a divided flop used as a clock
The engineering lesson: in RTL you generate clock-enables, not clocks — a divided flip-flop output used as a clock is a new, unanalyzed clock domain, and it is the most dangerous kind of bug because it passes every check you run and fails only in silicon. That is what makes it lethal: the RTL simulator has zero skew and infinite setup margin, so the buggy design is perfectly green in functional sim; STA never sees the generated clock, so timing signs off clean; and the failure surfaces only as intermittent, temperature- and voltage-dependent corruption in the field, on some boards, sometimes. Two habits make it impossible, and they are identical across SystemVerilog, Verilog, and VHDL: keep exactly one clock and express every 'slower' rate as a clock-enable that gates a register on that one clock, and hand any genuine second-frequency requirement to the clocking infrastructure — a PLL or clock-divider cell on the clock tree, with a proper synchronizer at the crossing — rather than manufacturing a clock from a flip-flop.
8. Interview Q&A
9. Exercises
Design and reasoning exercises — no syntax drills. Each rehearses the "generate enables, not clocks" and "the divider/timer is one counter" habits.
Exercise 1 — Blink an LED at 1 Hz from a 100 MHz clock, the right way
You must blink a status LED at 1 Hz (on for 0.5 s, off for 0.5 s) from a 100 MHz source clock. (a) Using the clock-enable pattern, what divide ratio N gives a tick you can use, and what does the LED register do on each tick to get a 1 Hz, 50%-duty blink? (b) What minimum counter WIDTH do you need? (c) A colleague instead builds a chain of toggle flops and routes the final divided output as the clock for the LED flop — name the three specific hazards that introduces and why none of them appear in an RTL simulation. (d) State the one-line self-checking assertion (in words) that proves the LED changes only on a tick.
Exercise 2 — Baud tick and the off-by-one
A UART on a 100 MHz clock needs a 115200-baud sample tick at 16× oversampling (the sample strobe runs at 16 × baud). (a) Compute the ideal divide ratio N = f_clk / (16 × 115200) — is it an integer? (b) Choose the nearest integer N and state the actual oversample-tick frequency and the resulting error in percent. (c) Your divider compares count == N instead of count == N-1; what period does the tick actually have, and how would a period-measuring self-check expose it where an eyeball would not? (d) Explain why this baud tick should be a clock-enable consumed by the UART's sampling FSM rather than a divided clock fed to that FSM.
Exercise 3 — One-shot vs periodic vs watchdog on paper
A single loadable down-counter can serve three roles. (a) For a one-shot loaded with period = 50 on a 100 MHz clock, how many cycles and how much wall-clock time until timeout, and what does the timer do after it fires? (b) Convert it to periodic mode: what changes at the terminal (zero) count, and what is the resulting timeout interval? (c) A watchdog is a periodic timer that resets the chip on timeout unless firmware feeds it. Describe the sequence of events if firmware feeds it every 30 ms with a 50 ms watchdog, versus what happens if a firmware hang stops the feeds — and identify which timer input the "feed" drives. (d) Why is a watchdog's timeout-to-reset path one of the few places you might want a timer to fire, rather than a bug to avoid?
Exercise 4 — The odd-divide duty-cycle trap
You need a /3 divided clock signal (not just an enable) with an exact 50% duty cycle to drive an off-chip peripheral. (a) Why can a single rising-edge design not achieve exactly 50% duty for a /3 divide — what duty cycle does a naive single-edge /3 produce, and why? (b) Sketch (in words) the both-edges structure that does achieve 50% duty, naming what the rising-edge and falling-edge parts each contribute. (c) If the peripheral actually only needs a periodic enable rather than a true clock, what simpler structure removes the whole duty-cycle problem? (d) Why is generating this /3 clock in RTL and routing it to the pin still safer than routing it internally to clock on-chip logic — what makes the off-chip case different from the DebugLab's on-chip case?
10. Related Tutorials
Continue in RTL Design Patterns (sibling and forward links unlock as they ship):
- Ring & Johnson Counters — Chapter 3; shift-register counters that are themselves a form of clock/phase divider — a ring counter of length N produces N evenly-spaced one-cycle phases, another way to build divided enables.
- Pulse & Strobe Generators — Chapter 3; the
tickandtimeoutstrobes generalized into one-shot, edge-detected, and delayed pulse generation — the direct forward step from this page's enables. - FSM Fundamentals — Chapter 4; the controllers that consume these ticks and timeouts as clock-enables and sequencing events (a UART sampler advances its FSM on the baud tick).
Builds on (in-track):
- Mod-N & Programmable Counters — Chapter 3.2; the compare-and-reset counter whose one-cycle wrap pulse is the divided
tick— a clock-enable divider is a mod-N counter, and the terminal-value off-by-one carries straight over. - Register with Enable & Load — Chapter 2.2; the clock-enable and load semantics at the heart of this page — the "slow" logic is a clock-enabled register, and a timer is a loadable down-counter.
- Up/Down Counters — Chapter 3.1; the count register + enable + reset the divider extends and the down-count a timer uses.
- The Register Pattern (D-FF) — Chapter 2.1; the clocked register underneath every counter, divider, and timer, and the single-clock discipline this page insists on.
- Synchronous Reset — Chapter 2; the reset-to-zero / disarm discipline the divider and timer inherit.
- Gray-Code Counters — Chapter 3; the sibling counter built for safe clock-domain crossing — the correct tool for the real CDC that the divided-clock anti-pattern mishandles.
Method / overview:
- The RTL Design Mindset — Chapter 0.2; the Interface → State → Datapath → Control → Verification lenses this page applies (a divider/timer is state plus a tiny bit of control, on one clock).
- What Is an RTL Design Pattern? — Chapter 0.1; why "generate enables, not clocks" earns the name pattern — a recurring need, a reusable form, a synthesis reality, and a signature silicon-only failure.
Verilog v1 prerequisites (the grammar this pattern transcribes into):
- Arithmetic Operators — the
+ 1advance, the- 1decrement, and theN - 1terminal-value computation. - Relational Operators — the
==terminal compare that fires thetickand thetimeout. - If-Else Statements — the
if (tick) …clock-enable gating and the one-shot-vs-periodic branch at zero. - Case Statements — the selection construct behind mode and next-value choices in the counter and timer.
- Blocking and Non-Blocking Assignments — why every register here uses
<=in its single clockedalways, the sequential idiom the whole page follows.
11. Summary
- In RTL you generate clock-enables, not clocks. The design has exactly one clock — the source oscillator; everything "slower" is a one-cycle enable that gates whether a register updates. A divide-by-N is a mod-N counter (3.2) raising a
tickhigh for one cycle every N cycles, and the slow logic is a clock-enabled register (2.2) on the same clock, advancing only ontick. One clock domain, zero skew, every path analyzed. - A divided flip-flop output used as a clock is the signature bug.
always @(posedge clk_div)on an RTL-generated divided signal creates a new clock domain with skew, an unhandled CDC, and paths STA never sees. It simulates perfectly (zero skew, infinite margin) and passes STA (the clock is undeclared), then fails intermittently in silicon, worse when hot — the worst possible failure profile (the §7 DebugLab). Fix: convert it to a clock-enable on the one source clock. - Timers are the same counter, pointed at duration. A loadable down-counter decrements each cycle (or each
tick) and raisestimeoutat zero: one-shot fires once and disarms; periodic auto-reloads and repeats; a watchdog is a periodic timer that resets unless fed. The divider (frequency) and timer (elapsed time) are one atom — a counter reaching a programmed value. - A
/Ntick is a strobe, not a 50%-duty clock — and odd 50%-duty divides need both edges. The mod-Ntickis one cycle high,N-1low; for a symmetric divided clock signal use a toggle-flop (/2^k), and for an exact 50% duty on an odd integer divide you must combine rising- and falling-edge logic, because half of an odd count is not a whole cycle. If duty does not matter, prefer the enable and avoid the issue. - Verify by measuring, and prevent the anti-pattern structurally. A clocked self-checking testbench asserts the tick period is exactly N, the slow logic changes only on
tick, and the one-shot fires once / periodic repeats at the right interval — self-checking testbenches shown here in SystemVerilog, Verilog, and VHDL. The divided-clock bug, though, is invisible to functional sim; it is caught by structural review, CDC/lint, and STA — so "generate enables, not clocks" is a hard rule enforced by construction, not by a testbench.