CDC 同步器深度解析
概述
跨时钟域(CDC, Clock Domain Crossing)是数字芯片设计中最高频的故障来源之一。当信号从一个时钟域跨越到另一个时钟域时,若采样边沿落入信号跳变窗口,触发器的输出将进入亚稳态(Metastable),导致:
- 采样值不确定(0 或 1)
- 亚稳态传播至下游逻辑
- 多 bit 信号出现数据不一致(部分 bit 被新时钟采样、部分 bit 被旧时钟采样)
CDC 同步器是解决上述问题的基本单元。本文从亚稳态物理机制出发,逐一解析五种同步器结构的原理、适用场景与 RTL 实现。
目录
1. 亚稳态与 MTBF
1.1 亚稳态的本质
触发器的输出由内部一对交叉耦合的反相器(双稳态锁存器)维持。当 setup/hold 时间窗口内数据变化时,内部节点无法在额定时间内稳定到有效逻辑电平,输出悬停在中间电压区域。

1.2 MTBF 计算
MTBF(Mean Time Between Failure)是衡量同步器可靠性的核心指标:

其中:
| 参数 | 含义 | 典型值(180nm) |
|---|---|---|
| t_r | 允许的稳定时间 | 1~2 个时钟周期 |
| \tau | 触发器恢复时间常数 | 0.1~0.5 ns |
| f_{clk} | 采样时钟频率 | 2.5 MHz |
| f_{data} | 数据翻转频率 | 取决于信号 |
| T_W | 亚稳态窗口宽度 | 0.1~0.3 ns |
工程经验:
- t_r 每增加一个 FF 级数,MTBF 提升约 e^{(T_{clk}/\tau)} 倍
- 2.5 MHz 低频下 MTBF 非常充裕,但多 bit 同步时的数据一致性问题比亚稳态更致命
2. 双级触发器同步器(2-FF)
2.1 结构
最基础、最常用的 CDC 同步器,适用于单 bit 控制信号。

2.2 原理
- FF1 采样异步信号,允许进入亚稳态
- FF1 在一个时钟周期内完成恢复
- FF2 采样已稳定的 FF1 输出,输出有效同步信号
2.3 适用条件
| 条件 | 要求 |
|---|---|
| 信号类型 | 单 bit 电平信号 |
| 脉冲宽度 | 必须 > 2 倍 dst_clk 周期 |
| 数据速率 | 远低于 dst_clk 频率 |
| 多 bit 需求 | 不适用(各 bit 独立同步会导致数据不一致) |
2.4 注意事项
- 脉冲太窄被漏采:src_clk 域的脉冲宽度 < dst_clk 周期时,可能在两次采样之间消失
- 输出毛刺:FF1 输出仍可能亚稳态时被下一级组合逻辑使用 → 增加第三级 FF
2.5 RTL
module sync_2ff #(
parameter int WIDTH = 1
) (
input logic clk_dst,
input logic rst_n,
input logic [WIDTH-1:0] async_in,
output logic [WIDTH-1:0] sync_out
);
logic [WIDTH-1:0] meta;
always_ff @(posedge clk_dst or negedge rst_n) begin
if (!rst_n) begin
meta <= '0;
sync_out <= '0;
end else begin
meta <= async_in;
sync_out <= meta;
end
end
endmodule
注意:上述 WIDTH > 1 时仅用于电平信号多 bit 同步,不适用于需要保持一致性的多 bit 总线。
3. 三级/多级触发器同步器
3.1 三级结构
当 dst_clk 频率较低或 \tau 较大时,2-FF 的 MTBF 可能不足。增加第三级 FF 可大幅提升可靠性。

3.2 什么时候需要三级
| 场景 | 推荐级数 |
|---|---|
| f_{clk} < 10 \text{ MHz} | 2 级足够 |
| f_{clk} > 100 \text{ MHz} | 2 级,但需检查 MTBF |
| 极高可靠性要求(汽车/航空) | 3 级 |
| 后仿中观察到 F2 仍有亚稳态 | 3 级 |
3.3 RTL
module sync_3ff (
input logic clk_dst,
input logic rst_n,
input logic async_in,
output logic sync_out
);
logic f1, f2;
always_ff @(posedge clk_dst or negedge rst_n) begin
if (!rst_n) begin
f1 <= '0;
f2 <= '0;
sync_out <= '0;
end else begin
f1 <= async_in;
f2 <= f1;
sync_out <= f2;
end
end
endmodule
4. 握手同步器
4.1 适用场景
当 src_clk 域的信号是脉冲(单周期宽度)而非电平时,2-FF 可能漏采。握手同步器将脉冲转为电平 + 请求/应答协议,确保每个脉冲都被可靠传递。
4.2 结构

