Quartus 编译结果时好时坏速查:同一份代码这次过时序下次不过
更新时间:2026-10-23。FAQ 速查页,属于 FPGA 工具报错排错系列。最让人怀疑人生的 Quartus 现象不是稳定报错,而是「不稳定」:同一份代码、同一台机器,这次全编译时序过了,下次又红了;比特流上板有时跑几天正常,有时上电就抽风。本篇先教你在「编译器结果波动」和「电路本身带病」之间做一次分流,再逐条给出可复现、可对比的排查动作。编译过程本身长时间无输出的见 Quartus 编译卡死速查,布线收敛的通用打法可对照 Vivado 时序不收敛速查(约束思想两家通用)。
本文要回答的问题
- 「这次过下次不过」到底是 Quartus 的随机性,还是我的设计本来就在时序临界线上?
- 固定 seed 之后结果稳定了,能说明设计没问题吗?
- 哪些约束缺失会让 Fitter 每次按不同目标优化,从而结果忽好忽坏?
- 上板「有时正常有时异常」,怎么区分是编译波动还是异步 CDC 的偶发故障?
- 用什么流程把「玄学 flaky」变成「可复现、可对比、可修」的普通 bug?
一、现象与两类病因的分流
Quartus 的「时好时坏」在临床上只有两大类,处理思路完全不同,先分流再动手:
A 类:编译结果 flaky(比特流之间的差异)。 同一 RTL、同一约束,多次全编译得到的时序报告不一样:这次 WNS(最差负裕量,Worst Negative Slack)为 0 通过,下次 WNS -0.3ns 失败。本质是设计的时序裕量贴着 0 线,Fitter 的布局布线带随机性(初始 seed、多核并行调度的细微顺序差异、增量编译叠加的历史状态),每次落点都在临界线两侧随机游走。这是设计裕量不足或约束不完整,不是工具坏了。
B 类:电路行为 flaky(同一比特流上板表现不一致)。 同一个 .sof 文件,同一块板子,有时上电正常、有时功能错乱、重新上下电又好了。编译器只是替罪羊——真凶多是异步信号跨时钟域(CDC,Clock Domain Crossing)没有做同步或没加约束,亚稳态命中概率随温度、电压、批次波动;也可能是复位释放时序、电源斜坡、外部时钟抖动等硬件因素。这类问题换 seed 重编译一百次也修不好,必须回到电路与约束本身。
一句话分流判据:把上次「好」的比特流和这次「坏」的比特流分别烧同一块板子,行为跟着比特流走的是 A 类;同一比特流自己时好时坏的是 B 类。 这个对照实验成本最低、信息量最大,务必先做。
二、现象 → 区分点 → 处置速查表
| 现象 | 区分点 / 最常见原因 | 处置 |
|---|---|---|
| 多次编译时序在通过线两侧摆动 | 时序裕量 ≈ 0,Fitter 随 seed 在临界线上波动 | 固定 seed 复现,确诊后加流水线/优化逻辑把裕量做正,而不是换 seed 赌运气 |
| 同一 RTL 换台机器结果不同 | 多核并行编译的微小不确定性(processor count 影响布局) | 可接受范围内属正常;以固定机器+固定 seed 的签核结果为准 |
| 约束文件缺 create_clock,报告里时钟频率不对 | 缺主时钟约束,Fitter 按默认/错误目标优化,每次侧重不同 | 补全所有时钟的 create_clock/create_generated_clock,重跑看 Timing Analyzer 时钟列表 |
| 异步输入口(按键、外部串行信号)偶发误动作 | CDC 未同步,亚稳态直接进逻辑 | 输入打两到三级同步触发器;属 B 类,与编译无关 |
| 两个异步时钟域之间有数据交互但未声明 | 缺 set_false_path/set_clock_groups,Fitter 白费力去收根本收不敛的路径,结果随机 | 对异步时钟组加 set_clock_groups -asynchronous,数据通路走正规 CDC 结构 |
| 上电偶尔不工作,重上电又正常 | 复位释放时序或电源/时钟稳定窗口问题,被误判为编译 flaky | 复位释放同步到各时钟域;示波器看电源斜坡与时钟起振,属 B 类硬件轴 |
| 增量编译后时序突变,清库重编又好了 | 增量编译叠加历史分区状态 | 重大修改后删掉 db/incremental_db 做一次干净全编译再签核 |
| Timing 报告路径终点是异步域寄存器 | 工具仍在优化不该优化的跨域路径,挤占真正关键路径资源 | 声明 false path 后裕量通常立即改善且稳定 |
三、排查决策流程
分诊只问一个问题:同一个比特流自己稳不稳。答案把现场切成「编译器波动」和「电路带病」两条通道:
读这张图抓住分流点:红色是入口现象与两类病因,蓝色是排查动作,橙色是修复,绿色是验证收口,紫色是防复发纪律。 先做蓝框那个对照实验再往下走,方向选错的话,A 类病人吃 B 类药(狂加同步触发器救不了负裕量)和 B 类病人吃 A 类药(狂换 seed 救不了亚稳态)都是白费功夫。
四、逐条排查展开
A 类第一步:固定 seed,把「随机」变成「可复现」。 Quartus Fitter 的初始布局种子决定整次布局布线的随机走向,QSF 里一行固定:
set_global_assignment -name SEED 1固定 seed 后同一工程连编三次,时序报告应完全一致;再换 seed 1..5 各编一次,把五次 WNS 摆在一起看。若五次里时而过时而不时,确诊:设计时序裕量贴线。注意固定 seed 只是让问题可复现、可对比的诊断手段,不是修复——seed 1 恰好通过不代表设计健康,只代表你赌赢了一次。
A 类第二步:用 report_timing 对比,确认波动落在哪些路径上。 TimeQuest 里拉出最差路径明细:
report_timing -setup -npaths 20 -detail full -file timing_seed1.rpt对比不同 seed 的报告:如果最差路径名单每次都换、且全是些正常同步路径,说明整体裕量薄,Fitter 顾此失彼;如果最差路径稳定集中在某几个时钟对之间,顺着时钟名去查约束。
A 类第三步:查约束完整性,这是「忽好忽坏」最常见的结构性根因。 缺约束时 Fitter 的优化目标是错的、而且每次编译侧重都不同。最小闭环:
create_clock -name sys_clk -period 8.000 [get_ports sys_clk]
create_generated_clock -name pll_clk -source [get_pins pll|inclk] [get_pins pll|outclk]
set_clock_groups -asynchronous -group {sys_clk} -group {uart_clk}打开 TimeQuest 的 Report Clocks,逐个核对:每个进设计的时钟是否都有 create_clock?PLL 输出是否都有 generated clock?异步时钟对之间是否声明了 clock groups 或 false path?任何一项缺失,Fitter 要么按错误频率优化,要么浪费精力去收根本收不敛的异步路径——这两种情况的表现都是「结果随编译漂移」。把 false path 补齐后裕量立即改善且稳定,是最典型的确认信号。
B 类排查:找异步入口,SignalTap 抓现场。 所有从芯片引脚进来的、与内部时钟无固定相位关系的信号(按键、外部总线、串行口、其他板卡来的数据),进入逻辑前必须经过两到三级同步触发器;多比特总线跨域必须走握手或异步 FIFO,不能逐 bit 各自打拍。电路里漏掉任何一条,亚稳态就以「偶尔出错、重上电又好了」的形式出现。用 SignalTap 把触发条件设在故障现象出现的信号组合上,抓到一次现场胜过猜一百次。同时别忽略硬件轴:复位信号建议异步复位、同步释放到每个时钟域;偶发故障如果集中在冷启动,拿示波器看电源斜坡和上电时序,排除硬件再回头怀疑编译。
增量编译的坑。 增量编译(Incremental Compilation / 设计分区)保留上次布局以省时间,但多次叠加后历史分区状态可能把时序拖入坏角落。任何一次「清库全编就好了」的经历都指向这里。纪律:日常迭代可以用增量,签核版本必须删掉 db/ 与 incremental_db/ 后做一次干净全编译。
五、防复发与纪律
- 裕量做正,不靠赌 seed:把「换 seed 找到能过的那一次」从工作流里剔除;裕量贴线的设计用加流水线、降逻辑级数、优化关键路径把 WNS 做出正余量。
- 约束评审制度化:SDC 文件与 RTL 同级评审;新时钟、新异步接口进设计时同步补 create_clock 与 set_clock_groups,约束不完整不允许进全编译。
- 签核固定条件:签核版本固定机器、固定 Quartus 版本、固定 seed,编译结果与 QSF、SDC 一起存档,可追溯可复现。
- CDC 审查入流程:异步输入同步化、跨域走 FIFO/握手、false path 声明齐全,作为设计 checklist 的固定三项。
- 上板异常先怀疑电路再怀疑编译:同一比特流自身不稳的一律按 B 类查,不重复编译碰运气。
这一篇在知识体系里的位置
本篇是 FPGA 排错速查簇中「结果不确定」节点的 FAQ 页:编译过程长时间无输出见 Quartus 编译卡死速查,布线不收敛的通用收敛打法对照 Vivado 时序不收敛速查。约束与 CDC 的原理性内容建议回到 FPGA 系列的时序约束与调试基础章节系统补齐。
一句话总结
Quartus 编译结果时好时坏先做一次对照实验分流:行为跟着比特流走的是 A 类「编译器在临界时序上随 seed 波动」,固定 seed 复现、report_timing 对比、补齐 create_clock 与 set_clock_groups 约束并把裕量做正;同一比特流自身就不稳的是 B 类「电路带病」,查异步 CDC 同步、false path 声明与复位释放,用 SignalTap 抓故障现场——两类都修好后,靠签核固定 seed 存档、约束评审和 CDC 审查入 CI 防复发。