Two flip-flops in series, both on the destination clock, with nothing between them. The first flop may become metastable; the second samples one edge later. Usable settling time subtracts clock-to-Q, routing and setup overhead and accounts for clock effects. This lowers failure probability without guaranteeing that every event resolves in time.
// Synthesizable hardware; rst_dst_n is already release-synchronised to clk_dst.
module sync2_rtl (
input logic clk_dst, rst_dst_n, async_level,
output logic sync_level
);
(* ASYNC_REG = "TRUE" *) logic stage1, stage2;
always_ff @(posedge clk_dst or negedge rst_dst_n)
if (!rst_dst_n) begin stage1 <= 1'b0; stage2 <= 1'b0; end
else begin stage1 <= async_level; stage2 <= stage1; end
assign sync_level = stage2;
endmoduleReset both stages to the same inactive value and suppress consumption until local reset release and pipeline warm-up. ASYNC_REG is flow-specific; adapt it to the target library/tool. An unreset chain is also possible if the protocol masks its unknown startup state. See reset release and implementation hygiene. The delayed meta_ff listing below is simulation-only sampling uncertainty, not this hardware or a physical resolution model.
One bit only, or a set of bits that are independent. Several related bits need a different structure.
The source value must stay stable for more than one destination clock period plus setup and hold (the usual engineering rule is at least 1.5 to 2 periods), or the destination may miss it.
No logic between the two flops, and no logic (other than the register) between the source flop and the first synchroniser flop. Logic creates glitches the first flop can catch.
The source should be a register output, not a combinational function of several registers, so it is glitch-free.
Don't branch: use the output of the second flop only. Using the first flop's output (or fanning it out) defeats the settling time.
In ideal RTL, a stable transition appears just after the second subsequent destination edge: elapsed latency is between roughly one and two periods, plus clock-to-Q. Missing the first capture can add an edge; physical metastability has no deterministic maximum latency. A source that toggles too quickly can lose pulses.
Fast events need a pulse synchroniser or a handshake (see the CDC handshakes topic).
No: 10 ns is shorter than the 50 ns destination period, so the pulse can fall between two sampling edges and vanish. The source must stretch the pulse, or use a toggle/pulse synchroniser or a handshake.
The waveform below is simulation output for a one-fast-cycle pulse crossing into a slower domain two ways: first a plain two-flop synchroniser, then a toggle scheme as described in the pulse synchroniser lesson. How to read it: one slot is one cycle of clk_f; clk_s shows its own level at each slot.
pulse_f is high for a single fast slot (slot 2), between two slow edges, so sync_p stays low in every slot: the plain synchroniser loses the pulse.
The same event sent as a toggle holds still: tog_f flips in slot 5 and stays high, giving the slow clock a stable level to sample.
sync_t rises in slot 10, two slow edges later, and the edge detector answers with pulse_s high across slots 10 to 12 and low again in slot 13.
A level that holds still crosses safely; a pulse shorter than the destination period needs a toggle or a handshake.
`timescale 1ns/1ps
// Sampling uncertainty only: random digital capture at fixed TCQ after a window hit.
// No analogue state, late-resolution tail, or downstream disagreement is modelled.
module meta_ff #(parameter real TCQ = 0.3, TSU = 0.15, THD = 0.1) (
input logic clk, d,
output logic q
);
realtime t_d = -100.0, t_clk = -100.0;
int meta_events = 0;
initial q = 1'b0;
always @(d) begin
t_d = $realtime;
if ($realtime - t_clk < THD) begin // changed just after the edge (hold window)
meta_events++; q <= #TCQ $urandom_range(0, 1);
end
end
always @(posedge clk) begin
t_clk = $realtime;
if ($realtime - t_d < TSU) begin // changed just before the edge (setup window)
meta_events++; q <= #TCQ $urandom_range(0, 1);
end else q <= #TCQ d;
end
endmodule
// Simulation-only chain; synthesizable hardware is listed separately above.
module sync2 (input logic clk, d, output logic q);
logic q1;
meta_ff f1 (.clk, .d, .q(q1)); // uncertain capture on model window hits
meta_ff f2 (.clk, .d(q1), .q(q)); // fixed-delay digital sample one edge later
endmodule
Finite source-check obligation | Expected check |
|---|---|
Input toggles at random times (about 35 to 90 ns apart), unrelated to the 7 ns destination clock | 20,000 toggles |
Setup/hold window hits by the first flop (random digital captures) | At least 100; the recorded run gives 725 |
Output edges per input edge | Exactly 1: no glitch, no lost edge |
Latency | Ceiling(elapsed time / 7 ns), bins 2 to 3 for this fixed-delay model only |
Final value | Equals the input |
Source checker, run with Icarus Verilog 13.0 (2026-10-16): 20,000 input edges give 20,000 output edges, latency bins 2 to 3 and 725 window hits, then PASS. It drives 20,000 slow transitions with random model window hits and a bounded eight-edge final drain. Latency is the ceiling of elapsed input-to-output time divided by 7 ns, including the model's 0.3 ns TCQ; it is a time-bin statistic, not a count of subsequent clock edges. Window hits are exposure coverage, not absorbed physical failures.