Slow functions run far below the system clock, such as a millisecond timebase from megahertz clocks. A tick enable marks one cycle in every N, and slow logic acts only then, all on the same clock.
The bottom row, tick, pulses high once per DIV cycles.
Slots 1–2: tick is low.
Slot 3: tick is high for one cycle.
Slots 4–6: tick is low.
Slot 7: tick is high for one cycle.
Slots 8–10: tick is low.
Slot 11: tick is high for one cycle.
Slots 12–14: tick is low.
The prescaler counts DIV cycles and registers its tick so the pulse is glitch-free. The timer counts N such ticks after start, then pulses expired.
DIV must be at least 2 and N at least 1: a one-cycle prescaler needs no counter, and a zero-tick timer has nothing to count. Both modules report a time-zero simulation error otherwise (an initial check at time zero, not an elaboration-time diagnostic).
The full code sits under Testbench below.
Predict when the timer first counts a tick. Number the first rising edge after reset release as edge 0. For the code below, DIV=4 and the comparison is c == DIV-2: a tick is produced after edge 2 and consumed at edge 3. Both sequential blocks read old values before NBA updates. The waveform above shows the periodic pulse shape; its slots are not these numbered post-reset edges.
Edge | Old c -> new c | Tick before -> after | Timer action (start=0, running) |
|---|---|---|---|
0 | 0 -> 1 | 0 -> 0 | No accepted tick |
1 | 1 -> 2 | 0 -> 0 | No accepted tick |
2 | 2 -> 3 | 0 -> 1 | No accepted tick; new tick is not yet visible to timer |
3 | 3 -> 0 | 1 -> 0 | Accept tick 1 |
6 | 2 -> 3 | 0 -> 1 | Produce next tick |
7 | 3 -> 0 | 1 -> 0 | Accept tick 2 |
start wins over a tick on the same edge, including a restart while running: it clears the interval and that coincident tick does not count. With N=3, starts at edges 0, 1, 2, 3 expire after 11, 10, 9, 12 further rising edges respectively. Each expiry follows exactly three accepted ticks; elapsed duration depends on start phase. expired is high for one fast-clock cycle, then clears even if the timer is idle.
Tick enable (one clock) | Divided clock | |
|---|---|---|
Clock domains | One | A new clock: needs clock routing and constraints; related clocks can be timed together |
Timing analysis | Ordinary single-clock paths | Generated clock, extra checks |
Glitches | Registered pulse, none | A divider output used as a clock must be glitch-free |
Test/scan | Normal | Divided clock needs special handling |
Flexibility | Change DIV without touching the clock network | Re-plan clocking |
Period of the clock = 10 ns, so DIV = 1 µs / 10 ns = 100. $clog2(100) = 7 bits.
The bounded test uses DIV=4, N=3. It checks 40 cycles of tick production/consumption, every start phase, start coincident with tick, and restart while running on a tick. An independent cycle-index reference checks spacing and pulse width every edge; a separate accepted-tick model predicts exact expiry. Inputs change in the low phase, the old tick is sampled at the rising edge, and outputs are checked 1 ns later after NBA updates. These are functional checks, not hardware timing measurements.
module tick_gen #(parameter int DIV = 4) ( // DIV must be at least 2
input logic clk, rst_n,
output logic tick // one-cycle pulse every DIV cycles
);
logic [$clog2(DIV)-1:0] c;
initial begin
if (DIV < 2) $error("tick_gen: DIV must be at least 2");
end
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) begin c <= '0; tick <= 1'b0; end
else begin
tick <= (c == DIV - 2); // registered, so tick is glitch-free
c <= (c == DIV - 1) ? '0 : c + 1'b1;
end
endmodule
module timer #(parameter int N = 3) ( // N must be at least 1
input logic clk, rst_n, tick, start,
output logic expired // pulses after N ticks
);
initial begin
if (N < 1) $error("timer: N must be at least 1");
end
logic [$clog2(N+1)-1:0] n;
logic running;
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) begin n <= '0; running <= 1'b0; expired <= 1'b0; end
else begin
expired <= 1'b0;
if (start) begin n <= '0; running <= 1'b1; end
else if (running && tick) begin
if (n == N - 1) begin running <= 1'b0; expired <= 1'b1; end
else n <= n + 1'b1;
end
end
endmodule