一块 FPGA 上跑两三个时钟域是常态:ADC 采样时钟、系统时钟、SerDes 恢复时钟,彼此既不同频又不同相。跨时钟域(CDC)真正要防的只有一件事——亚稳态。触发器在建立/保持窗口内采样到变化中的信号,输出会在一段时间内处于不确定的中间电平,之后随机落到 0 或 1;这个不确定窗口可能被下一级锁存,造成逻辑错误。亚稳态无法彻底消除,只能把失效率压到可接受的水平,MTBF 是衡量它的常用指标,时钟越快、数据翻转越频繁,MTBF 越短。

单比特控制信号的标准做法是两级触发器同步器:第一级吸收亚稳态,第二级输出给使用逻辑。关键是两级之间不加任何组合逻辑,且要对同步寄存器加 ASYNC_REG 这类约束,防止综合工具优化掉它、或把两级寄存器在布局上拉得太远。两级够不够用要看 MTBF 计算结果,高速设计里三级也常见,代价只是多一个时钟周期的延迟。

多比特信号不能逐位同步:各位的亚稳态恢复时间不同,接收端可能采到新旧数据拼成的错误组合。正确做法分三类:一是格雷码,只用于地址、计数器这类每次只变一位的信号,配合异步 FIFO 读写指针;二是握手同步,发送方置位 req,接收方两级同步后采样数据、回 ack,发送方再撤 req,代价是吞吐量低但数据完整,适合配置寄存器、模式切换这类低速控制;三是异步 FIFO,读写指针用格雷码同步,数据走双口 RAM,是高速数据流跨时钟域的首选。

实战里最常见的坑有三个:一是复位信号跨域,必须同步释放、异步复位后同步释放,否则各域复位释放时刻不一致;二是把同步器当成万能药,握手信号本身没做两级同步就直接用;三是仿真全过就以为安全——亚稳态是概率事件,仿真基本抓不到,必须靠 CDC 工具做结构检查,确认每个跨域信号都走了同步器、没有组合逻辑混在同步路径里。多时钟设计里,CDC 检查应当和时序收敛同等对待,放在每次综合后必跑的清单里。

还有一个容易被忽略的环节是 CDC 与功能验证的分工:仿真阶段可以用断言检查握手协议的时序关系,比如 req 置位后 ack 必须在限定周期内返回、数据在握手完成前不得变化;门级仿真则能暴露综合后插入的缓冲器是否破坏了同步路径。量产阶段,MTBF 的计算要按最坏的时钟频率和数据翻转率取数,留出至少一个数量级的余量——亚稳态失效是小概率事件,一旦发生在客户现场,定位成本远高于前期多加一级同步器的代价。