跨时钟域只打两拍,仿真干净、上板偶发挂死,这类问题在 Verilog 工程里很常见。本文按排错实录走一遍:复现现象、列排查清单、定位根因,最后给出能验收的修法。读完你应能判断自己的两级同步器到底缺了哪一环,并在仿真里复现一次失败、修好后波形稳定。跨时钟域排错的关键是先确认同步器输出有没有被组合逻辑消费。
现象复现
环境:Vivado 或 Quartus 以你本机安装版本为准,仿真器用工具自带仿真或 ModelSim/Questa。被测设计是一个 100 MHz 域产生的单比特使能 en_src,送进 75 MHz 域,接收侧只写了两级触发器:en_sync1 <= en_src; en_sync2 <= en_sync1;,再用 en_sync2 去使能一个 8 位计数器。
行为仿真全过,综合实现后时序报告也没有 setup 违例。但上板跑几小时,计数器偶尔多跳一次;把接收域时钟改成 200 MHz 后,出错频率明显上升。抓 ILA 能看到 en_sync2 出现一个极窄的毛刺,下游组合逻辑把它当成了有效使能。部分工程还会在综合日志里看到类似警告:
[Synth 8-327] inferring latch for variable ‘en_sync2_reg’
[Timing 38-282] The design failed to meet the timing requirements.
排查清单
按优先级从高到低查,每条都给出「查什么 → 期望看到什么」,别跳步。
- 确认同步的是单比特还是总线 → 查被跨域信号声明。期望:只有 1 bit 控制信号走两级触发器;多位数据必须走异步 FIFO 或握手,不能每 bit 各打两拍。
- 查两级触发器是否被综合优化或合并 → 看综合后网表与日志有无 inferring latch、寄存器被 constant 传播。期望:两级 always @(posedge clk) 都保留,且第二级输出直接进下游,不被工具折叠。
- 查同步器输出是否又做了组合逻辑 → 在 RTL 里搜 en_sync2 & …、if (en_sync2 | …)。期望:en_sync2 只作为寄存器输入或时钟使能,不参与组合判决。
- 查源端信号是否本身就是毛刺 → 回源域看 en_src 由哪条组合逻辑生成、是否跨了多个 always 块。期望:源端使能由单时钟域寄存器直接输出,脉宽至少覆盖一个源时钟周期。
- 查约束是否把跨域路径当同步路径分析 → 打开时序约束文件,找 set_false_path 或 set_max_delay -datapath_only。期望:跨域起点到同步器第一级有明确约束,工具不再对它报 setup 违例。
- 查复位与时钟使能是否破坏了同步链 → 看两级触发器是否共用异步复位、是否有 if (!rst_n) 夹在中间。期望:同步链复位释放同步于接收时钟,两级之间无额外门控。
- 复现常见报错并定位 → 若日志出现 [Timing 38-282],先确认跨域路径是否被误当同步路径;若出现 latch 推断,检查 always 块是否漏了 else。期望:改完后两类告警都消失。
- 下一步最小扩展 → 把单比特使能改成脉冲同步或握手,再跑一次回归。期望:新结构在 200 MHz 接收域下仍无多跳。
根因与修复
最可能的根因不是「两拍不够」,而是同步器输出被组合逻辑直接消费,加上源端使能本身是组合毛刺。两级触发器只能降低亚稳态概率,不能消除毛刺;一旦 en_sync2 进了与门、或作为多路选择器的条件,窄脉冲就会被放大成功能错误。这个根因优先查,是因为它同时解释「仿真过、上板偶发」和「接收时钟越快越容易错」两个现象。
常见的误排路径:只盯着亚稳态概率算 MTBF,反复加第三级、第四级触发器。级数增加对毛刺无效,反而掩盖了组合消费的问题,浪费一轮改版。
修复按下面步骤改,改完再综合:
- 把源端 en_src 改成寄存器直接输出,确保脉宽不小于一个源时钟周期;组合生成的使能先打一拍再跨域。
- 同步器单独成模块,两级触发器之间不加任何逻辑,输出命名为 en_sync,只允许接寄存器输入或时钟使能端口。
- 下游若需要边沿检测,在接收域用 en_sync 再打一拍做 en_sync & ~en_sync_d,不要回到组合逻辑里做。
- 在约束文件里对跨域起点到同步器第一级加 set_false_path 或 set_max_delay -datapath_only,避免工具按同步路径报违例。
- 多位数据场景直接换异步 FIFO 或握手,不要给每 bit 各打两拍。
- 重跑综合实现,确认 [Synth 8-327] 与 [Timing 38-282] 都不再出现,再上板长跑。
// 最短修复片段(接收域):
always @(posedge clk_dst or negedge rst_n) begin
if (!rst_n) begin
en_sync1 <= 1'b0;
en_sync2 <= 1'b0;
end else begin
en_sync1 <= en_src; // 第一级,允许亚稳态
en_sync2 <= en_sync1; // 第二级,输出只进寄存器
end
end
assign en_pulse = en_sync2 & ~en_sync2_d; // 边沿检测也在寄存器域完成
复发时快速定位:先看 ILA 里 en_sync2 是否出现窄于一个接收时钟周期的脉冲;若有,回查它是否被组合逻辑消费。再看源端使能脉宽是否小于一个源时钟周期。两步就能把问题锁在「毛刺」还是「亚稳态」上,不必重新算 MTBF。
验收
- 行为仿真中人为给 en_src 加一个窄脉冲,修复后下游计数器不再多跳。
- 综合日志无 latch 推断,时序报告无跨域 setup 违例。
- ILA 抓 en_sync2,脉宽均不小于一个接收时钟周期。
- 接收时钟提到 200 MHz 长跑,计数器计数与源端发送次数一致。
- 约束文件中跨域路径有明确 false path 或 max delay,复查通过。
迷你 FAQ
Q:卡在哪一步最常见?A:先查同步器输出有没有进组合逻辑,再查源端脉宽,最后才看约束和级数。Q:怎么证明自己做对了?A:看综合日志无 latch 推断、时序报告无跨域 setup 违例、ILA 抓 en_sync2 脉宽均不小于一个接收时钟周期。Q:没有板子能练什么?A:用仿真给跨域信号注入窄脉冲和随机延迟,观察两级触发器输出,纯 HDL 就能复现毛刺被放大的过程。
想系统学习 FPGA 并就业,可了解成电国芯 FPGA 就业实战班,含零基础教学、企业项目、就业内推。课程链接:https://admin.shaonianxue.cn/ 地址:请在后台填写 电话:请在后台填写

评论 0