GLS · Chapter 9 · CDC & Asynchronous Behaviour in GLS
Synchronisers Under Real Delays
The two-flop synchronizer is the workhorse of clock-domain crossing, and gate-level simulation shows how it behaves under real delays. The first flop samples the asynchronous input, and because that input can change during its aperture window, the flop may sample mid-transition, which the simulator models as an unknown. That unknown passes to the second flop, which gets a full cycle to settle and resolves it to a stable value the receiving domain can use. Because the first flop's unknown on a legitimate crossing is expected, its timing checks are usually disabled to avoid flooding the design, but only there. This lesson shows the synchronizer under real delays, explains why it works only for single-bit or gray-coded signals, and clarifies that metastability survival is quantified by MTBF, not by the simulation itself.
Foundation12 min readGLSSynchronizerCDCReal DelaysGray Code
Chapter 9 · Section 9.3 · CDC & Asynchronous Behaviour in GLS
Project thread — every crossing in the mini-SoC uses a synchronizer like this. This lesson shows it under real delays; 9.4 extends it to the async FIFO for multi-bit data.
1. Why Should I Learn This?
The synchronizer is the CDC building block — and it has two GLS gotchas.
- The first flop's
Xis expected — disable its checks (only there) to avoid X-flooding (8.5). - It works only for single-bit / gray-coded signals — multi-bit needs a FIFO/handshake (9.4).
- GLS shows structure/timing; MTBF verifies metastability survival (9.2).
This grounds every crossing in the chapter and the mini-SoC.
2. Real Silicon Story — the synchronizer that flooded the sim with X
A crossing used a proper two-flop synchronizer, but the gate-level run was flooded with X — hundreds of downstream signals unknown — and the team thought the synchronizer was broken.
The synchronizer was fine. Timing checks on its first flop were left enabled, so every async edge in the aperture injected X (correctly — the crossing is async), and that X propagated everywhere (8.5). Disabling timing checks on the first synchronizer flop (the expected async crossing) stopped the flood — and the synchronizer resolved cleanly as designed.
Lesson: the first synchronizer flop's X is expected on an async crossing — disable its timing checks (only there) to avoid X-flooding. The synchronizer isn't broken; the checks were misapplied.
3. Concept — the synchronizer under real delays
Structure:
- Two flops in series in the receive domain:
async_in → [flop1] → [flop2] → sync_out. - Flop1 samples the async input (may be metastable →
Xin sim, 9.2). - Flop2 gets a full cycle to settle the
X→ stablesync_out.
Under real delays:
- The settling budget = one receive-clock cycle minus (flop1 clk-to-Q + flop2 setup).
- MTBF quantifies whether that budget makes metastability acceptably rare (9.2) — GLS shows the timing, not the probability.
Two rules GLS teaches:
- (1) Disable checks on flop1 (only). The first flop's
Xon a legitimate async crossing is expected (8.4) — disabling its timing checks avoids X-flooding (8.5). Never disable checks elsewhere (masks real violations, 8.4). - (2) Single-bit / gray-coded only. A two-flop synchronizer is safe for one bit (or a gray-coded value where only one bit changes, 9.4). A multi-bit bus can have different bits resolve differently → garbage → use a handshake or async FIFO (9.4).
Scope (accuracy):
- GLS reveals structure and real-delay timing; metastability survival is MTBF (9.2).
- Disabling flop1's checks is targeted and justified — not wholesale (8.4).
4. Mental Model — a two-stage airlock
A synchronizer is an airlock between two pressures (clock domains).
- Flop1 is the outer door — it takes the chaotic outside pressure (async input) and may wobble (metastable) as it seals.
- Flop2 is the inner door — it waits a full cycle for the outer chamber to stabilize before opening into the clean domain.
- Don't alarm the outer door for wobbling (disable flop1's checks) — that's its job. But keep alarms on the inner rooms (checks elsewhere).
- One person at a time — an airlock passes a single value (bit); a crowd (multi-bit bus) needs a proper queue (FIFO/handshake).
Two doors, one cycle to settle, one bit at a time.
5. Working Example — the two-flop synchronizer (tri-HDL)
The synchronizer in three languages:
// SystemVerilog — 2-flop synchronizer (single-bit) in the receive domain
logic sync1, sync2;
always_ff @(posedge clk_b or negedge rst_n)
if (!rst_n) {sync2, sync1} <= 2'b0;
else {sync2, sync1} <= {sync1, async_in}; // async_in -> sync1 -> sync2
assign sync_out = sync2;// Verilog — same synchronizer
reg sync1, sync2;
always @(posedge clk_b or negedge rst_n)
if (!rst_n) {sync2, sync1} <= 2'b0;
else {sync2, sync1} <= {sync1, async_in};
assign sync_out = sync2;-- VHDL — same synchronizer
process (clk_b, rst_n) begin
if rst_n = '0' then sync1 <= '0'; sync2 <= '0';
elsif rising_edge(clk_b) then sync1 <= async_in; sync2 <= sync1; end if;
end process;
sync_out <= sync2;The GLS setup for the crossing:
# Synchronizer in GLS (tool-neutral):
# DISABLE timing checks on sync1's D (the async crossing) -> expected X, avoid flood (8.4/8.5)
# keep checks everywhere else
# single-bit / gray-coded only (9.4); multi-bit bus -> async FIFO / handshake
# GLS shows sync1 X settling through sync2 under real delays; MTBF verifies survival (9.2)Practical context (representative, tool-neutral):
# Synchronizer GLS checklist (tool-neutral):
# [ ] disable timing checks on flop1 (async crossing) -- ONLY there (8.4/8.5)
# [ ] single-bit or gray-coded signal? (multi-bit -> FIFO/handshake, 9.4)
# [ ] X at flop1 settles through flop2 within one receive cycle (real delays)
# [ ] metastability survival -> MTBF, not this sim (9.2)The X settling through the synchronizer, as a real waveform:
Two-flop synchronizer under real delays: flop1 X (risk) settles through flop2 to a stable sync_out
9 cycles6. Debugging Session — X-flood from a synchronizer's first flop
A gate-level run floods with X from a proper synchronizer because timing checks on its first flop were left enabled, injecting the expected async-crossing X everywhere; disabling checks only on the first synchronizer flop stops the flood without masking real violations
DISABLE CHECKS ON THE FIRST SYNC FLOP (ONLY) — EXPECTED ASYNC XA crossing uses a correct two-flop synchronizer, but the run is flooded with X — the synchronizer looks broken.
Timing checks left enabled on the first synchronizer flop. The first flop samples an asynchronous input, so data legitimately changes in its aperture window every crossing — firing a $setup/$hold check and injecting X (correctly, 9.2/8.5). But that X propagates downstream (8.5), flooding the design. The crucial point: this X is expected on a legitimate async crossing — the check is not physically meaningful there (8.4). Leaving it enabled turns a working synchronizer into an X-fountain. The synchronizer isn't broken; the timing checks are misapplied to an async crossing.
Disable timing checks on the first synchronizer flop's async input — a targeted, justified disable on a legitimate async crossing (8.4) — to stop the expected-X flood. Keep checks everywhere else (disabling wholesale would mask real violations, 8.4). The synchronizer then resolves cleanly under real delays (flop1 X → flop2 settled). Two related checks: confirm the crossing is single-bit / gray-coded (a multi-bit bus through a 1-bit synchronizer is a real bug — different bits resolve differently → garbage → use a FIFO/handshake, 9.4); and remember metastability survival is MTBF, not this sim (9.2). The lesson: the first synchronizer flop's X on an async crossing is expected — disable its timing checks (only there) to avoid X-flooding, keep checks elsewhere, and ensure the crossing is single-bit/gray-coded. (GLS shows structure/timing; MTBF verifies survival; STA signs off timing, 0.3.)
7. Common Mistakes
- Leaving timing checks on the first synchronizer flop. Expected async
Xfloods the sim (8.5). - Disabling checks wholesale to stop the flood. Masks real violations elsewhere (8.4).
- Sending a multi-bit bus through a 1-bit synchronizer. Bits resolve differently → garbage (use FIFO/handshake, 9.4).
- Thinking GLS proves the synchronizer beats metastability. That's MTBF (9.2).
- Not giving flop2 a full cycle to settle. The settling budget is the point.
8. Industry Best Practices
- Disable timing checks only on the first synchronizer flop (async crossing, justified).
- Keep checks everywhere else — never wholesale.
- Use synchronizers for single-bit / gray-coded signals; FIFO/handshake for multi-bit (9.4).
- Verify metastability survival with MTBF (9.2), not GLS.
- Confirm the settling budget (flop1 clk-to-Q + flop2 setup within one cycle).
Senior Engineer Thinking
- Beginner: "The synchronizer floods the sim with
X— it's broken." - Senior: "The first flop's
Xis expected on an async crossing — I disable its checks (only there) to stop the flood. And is this signal single-bit? A bus through a 1-bit synchronizer is the real bug."
The senior disables checks only on the first sync flop and verifies single-bit/gray-coded usage.
Silicon Impact
The two synchronizer gotchas map to two silicon realities. (1) Leaving checks on the first flop doesn't cause a silicon bug, but it buries real violations under an X-flood, so a genuine timing bug elsewhere gets masked (or the team disables checks wholesale to cope, 8.4 — and that ships a real bug, 0.3). (2) A multi-bit bus through a one-bit synchronizer is a real, serious silicon bug: different bits resolve independently, so the receiving domain can latch a value that never existed — a classic CDC data-corruption failure, fixed only by a gray code, handshake, or async FIFO (9.4). GLS reveals both (the flood, and — with real delays — the multi-bit skew), while MTBF handles the metastability probability. Correct synchronizer usage under real delays is foundational to every safe crossing.
Engineering Checklist
- Disabled timing checks only on the first synchronizer flop (async crossing).
- Kept checks everywhere else (no wholesale disable, 8.4).
- Confirmed the crossing is single-bit / gray-coded (else FIFO/handshake, 9.4).
- Verified the settling budget (flop1 clk-to-Q + flop2 setup ≤ one cycle).
- Left metastability survival to MTBF (9.2), not GLS.
Try Yourself
- Build a two-flop synchronizer and drive
async_into change in flop1's aperture; run with flop1's timing checks enabled — watchXflood downstream. - Observe: the flood comes from the expected async
Xon flop1 (8.5). - Change: disable timing checks only on flop1's async input.
- Expect: the flood stops;
Xsettles cleanly through flop2. Then push a 2-bit bus through and watch the bits resolve independently — the real bug a FIFO/handshake fixes.
Any free Verilog simulator with two async clocks and configurable timing checks reproduces this. No paid tool required.
Interview Perspective
- Weak: "A synchronizer just delays the signal two cycles."
- Good: "The first flop may go metastable (X in sim); the second settles it. Disable timing checks on the first flop's async input."
- Senior: "Under real delays, flop1's clk-to-Q plus flop2's setup must fit in a cycle to settle. Flop1's
Xon an async crossing is expected, so I disable its checks (only there) to avoid X-flooding — never wholesale. Synchronizers are single-bit/gray-coded only; a bus needs a FIFO/handshake. GLS shows structure/timing; MTBF verifies survival."
9. Interview / Review Questions
10. Key Takeaways
- A two-flop synchronizer places two flops in series in the receive domain: flop1 samples the async input (may be metastable →
Xin sim, 9.2), flop2 gets a full cycle to settle it to a stablesync_out. - Under real delays, the settling budget is one receive-clock cycle minus (flop1 clk-to-Q + flop2 setup) — MTBF quantifies the metastability probability, GLS shows the timing (9.2).
- Disable timing checks on flop1 only — its
Xon a legitimate async crossing is expected (8.4); leaving them on floods the sim (8.5); disabling wholesale masks real violations (8.4). - A synchronizer is safe only for single-bit / gray-coded signals — a multi-bit bus needs a handshake or async FIFO (9.4), or bits resolve independently into garbage.
- GLS shows structure and real-delay timing; metastability survival is MTBF's job (9.2); STA signs off timing (0.3). Next: 9.4 — asynchronous FIFO behaviour in GLS.
Quick Revision
2-flop synchronizer: flop1 samples async (may be
X, 9.2), flop2 settles it within one receive cycle. Disable timing checks on flop1 ONLY (expected asyncX→ avoid flood, 8.5); keep checks elsewhere. Single-bit / gray-coded only — multi-bit bus → FIFO/handshake (9.4), else bits resolve independently → garbage. GLS shows structure/timing; MTBF verifies survival (9.2). Next: 9.4 — async FIFO in GLS.