name: cdc-synchronizer description: > Generate production-quality Clock Domain Crossing (CDC) synchronization circuits in Verilog/SystemVerilog. Covers single-bit two-stage synchronizers, pulse synchronizers (fast-to-slow), handshake-based multi-bit transfer, DMUX synchronizers, and clock gating cell synchronizers. Every circuit includes MTBF analysis commentary, proper reset handling, and synthesis attributes to prevent optimization of synchronizer chains. Generates code that passes CDC verification tools (SpyGlass, Questa CDC). category: rtl-design level: advanced
Purpose
Generate correct, robust CDC circuits that handle all corner cases: metastability recovery, reset behavior, back-to-back transfers, and partial bus sampling. The generated code includes proper synthesis attributes to prevent logic optimization from breaking synchronizer chains.
Input Specification
Required
- Signal type: single-bit / multi-bit / pulse / level
- Source clock domain: clock name and approximate frequency
- Destination clock domain: clock name and approximate frequency
- Data rate: how often does the signal change relative to the slower clock
Optional
- MTBF target: desired mean time between failures (default: >1000 years)
- Latency tolerance: acceptable delay in destination clock cycles
- Coding standard: ASIC or FPGA (affects reset strategy and primitives)
Core Design Rules
Rule 1: The Two-Stage Synchronizer (Single-Bit, Slow-to-Fast or Level Signal)
This is the fundamental CDC primitive. Every single-bit CDC starts here.
// DO NOT let synthesis optimize this chain. Keep exactly 2 stages.
logic [1:0] sig_sync;
always_ff @(posedge dst_clk or negedge dst_rst_n) begin
if (!dst_rst_n) begin
sig_sync <= 2'b00;
end else begin
sig_sync <= {sig_sync[0], sig_async};
end
end
// USE sig_sync[1] downstream. NEVER use sig_sync[0] — it may still be metastable.Critical notes:
- The two flip-flops MUST be placed close together (same SLICE/CLB) — add placement constraints
- NEVER insert combinational logic between the two synchronizer stages
- NEVER use
sig_sync[0]anywhere — onlysig_sync[1] - Synthesis attribute:
(* ASYNC_REG = "TRUE" *)for Vivado,(* preserve *)for others
Rule 2: The Pulse Synchronizer (Single-Bit, Fast-to-Slow)
A narrow pulse in the fast domain might be missed by the slow clock. Solution: convert pulse to level toggle (stretch), synchronize the toggle, then regenerate the pulse.
// ---- Fast clock domain ----
logic toggle_fast;
always_ff @(posedge clk_fast or negedge rst_fast_n) begin
if (!rst_fast_n) begin
toggle_fast <= 1'b0;
end else if (pulse_fast) begin
toggle_fast <= ~toggle_fast; // flip on every pulse
end
end
// ---- Slow clock domain ----
logic [2:0] toggle_sync; // 3 stages for extra safety on slow clock
always_ff @(posedge clk_slow or negedge rst_slow_n) begin
if (!rst_slow_n) begin
toggle_sync <= 3'b000;
end else begin
toggle_sync <= {toggle_sync[1:0], toggle_fast};
end
end
// Edge detect: rising or falling edge = pulse occurred
assign pulse_slow = toggle_sync[2] ^ toggle_sync[1];Pulse stretching requirement: The pulse interval in the fast domain must be > 2× slow_clock_period. If user cannot guarantee this, use handshake instead.
Rule 3: The Handshake Synchronizer (Multi-Bit, Low-Speed)
For multi-bit data that changes infrequently (configuration registers, control words):
// ---- Source domain ----
logic req_src;
logic ack_src_sync;
always_ff @(posedge clk_src) begin
if (start_transfer) begin
data_hold <= data_to_send; // capture data
req_src <= 1'b1; // raise request
end else if (ack_src_sync) begin
req_src <= 1'b0; // clear request on ack
end
end
// Synchronize ack back to source domain
logic [1:0] ack_sync;
always_ff @(posedge clk_src) begin
ack_sync <= {ack_sync[0], ack_dst};
end
assign ack_src_sync = ack_sync[1];
// ---- Destination domain ----
logic [1:0] req_sync;
logic ack_dst;
always_ff @(posedge clk_dst) begin
req_sync <= {req_sync[0], req_src};
end
always_ff @(posedge clk_dst) begin
if (req_sync[1] && !ack_dst) begin
data_captured <= data_hold; // safe: data has been stable
ack_dst <= 1'b1;
end else if (!req_sync[1]) begin
ack_dst <= 1'b0;
end
endKey principle: Data is captured in the source domain BEFORE req is asserted, and remains stable until ack is received. The destination samples data only AFTER req is synchronized and stable. This guarantees data stability during sampling.
Rule 4: The DMUX Synchronizer (Multi-Bit, Medium-Speed, with Enable)
When multi-bit data has an accompanying valid/enable signal, synchronize only the enable and use it to gate the data capture:
// ---- Source domain ----
logic data_valid_src;
logic [WIDTH-1:0] data_src;
always_ff @(posedge clk_src) begin
if (produce_data) begin
data_src <= new_data;
data_valid_src <= 1'b1;
end else begin
data_valid_src <= 1'b0;
end
end
// ---- Destination domain ----
logic [1:0] valid_sync;
always_ff @(posedge clk_dst) begin
valid_sync <= {valid_sync[0], data_valid_src};
end
always_ff @(posedge clk_dst) begin
if (valid_sync[1]) begin
data_captured <= data_src; // safe: data is stable when valid asserted
end
endConstraint: Data must remain stable for at least 1 destination clock cycle after the synchronized valid is asserted. If not guaranteed, use handshake instead.
Rule 5: Synthesis Attributes for CDC Chains
Vivado / Xilinx:
(* ASYNC_REG = "TRUE" *) logic [1:0] sig_sync;Synplify:
(* syn_preserve = 1 *) logic [1:0] sig_sync;
(* syn_srlstyle = "registers" *) logic [1:0] sig_sync;Generic (for ASIC):
// Use `set_dont_touch` in SDC, not in RTL
// In SDC: set_dont_touch [get_cells sync_ff_1 sync_ff_2]Rule 6: CDC Naming Conventions
| Signal | Suffix | Example |
|--------|--------|---------|
| Async input (un-synchronized) | _async | sig_async |
| Synchronized output | _sync | sig_sync, pulse_sync |
| Synchronizer chain bits | _sync[N] | sig_sync[1], sig_sync[2] |
| Handshake request | _req | wr_req, rd_req |
| Handshake acknowledge | _ack | wr_ack, rd_ack |
Checklist Before Output
- [ ] Synchronizer uses exactly 2 stages (or 3 for pulse sync)
- [ ] Only the final stage output is used downstream
- [ ] No combinational logic between synchronizer stages
- [ ] Synthesis attributes applied to prevent optimization
- [ ] Reset strategy consistent with destination domain
- [ ] For multi-bit: data is guaranteed stable during sampling window
- [ ] For handshake: full req-ack cycle verified (no deadlock)
- [ ] For pulse sync: pulse interval > 2× slow_clock_period
- [ ] Signal naming follows CDC conventions
Interaction with Other Skills
verilog-module: Foundation coding rulesfifo-generator: Async FIFO uses these CDC primitives for pointer syncsdc-writer: Generate false_path or max_delay constraints for CDC pathsrtl-reviewer: Verify CDC correctnessassertion-writer: Generate assertions for CDC protocol compliance
Disclaimer
This skill is provided "as is" for reference and educational purposes only. Code generated using this skill should be independently reviewed and verified before use in any production design, tape-out, or safety-critical application. The author(s) and ICHDL assume no liability for any errors, omissions, or damages arising from the use of this skill or any code generated with it.
Usage Rights
You are free to use, modify, and distribute this skill for personal or commercial purposes. Attribution to ICHDL (ichdl.com) is appreciated but not required. Redistribution of this skill in substantially unmodified form must retain this notice.