Digital Design & Verification (Verilog, SystemVerilog, VLSI) Module 6 – Clocks, resets and clock-domain crossing
Clocks for RTL designers: enables, gating, muxes
Why designs keep few clocks, how a latch-based gate stops a clock without the glitch of an AND gate, a glitch-free clock switch and generated clocks.
What you will learn
- Explain why designs keep few clocks and slow their logic down with clock enables
- Show why an AND gate on a clock glitches and how a latch-based gating cell prevents it
- Build a glitch-free switch between two clocks and check its pulse widths in simulation
- Recognise generated clocks and the constraints that tell timing analysis about them
Before you start
What you’ll learn · Before you start
What you will learn
- Explain why designs keep few clocks and slow their logic down with clock enables
- Show why an AND gate on a clock glitches and how a latch-based gating cell prevents it
- Build a glitch-free switch between two clocks and check its pulse widths in simulation
- Recognise generated clocks and the constraints that tell timing analysis about them
Before you start
On this page
Gate a 100 MHz clock with a plain AND gate and an enable from a flip-flop, and a counter on the gated clock counted 41 edges in 64 cycles where the enable allowed 28. A clock is not like other signals: each of its edges is an event for every flip-flop it reaches, so a pulse cut short is a wrong clock edge, not a wrong value for a moment.
RTL designers change clocks in four ways, and this lesson builds each one: slowing logic down with an enable, stopping a clock with a gating cell, switching between two clocks without a glitch, and making a slower clock from a faster one. Icarus Verilog 13.0 and Yosys 0.69 ran every circuit here, and every number comes from those runs.
Each extra clock is a domain to cross, so slow logic uses an enable
A clock reaches its flip-flops through a network of its own. On a chip, clock tree: The network of buffers and wires that carries a clock to every flip-flop of a chip so that its edges arrive at nearly the same time. FPGAs have dedicated global clock networks for the same job.
synthesis builds a tree of buffers so that an edge arrives at every flip-flop at nearly the same time; in the
open-source flow that is OpenROAD’s cts step. An FPGA has dedicated global clock networks for the same job, and
AMD’s constraints guide recommends keeping look-up tables out of clock trees as much as possible (UG903). The
flip-flops of one such network form one clock domain: The flip-flops clocked by one clock, or by clocks that have a fixed timing relationship to it. A signal that goes from one domain to another crosses domains and needs a synchronizer or a protocol., and timing analysis checks every
path between two of them against that one clock.
A second clock is a second network and a second domain. Every signal between the two domains must then be synchronized, every path between them needs a timing constraint, and every crossing needs its own verification: the rest of this module is about exactly that work. The counters lesson measured the alternative on one slow counter:
| One counter that steps every 16 cycles | Clock nets in the netlist | Signals to synchronize | Clocks to declare |
|---|---|---|---|
| Clocked by bit 3 of a divider | 2: clk_i clocks 4 flip-flops, div_q[3] clocks 4 | every signal between the two groups | the divider bit, as a second clock |
| Advanced by a clock enable: An input that decides whether a register loads a new value at a clock edge or keeps its value. Synthesis builds it as a multiplexer in front of the flip-flop, or a flip-flop with an enable pin. More in “Sequential logic, always_ff and nonblocking” | 1: clk_i clocks all 8 | none | none |
The enable keeps one domain because it is ordinary logic in front of each flip-flop’s D input; the clock is never touched. So the rule of RTL design is to keep one clock wherever logic only needs to act less often, and to change a clock itself for one of three reasons only: to save the power its edges cost, to choose between two sources, or to give a pin or a block a slower clock it really needs. Each of these has one safe circuit, and the rest of the lesson builds them.
Checkpoint
Check: Keeping one clock with enables
An AND gate on the clock cuts pulses whenever the enable changes
Stopping a clock is clock gating: Stopping the clock of a group of flip-flops while their enable is off, so that they spend no power on clock edges. It is done with a clock-gating cell, never with a plain AND gate on the clock.: the flip-flops behind the gate get no edges while
their enable is off, so they spend no power on the clock. The obvious circuit is clk & en. clock_gate.sv has it as
gate_and, beside the circuit of the next section, and tb_clock_gate.sv drives both with the same enable, which a
flip-flop changes 1 ns after each rising edge, as any enable made on the same clock does:
hdl/clock_gate.sv
`timescale 1ns / 100ps
// Two clock gates for the same job: let clk through only in cycles where en is 1.
// en comes from a flip-flop clocked by clk, so it changes just after a rising edge.
// An AND gate: en reaches the gate whenever it changes, even in the middle of a pulse.
module gate_and (
input logic clk,
input logic en,
output logic gclk
);
assign gclk = clk & en;
endmodule
// A latch and an AND gate, the structure of an integrated clock-gating cell: the latch is
// open while clk is low and closed while it is high, so en can only change the gate's
// second input between pulses. (One assignment and no feedback: = and <= behave alike.)
module gate_latch (
input logic clk,
input logic en,
output logic gclk
);
logic en_held;
always_latch
if (!clk) en_held = en;
assign gclk = clk & en_held;
endmodule hdl/tb_clock_gate.sv
`timescale 1ns / 100ps
// Gates a 100 MHz clock with an enable from a flip-flop (1 ns clock-to-output delay), with
// both gates of clock_gate.sv. Prints a window of the waveforms (# is 1, _ is 0, one
// character per nanosecond), then counts the pulses of each gated clock, the edges a
// counter on it saw, and the pulses shorter than half a clock period.
module tb_clock_gate;
logic clk = 0, en = 0;
logic gclk_and, gclk_latch;
always #5 clk = ~clk;
gate_and u_and (.clk, .en, .gclk(gclk_and));
gate_latch u_latch (.clk, .en, .gclk(gclk_latch));
// The enable pattern: 64 cycles from a seeded xorshift generator, in runs of 1 to 4.
int unsigned state = 32'd2463534242;
function automatic int unsigned next_random();
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
return state;
endfunction
logic pattern[64];
initial begin
int i, run;
logic value;
i = 0;
value = 0;
while (i < 64) begin
run = next_random() % 4 + 1;
for (int k = 0; k < run && i < 64; k++) pattern[i++] = value;
value = !value;
end
end
int cycle = 0;
always @(posedge clk) begin
en <= #1 pattern[cycle];
cycle <= cycle + 1;
end
// What a counter on each gated clock sees, and the edges the enable allows: an edge k is
// allowed when en was 1 during the cycle before it.
int edges_and = 0, edges_latch = 0, allowed = 0;
always @(posedge gclk_and) edges_and++;
always @(posedge gclk_latch) edges_latch++;
always @(posedge clk) if (en) allowed++;
// Pulse widths of each gated clock.
realtime rise_and, rise_latch;
int pulses_and = 0, pulses_latch = 0, short_and = 0, short_latch = 0;
realtime min_and = 1e9, min_latch = 1e9;
always @(posedge gclk_and) rise_and = $realtime;
always @(negedge gclk_and) begin
pulses_and++;
if ($realtime - rise_and < 5.0) short_and++;
if ($realtime - rise_and < min_and) min_and = $realtime - rise_and;
end
always @(posedge gclk_latch) rise_latch = $realtime;
always @(negedge gclk_latch) begin
pulses_latch++;
if ($realtime - rise_latch < 5.0) short_latch++;
if ($realtime - rise_latch < min_latch) min_latch = $realtime - rise_latch;
end
// One character per nanosecond, from 0 to 80 ns.
string row_clk = "", row_en = "", row_and = "", row_latch = "";
initial begin
#0.5;
repeat (80) begin
row_clk = {row_clk, clk ? "#" : "_"};
row_en = {row_en, en ? "#" : "_"};
row_and = {row_and, gclk_and ? "#" : "_"};
row_latch = {row_latch, gclk_latch ? "#" : "_"};
#1;
end
end
initial begin
#642;
$display("first 80 ns, one character per ns:");
$display("clk %s", row_clk);
$display("en %s", row_en);
$display("clk & en %s", row_and);
$display("latch gate %s", row_latch);
$display("over 64 cycles, en allowed %0d edges", allowed);
$display("AND gate: %0d edges counted, %0d pulses shorter than 5 ns, shortest %.1f ns",
edges_and, short_and, min_and);
$display("latch gate: %0d edges counted, %0d pulses shorter than 5 ns, shortest %.1f ns",
edges_latch, short_latch, min_latch);
$finish;
end
endmodule Commands · hdl/gate_session.py
from session import run
# Icarus Verilog runs both clock gates of clock_gate.sv on the same enable for 64 cycles.
run("""
iverilog -g2012 -Wall -o gate.vvp tb_clock_gate.sv clock_gate.sv
vvp -n gate.vvp
""") Output
# Icarus Verilog 13.0 (stable) $ iverilog -g2012 -Wall -o gate.vvp tb_clock_gate.sv clock_gate.sv $ vvp -n gate.vvp first 80 ns, one character per ns: clk _____#####_____#####_____#####_____#####_____#####_____#####_____#####_____##### en ______________________________________________##############################____ clk & en ______________________________________________####_____#####_____#####_____#____ latch gate _______________________________________________________#####_____#####_____##### over 64 cycles, en allowed 28 edges AND gate: 41 edges counted, 25 pulses shorter than 5 ns, shortest 1.0 ns latch gate: 28 edges counted, 0 pulses shorter than 5 ns, shortest 5.0 ns tb_clock_gate.sv:91: $finish called at 6420 (100ps)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 gate_session.py
In the drawn window the enable rises at 46 ns and falls at 76 ns, both times while the clock is high. The AND gate passed each change straight into the clock: a 4 ns pulse at 46 ns, one cycle before the first edge the enable allowed, and a 1 ns pulse at 75 ns where the last pulse should have lasted 5 ns. Over 64 cycles that made 41 edges where the enable allowed 28, and 25 pulses shorter than half a period. In this zero-delay simulation every short pulse still counted as an edge; in silicon a pulse shorter than a flip-flop’s minimum pulse width may clock some flip-flops and not others, which is a glitch: A short, unwanted pulse on the output of combinational logic while its inputs change at slightly different times. It is over before the next clock edge, so it harms only logic that uses the signal without that clock. More in “Counters, LFSRs, PWM and clock enables” on a clock.
An AND gate as a clock gate
Wrong: assign gclk = clk & en; with en from a flip-flop on clk. Every rise of en added an early edge and every
fall cut a pulse to 1 ns, as the run shows. Right: a clock-gating cell, or a clock enable on the flip-flops
themselves, if (en) q <= d;, when only the logic has to stop.
A latch in front of the gate holds the enable while the clock is high
The cure is to let the gate’s enable change only while the clock is low, when the AND gate’s output is 0 whatever the enable does. A latch: A storage element that passes its input while its enable is on and holds its last value while the enable is off. A combinational block with a path that assigns nothing describes one. More in “Combinational logic with assign and always_comb” that is open while the clock is low and closed while it is high does exactly that. A latch and an AND gate together are a clock-gating cell: A standard cell that gates a clock without glitches: a latch, open while the clock is low, holds the enable, and an AND gate passes the clock only while the held enable is 1.: the behavioural model of sky130_fd_sc_hd__dlclkp, the clock gate of the SKY130 standard-cell library, is such a latch, enabled by the inverted clock, and such an AND gate. Step through gate_latch in the same run:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Edge 4. The clock rises and the latch closes, holding the enable it saw while the clock was low, 0. The gated clock stays low.
- 1 ns later. The enable's flip-flop changes en to 1 in the middle of the pulse. The latch is closed, so the AND gate still sees 0, where a plain AND gate would pass a 4 ns pulse.
- The clock falls. The clock goes low and the latch opens. The held enable becomes 1 while the AND gate's other input, the clock, is 0, so nothing reaches the gated clock.
- Edge 5. The clock rises with the held enable at 1, and the latch closes again. The gated clock carries a full 5 ns pulse.
- Edge 7, 1 ns later. Edges 6 and 7 passed full pulses too. 1 ns after edge 7 en falls, in the middle of the pulse. The latch is closed and still holds 1, so this pulse stays a full 5 ns, where the AND gate cut it to 1 ns.
- Edge 8. The latch took the 0 while the clock was low, so edge 8 is gated off. The enable was 1 for three cycles and the gated clock carried three full pulses, at edges 5, 6 and 7.
The latch gate passed exactly the 28 edges the enable allowed, and none of its pulses was shorter than 5 ns. Its first pulse came at edge 5, one cycle after the enable rose: an enable set in one cycle lets the next edge through, the same timing as an enable on a flip-flop.
You rarely draw this cell yourself. You write the enable, and an ASIC flow moves it into gating cells: the clockgate pass of Yosys “transforms each set of FFs sharing the same clock and enable signal into a clock-gating cell and a set of enable-less FFs”, which the next run shows on a bank of 8 registers:
hdl/bank.sv
// A bank of 8 registers that load together when en is 1, written with an enable.
module bank (
input logic clk,
input logic en,
input logic [7:0] d,
output logic [7:0] q
);
always_ff @(posedge clk)
if (en) q <= d;
endmodule hdl/cells.py
"""Counts the cells of each type in a Yosys JSON netlist.
Usage: python3 cells.py netlist.json"""
import json
import sys
from collections import Counter
for name, module in json.load(open(sys.argv[1]))["modules"].items():
if module.get("attributes", {}).get("blackbox"):
continue
kinds = Counter(cell["type"] for cell in module["cells"].values())
listed = ", ".join(f"{n} {kind}" for kind, n in sorted(kinds.items()))
print(f"{sys.argv[1]}: {sum(kinds.values())} cells: {listed}") Commands · hdl/gating_session.py
from session import run
# Yosys builds bank.sv, then its clockgate pass moves the enable of the 8 flip-flops into one clock-gating cell of
# the SKY130 library, sky130_fd_sc_hd__dlclkp_1, whose ports are GATE, CLK and GCLK.
run("""
yosys -q -p "read_verilog -sv bank.sv; synth -top bank; write_json before.json; clockgate -pos sky130_fd_sc_hd__dlclkp_1 GATE:CLK:GCLK; write_json after.json"
python3 cells.py before.json
python3 cells.py after.json
""") Output
# Yosys 0.69 $ yosys -q -p "read_verilog -sv bank.sv; synth -top bank; write_json before.json; clockgate -pos sky130_fd_sc_hd__dlclkp_1 GATE:CLK:GCLK; write_json after.json" $ python3 cells.py before.json before.json: 8 cells: 8 $_DFFE_PP_ $ python3 cells.py after.json after.json: 9 cells: 8 $_DFF_P_, 1 sky130_fd_sc_hd__dlclkp_1
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 gating_session.py
Eight flip-flops with an enable became eight plain flip-flops and one sky130_fd_sc_hd__dlclkp_1. The cell has one
timing rule of its own: the enable must reach the latch before the clock rises, which timing analysis checks with a
clock-gating setup and hold check (set_clock_gating_check in OpenSTA).
Key idea
A clock may only be gated while it is low: a latch that is open while the clock is low holds the enable through each pulse, so the gated clock carries whole pulses or none, where the AND gate alone cut 25 of them short.
Checkpoint
Check: Why an AND gate on a clock glitches · The latch-based clock-gating cell
A glitch-free switch lets go of one clock before it takes the other
Some blocks must run on one of two clocks: a slow crystal clock while a PLL starts, a test clock in place of the
functional one. A plain multiplexer, sel ? clk_b : clk_a, switches the instant sel changes, wherever the two clocks
are in their periods. clock_mux.sv has it as mux_plain, and a glitch-free clock switch: A circuit that moves its output from one clock to another without short pulses: it turns the old clock off while that clock is low, and only then turns the new one on while the new one is low. as mux_safe. The safe one gives each clock an enable of two flip-flops on that clock’s falling edge, so
an enable changes only while its clock is low, and lets an enable rise only after the other one has fallen. These
enables cross from one clock to the other, so they are synchronized, as Ginosar’s tutorial on synchronizers notes of
every clock-switching signal. tb_clock_mux.sv switches between a 10 ns and a 26 ns clock 200 times
at random moments and measures every high and low phase of both outputs:
hdl/clock_mux.sv
`timescale 1ns / 100ps
// Two ways to switch between clk_a (sel = 0) and clk_b (sel = 1).
// A plain multiplexer: the output follows sel at once, wherever the two clocks are.
module mux_plain (
input logic clk_a,
input logic clk_b,
input logic sel,
output logic clk_out
);
assign clk_out = sel ? clk_b : clk_a;
endmodule
// A glitch-free switch. Each clock has an enable that changes only on that clock's falling
// edge, through two flip-flops, so it never changes while that clock is high; and an enable
// rises only after the other clock's enable has fallen, so the two clocks never overlap.
module mux_safe (
input logic rst_n,
input logic clk_a,
input logic clk_b,
input logic sel,
output logic clk_out
);
logic a1, a2, b1, b2;
always_ff @(negedge clk_a or negedge rst_n)
if (!rst_n) begin
a1 <= 1'b0;
a2 <= 1'b0;
end else begin
a1 <= !sel && !b2;
a2 <= a1;
end
always_ff @(negedge clk_b or negedge rst_n)
if (!rst_n) begin
b1 <= 1'b0;
b2 <= 1'b0;
end else begin
b1 <= sel && !a2;
b2 <= b1;
end
assign clk_out = (clk_a && a2) || (clk_b && b2);
endmodule hdl/tb_clock_mux.sv
`timescale 1ns / 100ps
// Measures every high and low phase of a clock while `measuring` is 1. A phase shorter than
// `shortest` is a pulse no flip-flop on that clock was designed for.
module phase_meter #(
parameter realtime shortest = 5.0
) (
input logic clk,
input logic measuring
);
realtime last = 0, min_high = 1e9, min_low = 1e9, max_low = 0;
int short_high = 0, short_low = 0;
always @(clk) begin
if (measuring && last > 0) begin
if (clk) begin // a low phase just ended
if ($realtime - last < shortest) short_low++;
if ($realtime - last < min_low) min_low = $realtime - last;
if ($realtime - last > max_low) max_low = $realtime - last;
end else begin // a high phase just ended
if ($realtime - last < shortest) short_high++;
if ($realtime - last < min_high) min_high = $realtime - last;
end
end
last = $realtime;
end
endmodule
// Switches between a 100 MHz clock (clk_a, 10 ns) and a 38.5 MHz clock (clk_b, 26 ns) at
// 200 seeded random moments, through both switches of clock_mux.sv, and measures every
// phase of each output. At the end clk_b stops while the output runs on it, and sel asks
// for clk_a.
module tb_clock_mux;
logic clk_a = 0, clk_b = 0, sel = 0, rst_n = 0, measuring = 0;
logic out_plain, out_safe;
bit run_b = 1;
always #5 clk_a = ~clk_a;
initial begin
#3.3;
forever #13 if (run_b) clk_b = ~clk_b;
end
mux_plain u_plain (.clk_a, .clk_b, .sel, .clk_out(out_plain));
mux_safe u_safe (.rst_n, .clk_a, .clk_b, .sel, .clk_out(out_safe));
phase_meter m_plain (.clk(out_plain), .measuring);
phase_meter m_safe (.clk(out_safe), .measuring);
int unsigned state = 32'd88172645;
function automatic int unsigned next_random();
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
return state;
endfunction
realtime last_safe_edge = 0;
always @(posedge out_safe) last_safe_edge = $realtime;
// One switch drawn from 210 ns to 290 ns, one character per nanosecond.
string r_sel = "", r_a = "", r_b = "", r_plain = "", r_safe = "";
initial begin
#210.5;
repeat (80) begin
r_sel = {r_sel, sel ? "#" : "_"};
r_a = {r_a, clk_a ? "#" : "_"};
r_b = {r_b, clk_b ? "#" : "_"};
r_plain = {r_plain, out_plain ? "#" : "_"};
r_safe = {r_safe, out_safe ? "#" : "_"};
#1;
end
end
initial begin
#20 rst_n = 1;
#197 sel = 1;
#83;
$display("sel rises at 217 ns; 210 to 290 ns, one character per ns:");
$display("sel %s", r_sel);
$display("clk_a %s", r_a);
$display("clk_b %s", r_b);
$display("plain mux %s", r_plain);
$display("safe switch %s", r_safe);
sel = 0;
#100 measuring = 1;
repeat (200) begin
#(50 + next_random() % 400);
sel = !sel;
end
#600 measuring = 0;
$display("200 switches between a 10 ns and a 26 ns clock:");
$display("plain mux: %0d high and %0d low phases under 5 ns; shortest high %.1f ns, low %.1f ns",
m_plain.short_high, m_plain.short_low, m_plain.min_high, m_plain.min_low);
$display("safe switch: %0d high and %0d low phases under 5 ns; shortest high %.1f ns, low %.1f ns",
m_safe.short_high, m_safe.short_low, m_safe.min_high, m_safe.min_low);
$display("safe switch: longest low phase %.1f ns, while one clock hands over to the other",
m_safe.max_low);
// The handover needs both clocks: stop clk_b while the output runs on it, then ask for clk_a.
if (!sel) begin
sel = 1;
#400;
end
wait (!clk_b);
run_b = 0;
#100 sel = 0;
#2000;
$display("clk_b stopped and sel = 0 at %.1f ns; the safe switch's last edge: %.1f ns; now: %.1f ns",
$realtime - 2000, last_safe_edge, $realtime);
$finish;
end
endmodule Commands · hdl/mux_session.py
from session import run
# Icarus Verilog switches between two clocks 200 times through both switches of clock_mux.sv.
run("""
iverilog -g2012 -Wall -o mux.vvp tb_clock_mux.sv clock_mux.sv
vvp -n mux.vvp
""") Output
# Icarus Verilog 13.0 (stable) $ iverilog -g2012 -Wall -o mux.vvp tb_clock_mux.sv clock_mux.sv $ vvp -n mux.vvp sel rises at 217 ns; 210 to 290 ns, one character per ns: sel _______######################################################################### clk_a _____#####_____#####_____#####_____#####_____#####_____#####_____#####_____##### clk_b #_____________#############_____________#############_____________#############_ plain mux _____##_______#############_____________#############_____________#############_ safe switch _____#####_____#####______________________________________________#############_ 200 switches between a 10 ns and a 26 ns clock: plain mux: 71 high and 74 low phases under 5 ns; shortest high 0.3 ns, low 0.3 ns safe switch: 0 high and 0 low phases under 5 ns; shortest high 5.0 ns, low 5.0 ns safe switch: longest low phase 64.3 ns, while one clock hands over to the other clk_b stopped and sel = 0 at 52003.0 ns; the safe switch's last edge: 51886.3 ns; now: 54003.0 ns tb_clock_mux.sv:106: $finish called at 540030 (100ps)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 mux_session.py
The plain multiplexer made 71 high and 74 low phases shorter than 5 ns, the shortest 0.3 ns; in the drawn switch at 217 ns it cut a pulse of clk_a to 2 ns. The safe switch made none: its shortest phase was 5.0 ns, a half period of clk_a. Step through the drawn switch to see where its time goes:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Running on clk_a. clk_a's enable is 1 and clk_b's is 0, so clk_out is clk_a. Each enable is two flip-flops on the falling edge of its own clock, and each holds the other off.
- 217 ns, sel rises. sel asks for clk_b 2 ns into a high phase of clk_a. Nothing changes at once; a plain multiplexer would have switched here and cut that pulse to 2 ns.
- 220 ns and 230 ns. At two falling edges of clk_a, its enable's flip-flops take the 0. The enable falls at 230 ns, while clk_a is low, so the pulse from 225 ns to 230 ns was the last one and it was whole.
- 237.3 ns. At a falling edge of clk_b, its first flip-flop sees sel at 1 and clk_a's enable at 0, and takes the 1. Until clk_a's enable had fallen, it could not.
- 263.3 ns. At the next falling edge of clk_b, the enable rises, while clk_b is low. clk_out is still low.
- 276.3 ns, running on clk_b. clk_b rises and clk_out carries its first full pulse. The output was low for 46.3 ns, from 230 ns, and no pulse was cut.
The price is a gap: the output stays low while one enable falls and the other rises, 46.3 ns in the drawn switch and 64.3 ns at most in the 200. The design also needs both clocks running. At the end of the run clk_b stops while the output runs on it, and sel asks for clk_a: clk_b’s enable can only fall on a falling edge of clk_b, which never comes, so the safe switch gives no edge at all in the 2,000 ns that follow.
Switching to, or away from, a clock that is not running
Wrong: switching the select while one of the clocks is stopped, for example before a PLL has locked. The switch waits for edges of both clocks and hangs with its output low. Right: switch only while both clocks run, or give the switch a way to drop a dead clock that the rest of the design controls.
Checkpoint
Check: Switching clocks without glitches
Divided and switched clocks are generated clocks, and timing must be told
Sometimes a slower clock is the point: an output pin that must toggle at a third of the system clock, or a block that must run at a lower rate. A counter’s terminal count gives one-third of the frequency, high for one cycle in three. clock_div3.sv also gets a 50 % duty cycle: The share of each period that a clock or a pulse train is high: 50 % for a symmetric clock, 33.3 % for a divide-by-3 counter output that is high for one cycle in three., which needs both clock edges: q_pos is 1 for one cycle in three, q_neg copies it on the falling edge, half a cycle later, and their OR is 1 for one and a half cycles in three. Both outputs come from flip-flops and their OR, never from logic that can glitch on the way:
hdl/clock_div3.sv
`timescale 1ns / 100ps
// Divides clk by 3. q_pos is a flip-flop on the rising edge that is 1 for one cycle in
// three; q_neg copies it on the falling edge, half a cycle later. Their OR is 1 for one and
// a half cycles in three: a 50 % duty cycle. q_pos alone is the 33 % divider.
module clock_div3 (
input logic clk,
input logic rst_n,
output logic clk_div3,
output logic clk_div3_33
);
logic [1:0] count;
logic q_pos, q_neg;
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) begin
count <= 2'd0;
q_pos <= 1'b0;
end else begin
count <= (count == 2'd2) ? 2'd0 : count + 2'd1;
q_pos <= (count == 2'd2);
end
always_ff @(negedge clk or negedge rst_n)
if (!rst_n) q_neg <= 1'b0;
else q_neg <= q_pos;
assign clk_div3 = q_pos | q_neg;
assign clk_div3_33 = q_pos;
endmodule hdl/tb_clock_div3.sv
`timescale 1ns / 100ps
// Runs clock_div3 on a 100 MHz clock and measures the period and the high time of both
// outputs over 10 periods, after reset.
module tb_clock_div3;
logic clk = 0, rst_n = 0, div, div33;
always #5 clk = ~clk;
clock_div3 dut (.clk, .rst_n, .clk_div3(div), .clk_div3_33(div33));
realtime rise50 = 0, high50 = 0, period50 = 0, rise33 = 0, high33 = 0, period33 = 0;
int n50 = 0, n33 = 0, h50 = 0, h33 = 0;
always @(posedge div) begin
if (rise50 > 0) begin
period50 += $realtime - rise50;
n50++;
end
rise50 = $realtime;
end
always @(negedge div)
if (rise50 > 0) begin
high50 += $realtime - rise50;
h50++;
end
always @(posedge div33) begin
if (rise33 > 0) begin
period33 += $realtime - rise33;
n33++;
end
rise33 = $realtime;
end
always @(negedge div33)
if (rise33 > 0) begin
high33 += $realtime - rise33;
h33++;
end
string r_clk = "", r_50 = "", r_33 = "";
initial begin
#40.5;
repeat (60) begin
r_clk = {r_clk, clk ? "#" : "_"};
r_50 = {r_50, div ? "#" : "_"};
r_33 = {r_33, div33 ? "#" : "_"};
#1;
end
end
initial begin
#12 rst_n = 1;
@(posedge div);
repeat (10) @(posedge div);
$display("40 to 100 ns, one character per ns:");
$display("clk %s", r_clk);
$display("clk_div3 %s", r_50);
$display("33 %% out %s", r_33);
$display("clk_div3: period %.1f ns, high %.1f ns, duty %.1f %%", period50 / n50, high50 / h50,
100.0 * (high50 / h50) / (period50 / n50));
$display("33 %% output: period %.1f ns, high %.1f ns, duty %.1f %%", period33 / n33, high33 / h33,
100.0 * (high33 / h33) / (period33 / n33));
$finish;
end
endmodule Commands · hdl/div_session.py
from session import run
# Icarus Verilog runs the divide-by-3 of clock_div3.sv and measures both of its outputs.
run("""
iverilog -g2012 -Wall -o div.vvp tb_clock_div3.sv clock_div3.sv
vvp -n div.vvp
""") Output
# Icarus Verilog 13.0 (stable) $ iverilog -g2012 -Wall -o div.vvp tb_clock_div3.sv clock_div3.sv $ vvp -n div.vvp 40 to 100 ns, one character per ns: clk _____#####_____#####_____#####_____#####_____#####_____##### clk_div3 ##########_______________###############_______________##### 33 % out #####____________________##########____________________##### clk_div3: period 30.0 ns, high 15.0 ns, duty 50.0 % 33 % output: period 30.0 ns, high 10.0 ns, duty 33.3 % tb_clock_div3.sv:59: $finish called at 3350 (100ps)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 div_session.py
Both outputs have a 30 ns period, three cycles of clk. clk_div3 is high for 15.0 ns, a 50.0 % duty cycle, and the
counter’s own output for 10.0 ns, 33.3 %. Each is a generated clock: A clock that logic makes from another clock, such as a divider's output or a clock switch's output. Timing analysis is told about it with create_generated_clock, so that paths between it and its source are timed.: timing analysis
must know it is clk divided by three, or it cannot time the paths between the two. OpenSTA’s create_generated_clock
describes it from the source clock’s edges, numbered from 1: the 50 % output rises at edge 1 and falls at edge 4 of
clk, -edges {1 4 7}, and the 33 % one falls at edge 3, -edges {1 3 7}.
The switch of the last section puts two clocks on one net, and timing analysis traces both through it: AMD’s guide
notes that Vivado propagates every incoming clock through such a multiplexer. They can never be there at the same
time, so they are declared exclusive, with set_clock_groups -physically_exclusive or -logically_exclusive. When the
two clocks also have timing paths between them elsewhere in the design, AMD’s timing wizard declares generated copies of
them on the switch’s output and makes only the copies exclusive. A clock that passes a gating cell stays the same clock, delayed by the cell, and
needs no new declaration.
Checkpoint
Check: Generated clocks and their constraints
Choosing the gentlest change to a clock
The five circuits of this lesson trade a new clock, short pulses, power and timing work against each other. Answer two questions about a block of your own and the table marks what fits:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Way | New clock | Short pulses | Clock power | Timing work |
|---|---|---|---|---|
| Clock enable Built from: a multiplexer at each D input | Good none: every flip-flop stays on one clock | Good none: the clock is not touched | Poor the clock still reaches every flip-flop at every edge | Good nothing beyond the enable's own paths |
| Clock-gating cell Built from: a latch and an AND gate | Good none: the same clock, one cell later | Good none in the 64 cycles of the run | Good the gated flip-flops get no edges while off | Mixed a clock-gating check on the enable at the cell |
| AND gate on the clock Built from: one gate | Good none: the same clock | Poor 25 short pulses and 13 extra edges in 64 cycles | Good the same saving as the cell | Poor no check makes an enable that changes after the edge safe |
| Glitch-free switch Built from: two enables of two flip-flops | Mixed its output is one of two clocks, never both | Good none under 5 ns in 200 switches | Mixed not what it is for | Mixed two clocks on one net, declared exclusive |
| Divided clock Built from: a counter and two flip-flops | Poor a second clock with crossings to plan | Good from flip-flops: 50.0 % duty and no glitch | Mixed its logic is clocked less often | Mixed create_generated_clock with its edges |
How the answers decide
- “What does the logic need from its clock?” To act only every Nth cycle: rules out Glitch-free switch and Divided clock. Logic that acts every Nth cycle needs no second clock; an enable on the same clock does it and adds no domain.
- “What does the logic need from its clock?” To stop for long stretches: rules out Glitch-free switch and Divided clock and adds a caveat to Clock enable. To save the power of the clock itself the gated flip-flops must stop seeing edges; an enable stops only their work.
- “What does the logic need from its clock?” A slower clock for a pin or block: rules out Clock enable, Clock-gating cell and Glitch-free switch. A pin or a block that needs a slower clock needs a real one, divided by flip-flops and declared as a generated clock.
- “What does the logic need from its clock?” One of two clock sources: rules out Clock enable, Clock-gating cell and Divided clock. Choosing between two sources takes a switch that stops one clock while it is low before it starts the other.
- “Where do the enable or the select come from?” A flip-flop on the same clock: rules out AND gate on the clock. An enable from a flip-flop on the same clock changes just after the rising edge, while the clock is high.
- “Where do the enable or the select come from?” Another clock domain or software: rules out AND gate on the clock and adds a caveat to Clock-gating cell and Glitch-free switch. An enable from another domain changes at any moment; synchronize it to the clock first, or an edge is cut.
In short: an enable whenever logic only has to act less often; a gating cell, written as an enable and inserted by the flow, when the clock’s own power matters; a glitch-free switch between two sources that both run; a divider of flip-flops, declared as a generated clock, when a slower clock is the point; and never an AND gate or a plain multiplexer on a clock.
Checkpoint
Check: Choosing how to change a clock
Practise: count the edges of two gated clocks
The exercise predicts what the run of the AND-gate section counted, without a simulator: the edges and the short pulses of both gates for any enable pattern. Its first sample test replays the 64 cycles of tb_clock_gate.sv, which gave 28, 41 and 25.
Exercise · Medium · Python
Count the edges of two gated clocks
Predict what the two clock gates of clock_gate.sv do with an enable, without simulating them.
gated_edges(en) gets the enable cycle by cycle, as tb_clock_gate.sv drives it: en[k] is its value from 1 ns after rising edge k of the 10 ns clock until 1 ns after edge k + 1. Before edge 0 the enable is 0. The clock is high for the first 5 ns after each rising edge.
Return a dictionary with three counts over edges 0 to len(en) - 1:
"latch_edges": the rising edges of the latch gate's clock;"and_edges": the rising edges ofclk & en;"and_short": the pulses ofclk & enshorter than 5 ns.
The sample tests replay the lesson's 64 cycles, which gave 28, 41 and 25 in the simulation, and a few short patterns.
Starter code · gates.py
def gated_edges(en):
"""Counts the rising edges and short pulses of the two gated clocks of clock_gate.sv."""
latch_edges = and_edges = and_short = 0
for k in range(len(en)):
# One clock pulse: rising edge k, high for 5 ns.
if en[k]:
latch_edges += 1
and_edges += 1
return {"latch_edges": latch_edges, "and_edges": and_edges, "and_short": and_short} The sample tests · test_gates.py
from gates import gated_edges
def lesson_pattern():
"""The 64 enable values of tb_clock_gate.sv: runs of 1 to 4 cycles from its xorshift generator."""
state = 2463534242
def next_random():
nonlocal state
state ^= (state << 13) & 0xFFFFFFFF
state ^= state >> 17
state ^= (state << 5) & 0xFFFFFFFF
return state
pattern, value = [], 0
while len(pattern) < 64:
run = next_random() % 4 + 1
pattern += [value] * min(run, 64 - len(pattern))
value = 1 - value
return pattern
def test_the_lessons_64_cycles():
"""the simulation counted 28 latch-gate edges, 41 AND-gate edges and 25 short AND-gate pulses"""
assert gated_edges(lesson_pattern()) == {"latch_edges": 28, "and_edges": 41, "and_short": 25}
def test_enable_never_on():
"""an enable that stays 0 lets no edge through either gate"""
assert gated_edges([0, 0, 0, 0, 0]) == {"latch_edges": 0, "and_edges": 0, "and_short": 0}
def test_enable_on_from_the_start():
"""the enable rises 1 ns after edge 0: the AND gate makes a 4 ns pulse there, the latch gate waits for edge 1"""
assert gated_edges([1, 1, 1, 1]) == {"latch_edges": 3, "and_edges": 4, "and_short": 1}
def test_one_cycle_of_enable():
"""one cycle of enable: the latch gate passes one full pulse, the AND gate makes two short ones"""
assert gated_edges([0, 1, 0, 0]) == {"latch_edges": 1, "and_edges": 2, "and_short": 2} A hint
Look at one clock pulse at a time. At rising edge k the AND gate still sees the old enable, en[k - 1], for 1 ns, then the new one, en[k], for 4 ns. The latch gate sees only the enable it held while the clock was low before edge k, which is en[k - 1].
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Then practise on enables, gates, switches and generated clocks. The questions start where your answers have been weakest so far:
Practice that adapts to you
Practise this lesson
14 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): what is an integrated clock-gating cell, and why does it contain a latch?
It is a standard cell that stops a clock for the flip-flops behind it while their enable is 0, so they spend no power on clock edges. Inside are a latch and an AND gate. The latch is open while the clock is low and closed while it is high, so an enable that changes during a clock pulse waits until the clock has fallen; the AND gate then passes whole pulses or nothing. Without the latch, an enable from a flip-flop on the same clock changes just after the rising edge and the AND gate cuts or adds pulses: 25 short pulses in the lesson’s 64 cycles, against none with the latch. In RTL you write the enable, and the flow inserts the cell.
Two harder questions follow, on moving a running chip to another clock and on a clock gated by a bare AND gate. For each, draw the clocks and their enables over a few cycles before you look at the first step:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
A chip boots on a 25 MHz crystal clock and must move its core to a 400 MHz PLL clock once the PLL locks, without stopping the software. Design the switch, explain why a plain multiplexer is unsafe, and say what can make your switch hang.
-
Fresher to mid
In a code review you find assign gclk = clk & en; where en comes from a flip-flop on clk and gclk clocks 64 registers. The simulations pass. Is it safe, what would you measure to convince the author, and what do you change for an ASIC and for an FPGA?
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
Why does a design keep as few clocks as it can?
Each extra clock is another network and clock domain: every signal between domains must be synchronized, every path between them needs a timing constraint, and every crossing needs its own verification.
-
Why does assign gclk = clk & en; misbehave when en is registered on the same clock?
en changes just after the rising edge, while the clock is high, so the gate passes each change into the clock: an early edge when en rises and a cut pulse when it falls. The run counted 41 edges where 28 were allowed.
-
What is inside a clock-gating cell, and why?
A latch that is open while the clock is low, and an AND gate. The enable can then change only while the clock is low, so the gated clock carries whole pulses or none.
-
How does a clock-gating cell usually get into an ASIC design?
The RTL writes an enable and the flow moves it into gating cells: Yosys's clockgate pass turned eight enabled flip-flops into eight plain ones and one sky130_fd_sc_hd__dlclkp_1.
-
How does a glitch-free clock switch avoid short pulses, and what does it cost?
Each clock has an enable of two flip-flops on its falling edge, and one enable may rise only after the other has fallen, so the output lets go of one clock before taking the other. It costs a gap with the output low.
-
Why can a glitch-free clock switch hang?
Its old clock's enable can fall only on that clock's falling edge. If that clock has stopped, the edge never comes and the output gives no edge at all, so switch only while both clocks run.
-
How is a 50 % duty clock at a third of clk declared to timing analysis?
As a generated clock from the source's edges, numbered from 1: create_generated_clock with -edges {1 4 7}, rising at edge 1 and falling at edge 4 of clk.
-
How are two clocks that share one net through a switch declared?
As exclusive, with set_clock_groups -physically_exclusive or -logically_exclusive, because they can never be on the net at the same time.
Key takeaways
- Every extra clock is a domain with crossings, constraints and checks of its own, so logic that only needs to act less often uses a clock enable and keeps one clock.
- An AND gate on a clock turns every change of an enable from the same clock into a short pulse or an extra edge: 41 edges where 28 were allowed in the lesson’s run.
- A clock-gating cell holds the enable in a latch that is open only while the clock is low, so it passes whole pulses or none; you write the enable and the flow inserts the cell.
- A glitch-free switch turns the old clock off while it is low, then turns the new one on while it is low; it pays with a gap in the output and needs both clocks running.
- Divided and switched clocks are generated clocks: declare them from their source’s edges with create_generated_clock, and declare two clocks that share a net through a switch as exclusive.
Terms in this lesson
- clock domain
- The flip-flops clocked by one clock, or by clocks that have a fixed timing relationship to it. A signal that goes from one domain to another crosses domains and needs a synchronizer or a protocol.
- clock gating
- Stopping the clock of a group of flip-flops while their enable is off, so that they spend no power on clock edges. It is done with a clock-gating cell, never with a plain AND gate on the clock.
- clock tree
- The network of buffers and wires that carries a clock to every flip-flop of a chip so that its edges arrive at nearly the same time. FPGAs have dedicated global clock networks for the same job.
- clock-gating cell
- A standard cell that gates a clock without glitches: a latch, open while the clock is low, holds the enable, and an AND gate passes the clock only while the held enable is 1.
- duty cycle
- The share of each period that a clock or a pulse train is high: 50 % for a symmetric clock, 33.3 % for a divide-by-3 counter output that is high for one cycle in three.
- generated clock
- A clock that logic makes from another clock, such as a divider's output or a clock switch's output. Timing analysis is told about it with create_generated_clock, so that paths between it and its source are timed.
- glitch-free clock switch
- A circuit that moves its output from one clock to another without short pulses: it turns the old clock off while that clock is low, and only then turns the new one on while the new one is low.
References
- IEEE 1800-2023: IEEE Standard for SystemVerilog, Unified Hardware Design, Specification, and Verification Language (IEEE Standards Association)
- Metastability and Synchronizers: A Tutorial (IEEE Design & Test of Computers, vol. 28, no. 5) (IEEE)
- sky130_fd_sc_hd__dlclkp: the clock-gate cell of the SKY130 high-density library, behavioural model (The SkyWater PDK Authors)
- Yosys command reference: technology mapping passes (clockgate) (YosysHQ)
- OpenSTA command reference: create_generated_clock, set_clock_gating_check, set_clock_groups (The OpenROAD Project)
- OpenROAD documentation: clock tree synthesis (cts) (The OpenROAD Project)
- Vivado Design Suite User Guide: Using Constraints (UG903): clock groups and clock multiplexers (AMD)
- Icarus Verilog documentation (Icarus Verilog)
Report a problem with this lesson
Kept only in this browser. Your Learn progress