4.3 流程
- src 域脉冲到来 → toggle 寄存器翻转(电平变化)
- toggle 输出经 2-FF 同步到 dst 域
- dst 域检测到边沿 → 输出脉冲,同时翻转 ack toggle
- ack 经 2-FF 同步回 src 域
- src 域检测到 ack 边沿 → 允许发送下一个脉冲
4.4 RTL
module handshake_sync (
input logic clk_src,
input logic clk_dst,
input logic rst_n,
input logic pulse_in,
output logic pulse_out
);
logic toggle_src, toggle_dst_synced, ack_synced;
// src domain: toggle on pulse
always_ff @(posedge clk_src or negedge rst_n) begin
if (!rst_n) begin
toggle_src <= '0;
end else if (pulse_in) begin
toggle_src <= ~toggle_src;
end
end
// synchronize toggle to dst domain
sync_2ff sync_toggle (
.clk_dst, .rst_n,
.async_in(toggle_src),
.sync_out(toggle_dst_synced)
);
// dst domain: edge detect -> pulse_out
logic toggle_dst_dly;
always_ff @(posedge clk_dst or negedge rst_n) begin
if (!rst_n) begin
toggle_dst_dly <= '0;
end else begin
toggle_dst_dly <= toggle_dst_synced;
end
end
assign pulse_out = toggle_dst_synced ^ toggle_dst_dly;
// dst domain: generate ack sync
logic toggle_ack;
always_ff @(posedge clk_dst or negedge rst_n) begin
if (!rst_n) begin
toggle_ack <= '0;
end else if (pulse_out) begin
toggle_ack <= ~toggle_ack;
end
end
// sync ack back to src domain
sync_2ff sync_ack (
.clk_dst(clk_src),
.rst_n,
.async_in(toggle_ack),
.sync_out(ack_synced)
);
// src domain: wait ack before allowing next pulse
// (omitted for brevity - typically a small FSM)
endmodule
4.5 吞吐量
握手同步器每笔传输需要:
- src → dst:2 个 dst_clk 周期
- dst → src:2 个 src_clk 周期
- 总延迟:约 4~5 个跨时钟周期
5. 异步 FIFO 同步器
5.1 适用场景
需要传输多 bit 数据总线或高速连续数据流时,握手同步器吞吐量不足。异步 FIFO 是最高效的多 bit CDC 方案。
5.2 核心设计要点
| 要点 | 说明 |
|---|---|
| 格雷码指针 | 读写指针跨时钟域传递时必须使用格雷码,确保每次只翻转 1 bit |
| 空/满判断 | 读空:读指针追上写指针;写满:写指针绕一圈追上读指针 |
| 指针同步 | 写指针 → 同步到读时钟域判断空;读指针 → 同步到写时钟域判断满 |
5.3 格雷码编码
| 二进制 | 格雷码 |
|---|---|
| 000 | 000 |
| 001 | 001 |
| 010 | 011 |
| 011 | 010 |
| 100 | 110 |
| 101 | 111 |
| 110 | 101 |
| 111 | 100 |
格雷码性质:相邻值仅 1 bit 变化 → 跨时钟域采样时最多错 1 bit → 错也错成相邻值 → 不影响空满判断正确性。
function automatic logic [WIDTH-1:0] bin2gray(input logic [WIDTH-1:0] bin);
return (bin >> 1) ^ bin;
endfunction
5.4 空满判断
// Full: gray pointers are equal AND MSB differs (wrapped around)
assign full = (wptr_gray == {~rptr_sync[$bits(wptr_gray)-1],
rptr_sync[$bits(wptr_gray)-2:0]});
// Empty: all bits equal
assign empty = (rptr_gray == wptr_sync);
5.5 异步 FIFO 架构

