Interrupts, start commands and event flags are usually one-cycle pulses. When the destination clock is slower than the source, such a pulse can be present for part of a destination period but absent at both neighbouring sampling edges. The crossing then delivers nothing, with no error flag: the event simply never happened as far as the destination is concerned.
A destination flop samples its input at its own clock edges. A pulse is seen only if it overlaps at least one edge with enough setup and hold margin. A pulse shorter than the destination period, positioned between two edges, overlaps neither.
Direction | Pulse width versus destination period | Verdict |
|---|---|---|
Slow source to fast destination, pulse held 2+ destination cycles | Wide enough to be sampled at least twice | A plain two-flop synchroniser is fine |
Fast source to slow destination, one source cycle | Narrower than the destination period | Lost unless converted to a level first |
Same-rate, phase-unknown clocks | Comparable to the period | Treat as unsafe: use a pulse scheme anyway |
The tempting fix is wrong:
// bad-example: a short pulse fed straight into a synchroniser.
// It compiles, and it usually passes simulation, because the test pulse
// happens to overlap a destination edge. Shift the phase and it vanishes.
module bad_pulse_cdc (input logic clk_d, input logic pulse_s, output logic pulse_d);
logic q1;
always @(posedge clk_d) begin q1 <= pulse_s; pulse_d <= q1; end
endmoduleA structural analysis generally looks for a pulse qualifier on the crossing: a toggle flop, a stretch counter, or a handshake around the event, rather than a bare synchroniser on a signal the design treats as a pulse. A reviewer asks the same question in words: how long does this signal stay valid compared with the destination period?
One 5 ns pulse against a 20 ns destination period: a quarter of a period, so it can easily sit between two edges. Not acceptable; use a toggle, stretch or handshake scheme.