4. CDC同步器结构与行为

跨时钟域(CDC)问题的本质,是信号在源时钟域被采样、在目的时钟域被再次采样时,两者没有固定相位关系,导致目的域触发器可能进入亚稳态。同步器的作用不是"消除"亚稳态,而是给亚稳态足够的时间衰减,并把单比特控制信号安全地传递到目的域。本节围绕五类最常用的同步结构,从RTL实现到仿真波形观察逐一展开。

一、两级触发器同步器:最基础的单比特电平同步

两级触发器同步器适用于单比特、电平型、变化缓慢的信号。其结构是目的时钟域串联两个触发器,第一级可能进入亚稳态,第二级在其后一个周期再采样,使亚稳态在到达组合逻辑前已衰减到稳定值。

// 两级同步器(目的域 clk_b)
reg sync_meta, sync_out;
always @(posedge clk_b or negedge rst_n)
  if (!rst_n) {sync_out, sync_meta} <= 2'b00;
  else        {sync_out, sync_meta} <= {sync_meta, sig_a};

关键约束:源信号 sig_a 的脉宽必须大于目的时钟周期,否则可能被完全漏采。仿真观察时,在 sig_a 上升沿后,sync_meta 先变化,sync_out 再延迟一个 clk_b 周期出现,这就是两级同步带来的固定延迟。若源信号是窄脉冲,两级同步器会失效,必须改用脉冲同步器。

二、握手同步:多比特数据的可靠传递

当需要传递多比特数据(如配置寄存器、状态字)时,不能对每一位分别做两级同步,因为各位的亚稳态衰减时间不同,会导致目的域采到"半新半旧"的错误组合。握手同步通过"请求—应答"机制解决:数据在源域保持稳定,仅用单比特请求信号跨域,目的域确认收到后回送应答。

典型流程:源域把数据放到 data_a 并拉高 req_a;req_a 经两级同步进入目的域得到 req_b_sync;目的域在 req_b_sync 有效时锁存数据并拉高 ack_b;ack_b 再经两级同步回源域,源域收到后撤销 req_a。整个往返至少需要 4~6 个目的时钟周期,吞吐率低但绝对可靠。仿真中应重点观察 req_a 与 ack_b 的握手时序,以及数据在 req_a 有效期间是否保持不变。

三、异步FIFO:大批量数据的跨域缓冲

异步FIFO是吞吐率最高的CDC方案,读写端口分别工作在两个时钟域,通过双端口RAM和格雷码指针实现。核心设计点有三:

  • 指针用格雷码:相邻地址只变一位,跨域同步时最多只有一位处于亚稳态,避免多位同时跳变导致指针错乱。
  • 空满判断:读指针同步到写域判断"满",写指针同步到读域判断"空",同步本身有延迟,因此判断结果偏保守——可能误报满/空,但不会误报非空/非满,保证安全。
  • 深度选择:深度需覆盖最坏情况下的同步延迟与突发长度,常用公式为 深度 ≥ 突发长度 + 同步延迟周期数。

仿真观察时,向写端口连续写入数据,读端口以不同频率读出,检查 full、empty 标志是否在正确时机拉高,以及读出数据顺序是否与写入一致。若发现数据错乱,优先检查格雷码转换是否正确、空满判断是否用了同步后的指针。

四、脉冲同步器:窄脉冲的跨域传递

脉冲同步器用于把源域的窄脉冲(可能只有一个源时钟周期宽)传递到目的域。其原理是:源域用脉冲触发一个翻转触发器(toggle),该翻转信号是电平型、变化缓慢,可安全经过两级同步进入目的域;目的域再对同步后的翻转信号做边沿检测,还原出脉冲。

// 源域:脉冲转电平
always @(posedge clk_a) if (pulse_a) toggle_a <= ~toggle_a;
// 目的域:两级同步 + 边沿检测
always @(posedge clk_b) begin
  sync1 <= toggle_a; sync2 <= sync1; sync3 <= sync2;
  pulse_b <= sync2 ^ sync3;   // 检测跳变
end

约束:两个源脉冲之间的间隔必须大于目的域同步延迟(通常 2~3 个目的周期),否则会丢失脉冲。仿真中应构造不同间隔的脉冲序列,观察 pulse_b 是否与 pulse_a 一一对应。

五、MUX同步器:带使能的单比特同步

MUX同步器在两级触发器前增加一个多路选择器,用使能信号控制是否采样。当数据稳定且使能有效时才把数据送入同步链,避免在数据变化期间采样。它常用于"数据+有效"总线的跨域,使能信号本身也需同步。相比握手同步,MUX同步器延迟更小,但要求数据在使能有效窗口内保持稳定。

五类CDC同步器结构与信号流对比 ① 两级触发器同步器 FF1 FF2 sig_a → FF1 → FF2 → sync_out 适用:单比特电平信号 ② 握手同步 源域 目的域 req ack data 保持稳定,单比特握手 适用:多比特数据、低速 ③ 异步FIFO 写域 读域 双口RAM 格雷码指针 + 空满判断 适用:大批量数据、高吞吐 ④ 脉冲同步器 脉冲→翻转 两级同步 边沿检测 pulse_a → toggle → sync → pulse_b 适用:窄脉冲跨域,间隔需足够 ⑤ MUX同步器 数据+使能 MUX选择 两级同步 使能有效时采样,数据需稳定 适用:带有效标志的总线跨域 选型速查:按信号类型与吞吐需求匹配 单比特电平 → 两级触发器 多比特低速 → 握手同步 大批量高吞吐 → 异步FIFO 窄脉冲 → 脉冲同步器 带有效总线 → MUX同步器

六、仿真行为观察要点

在Xcelium中搭建上述同步器时,建议统一采用以下观察方法:

  • 时钟频率比:设置源域 100MHz、目的域 33MHz(约3:1),便于观察同步延迟与漏采风险。
  • 亚稳态建模:普通RTL仿真不会产生亚稳态,可用 $random 在同步第一级注入随机延迟,或使用Xcelium的 -xmsim 亚稳态建模选项,观察第二级是否稳定。
  • 断言检查:对握手同步添加"req有效期间data不变"的断言;对异步FIFO添加"full时不再写入、empty时不再读出"的断言。
  • 覆盖率:关注跨域信号在目的域采样窗口内的跳变覆盖率,确保测试激励覆盖了最坏相位关系。
常见错误:把多比特数据直接接两级同步器。由于各位亚稳态衰减时间不同,目的域可能采到新旧混合值。多比特必须走握手、异步FIFO或格雷码编码路径。
选型口诀:单比特电平用两级,多比特低速用握手,大批量高吞吐用异步FIFO,窄脉冲用脉冲同步器,带有效标志的总线用MUX同步器。任何同步器都不能消除亚稳态,只能把MTBF提升到可接受水平。