Quartus 时钟约束缺失速查:No clocks defined 与 unconstrained clock 的排查主线
更新时间:2026-10-23。FAQ 速查页,属于 FPGA 工具报错排错系列。Quartus 工程全编译能通过、布线也顺利完成,打开 TimeQuest 一看却满屏红:setup slack 负得离谱,或者干脆报「没有任何时钟定义」。这类问题的本质几乎从不是电路真慢,而是约束没喂对——TimeQuest 只按 .sdc 里声明的时钟做分析,没声明的路径要么不分析、要么按默认 1GHz 目标乱算。本篇给出报错原文、分流图与逐条补齐动作;约束补齐后仍然时序不收敛的情形,思路与 Vivado 时序不收敛怎么办 同构,可以对照。
本文要回答的问题
Warning: No clocks defined in design出现时第一眼看哪里?- .sdc 文件明明写了,为什么 TimeQuest 像没读到一样?
- PLL 输出的时钟为什么需要单独声明?
- 跨时钟域路径的假违例怎么正确豁免?
- 多个 .sdc 并存时谁先谁后、会不会互相覆盖?
一、报错原文与现象
TimeQuest 或全编译日志里的典型原文(对象为示意):
Warning: No clocks defined in design
Critical Warning (332012): Synopsys Design Constraints File file not
found: 'timing.sdc'. A Synopsys Design Constraints File is required
by the TimeQuest Timing Analyzer to get proper timing constraints.
Warning (332060): Node: clk was determined to be a clock but was found
without an associated clock assignment.
Warning (332056): PLL output clocks were found but were not constrained
Info (332126): Derived 1 clock(s) using the derive_pll_clocks command
Warning (332060): Node: sys_pll|pll_inst|clk[0] was determined to be
a clock but was found without an associated clock assignment.肉眼可见的三种表征:一是 Report Clocks 空空如也,report_clocks 一条都没有;二是时钟存在但 Unconstrained Paths 报告里整片红,内部路径被标成 unconstrained;三是布线后时序报告里出现莫名其妙的超紧要求(周期被当成 1ns 量级分析),slack 大面积为负。还有一种更隐蔽的:约束文件在,但其中几条 create_clock 因端口名对不上被静默跳过,TimeQuest 只给一条 Warning 就继续——编译照样「成功」,留下一个半约束的设计。
二、排查决策流程图
先确认 .sdc 是否真被读进来,再按「时钟源头 → 派生时钟 → 跨域豁免」的顺序补,这是唯一不绕路的顺序。
读这张图抓住主线:红色是入口现象,蓝色是三条「确认约束到底读没读、读到哪一层」的诊断动作,橙色是按层的修复动作,绿色是复查通过的判决点,紫色是防复发沉淀。核心纪律只有一条——每改一处都回 report_clocks 与 report_ucp(Report Unconstrained Paths)对拍,不要凭「我觉得写了」收尾。
三、病因速查表
| 现象 / 报错锚点 | 根因 | 一判据 | 处置方向 |
|---|---|---|---|
| Critical Warning 332012 文件找不到 | .sdc 没加进工程或路径错 | .qsf 里搜 SDC_FILE 无对应行 | Project 里 Add File 或手写 .qsf 行 |
| No clocks defined,.sdc 明明在 | .sdc 里 create_clock 全部失配被跳过 | report_clocks 为空且有 332060 | 逐字符核对端口名,改用 get_ports |
| 主时钟有、PLL 输出报 332060 | 派生时钟没声明 | Report Clocks 只有输入时钟 | 加 derive_pll_clocks 或 create_generated_clock |
| 时钟齐全但跨域路径满屏红 | 异步时钟未分组 | 违例路径起点终点在不同 clock 域 | set_clock_groups -asynchronous 分组 |
| 改完 .sdc 时序结果纹丝不动 | 多 .sdc 相互覆盖或顺序错 | .qsf 里 SDC_FILE 出现多行 | 理清读入顺序,先 create 后引用 |
| .sdc 报 can't find object 类错误 | 约束顺序错,引用了还没创建的对象 | TimeQuest 消息窗口的 error 行号 | 把 create_clock 移到引用它的命令之前 |
| 时序约束在 Quartus GUI 里「看得到」但不生效 | 只在 TimeQuest 里交互输入、没存盘 | 重开工程约束消失 | 全部落进 .sdc 文件并登记进工程 |
四、逐条排查
.sdc 没登记进工程。 这是新手第一大坑:文件躺在工程目录里,但 .qsf 里没有 set_global_assignment -name SDC_FILE timing.sdc 这一行,TimeQuest 根本不会读它,编译日志里的 332012 就是铁证。修法:Project → Add/Remove Files in Project 把 .sdc 加进去,或直接编辑 .qsf 补这一行,然后必须重新跑一遍全编译或至少 Update Timing Netlist——TimeQuest 只在时序网表更新时重新吃约束。
create_clock 端口名写错。 约束写的是 create_clock -name sys_clk -period 20.000 [get_ports clk],而顶层端口实际叫 clk_50m——这条命令静默失败,TimeQuest 只在消息窗口给一条 Warning。判据永远是 report_clocks:期望的时钟没出现在清单里就是没匹配上。核对时用 RTL 顶层端口列表逐字符比对,注意大小写、下划线、总线位宽后缀(clk[0] 与 clk 是两个对象)。改进写法是在 .sdc 头部统一用变量收端口名,失配时一眼看到。
PLL 输出时钟未约束。 Quartus 不会自动把 PLL 输出当时钟分析,必须显式派生。最省事的一行是 derive_pll_clocks,它会按 PLL megafunction 参数自动生成全部输出时钟(日志里的 Info 332126 就是它的工作痕迹);配合 derive_clock_uncertainty 把抖动不确定度也补上。要求更精细(比如只约束其中一路、或要改名字供后文引用)时对每路输出单独写 create_generated_clock,源对象指到 PLL 输出引脚。二选一即可,混用会重复定义。
跨时钟域假违例。 时钟都齐了之后最常见的「红」是 CDC 路径:两个异步时钟域之间的路径被按同步关系分析,slack 必然离谱。正确豁免是 set_clock_groups -asynchronous -group {clk_a} -group {clk_b} 整组切断;只有少量已知安全的点对点路径才用 set_false_path -from ... -to ...。注意这和「时序真不收敛」是两回事——分组切干净后剩下的负 slack 才是真问题,那时候走的是布局布线优化路线,参考 Vivado 时序不收敛怎么办 的思路(工具不同、方法论一致)。
约束顺序与多文件覆盖。 .sdc 是 Tcl 脚本,顺序敏感:先 create_clock 创建对象,后面的 set_clock_groups、set_false_path 才能引用它;反了就是 can't find object。工程里挂多个 .sdc 时按 .qsf 登记顺序串行读入,后读的同名时钟定义会覆盖先读的——分模块约束时约定「时钟创建文件在前、豁免文件在后」,或在每个文件头注释声明依赖关系。
只在 GUI 里敲约束没落盘。 在 TimeQuest 控制台里交互输入的命令,关掉工具就没了。所有验证过的约束必须回写进 .sdc 文件并登记工程,否则下次重开工程一切归零——这也是「约束明明做过却又缺失」的经典来源。
五、防复发
- .sdc 与 RTL 同等地位进版本管理,任何时序改动都有 diff 可查;新建工程第一件事就是把 .sdc 登记进 .qsf。
- 编译日志里 grep
3320开头的 Critical Warning/Warning 做成流水线检查项——332012、332060 这两类出现即失败,不许带约束病上线。 - 每次时序签收前固定跑两条命令截图归档:
report_clocks确认清单齐、report_ucp确认未约束路径归零。 - 换器件或改顶层端口名时全局搜一遍 .sdc 里的
get_ports引用,端口改名是约束失效的高发触发点。 - 跨时钟域分组写进工程模板,新模块引入异步接口时同步更新
set_clock_groups,别等红了再补。 - 约束相关的「假编译成功」往往表现为结果随种子漂移,与 Quartus 结果随机不一致速查 互参:先排约束完整性,再谈种子与布局噪声。
这一篇在知识体系里的位置
本篇是 FPGA 工具排错速查簇中「时序约束层」的 Quartus 侧分流页:约束补齐后真不收敛走 Vivado 时序不收敛怎么办 的优化主线;时序结果忽好忽坏、换种子就变脸的情形见 Quartus 结果随机不一致速查,两者合看可以覆盖「约束缺失」与「布局噪声」这两类最容易混淆的时序异常。
一句话总结
Quartus 时钟约束缺失九成不是电路慢而是约束没喂到:先跑 report_clocks 看清单,空了就查 .qsf 有没有登记 SDC_FILE、create_clock 端口名是否逐字符对得上;主时钟齐了再补 derive_pll_clocks 派生 PLL 输出,跨时钟域假红用 set_clock_groups 分组切掉;多 .sdc 注意读入顺序、先创建后引用,所有约束必须落盘进 .sdc 而非只敲在 GUI 里;最后以 report_ucp 未约束清单归零作为签收线,把 report_clocks 归档成每次编译的固定纪律。