6. MUX 同步器(控制信号重收敛)
6.1 问题场景
当多个独立同步的控制信号在目的域中需要重新组合(如 MUX 选择、算术运算)时,即使每个信号都用 2-FF 同步,各信号到达时间仍可能因布局布线偏差而错开,导致短暂的错误组合。
6.2 问题示例
// ❌ 错误:sel_a 和 sel_b 独立同步后用于 MUX 选择
logic sel_a_sync, sel_b_sync;
sync_2ff sync_a (.async_in(sel_a), .sync_out(sel_a_sync));
sync_2ff sync_b (.async_in(sel_b), .sync_out(sel_b_sync));
assign out = sel_a_sync ? data_a :
sel_b_sync ? data_b : data_default;
// 危险:sel_a_sync 和 sel_b_sync 可能同时为 1(瞬态)
6.3 解决方案
方案 A:数据使能同步(推荐)
不再同步控制信号,而是同步数据使能,将控制逻辑放在 src 域完成:
// src domain: select and register
always_ff @(posedge clk_src) begin
sel_data_valid <= ...; // mux output + valid flag
end
// dst domain: sync valid + 2-ff data
sync_2ff #(WIDTH) sync_data (.async_in(sel_data), .sync_out(sel_data_sync));
sync_2ff sync_valid (.async_in(sel_data_valid), .sync_out(valid_sync));
方案 B:格雷码编码选择信号
若选择信号本身可编码为每次只变 1 bit(如状态机状态、环形计数器),使用格雷码传递。
方案 C:异步 FIFO
若控制信号是命令/数据流的一部分,直接用异步 FIFO 整体传递。
7. 同步器选型对照表
| 同步器类型 | 信号类型 | 多 bit | 吞吐量 | 延迟 | 面积 | 适用场景 |
|---|---|---|---|---|---|---|
| 2-FF | 电平 | ❌ | 高 | 2 clk | 极小 | 单 bit 复位、中断、标志位 |
| 3-FF | 电平 | ❌ | 高 | 3 clk | 小 | 高可靠性单 bit 信号 |
| 握手同步器 | 脉冲 | ✅※ | 低 | 4~5 clk | 中 | 低速脉冲传递 |
| 异步 FIFO | 数据流 | ✅ | 高 | 可变 | 大 | 多 bit 数据总线、连续流 |
| MUX 同步器 | 控制组合 | ✅※ | 中 | 2~3 clk | 小~中 | 目的域需要重组合的信号 |
✅※ 握手同步器和 MUX 同步器在正确设计下可以传递多 bit,但吞吐量和复杂度不同。
8. RTL 编码指南
8.1 DO
// ✅ 专用同步器模块,统一管理
sync_2ff u_sync_intr (
.clk_dst(clk_sys),
.rst_n (rst_n),
.async_in(intr_raw),
.sync_out(intr_sync)
);
// ✅ 格雷码指针用于 FIFO
assign wptr_gray = (wptr_bin >> 1) ^ wptr_bin;
// ✅ 同步器输出仅连接 FF 输入,不直接连组合逻辑
always_ff @(posedge clk_dst)
sync_pulse <= sync_pulse_meta; // meta is the 2nd FF
8.2 DON'T
// ❌ 组合逻辑直接使用同步器第一级输出
assign glitch = sync_meta_ff1 & some_signal; // FF1 仍可能亚稳态
// ❌ 独立同步多 bit 控制信号,认为"各 bit 过 2-FF 就安全"
// 各 bit 可能在不同时钟周期到达,产生短暂错误组合
// ❌ 同步器的复位与数据域混合
// 同步器使用 dst 域复位即可,不需要 src 域参与
8.3 综合/STA 约束
# 将同步器路径标记为 false path(不需要 timing optimization)
set_false_path -from [get_pins sync_2ff_inst/FF1/D] \
-to [get_pins sync_2ff_inst/FF1/Q]
# 跨时钟域分组
set_clock_groups -asynchronous \
-group [get_clocks clk_src] \
-group [get_clocks clk_dst]
总结
CDC 同步器是跨时钟域设计的基石,没有"万能同步器":
- 单 bit 电平信号 → 2-FF 同步器(默认),高频或高可靠 → 3-FF
- 单 bit 脉冲信号 → 握手同步器(toggle + 反馈)
- 多 bit 数据总线 → 异步 FIFO(格雷码指针)
- 控制信号重组合 → 前移控制到 src 域或用 FIFO 整体传递
对于低速设计,2-FF 同步器的亚稳态 MTBF 非常充裕,更需要关注的是:
- 多 bit 数据一致性(必须用格雷码或 FIFO)
- 同步器输出不要直接进入组合逻辑
- STA 中正确设置异步时钟组和 false path
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)