OpenSTA 时序起点无时钟警告:unclocked pins 与 unconstrained endpoints 排查
静态时序分析以时钟为起点:没有时钟,工具就不知道数据路径该在多长时间内走完,于是寄存器时钟引脚成为 unclocked register/latch pins,端点成为 unconstrained endpoints。OpenSTA 时序起点无时钟警告最坑的一点是——这些只是 Warning,sta 退出码照样为 0,而默认 report_checks 只回一句 No paths found.,绿灯感十足。本文在 OpenSTA 2.0.17 上复现三种 create_clock 漏约束(完全不建、建错对象、名字对不上),再修复到 check_setup 全静、slack MET。无约束路径的审计方法见 OpenSTA unconstrained endpoint 排查;连一个时钟对象都没有的更极端情形见 OpenSTA no clocks found。
本文要回答的问题
unclocked register/latch pins与unconstrained endpoints两条 Warning 各是什么意思?- 为什么默认
report_checks只说No paths found.,问题路径要怎么才能看到? create_clock建在内部引脚u_a/C而不是输入端口clk,约束为什么只生效一半?- 时钟取名
sys_clk,SDC 后面却用-clock clk,哪些约束静默丢失? - 修复后应该看到哪些输出,才能确认时序路径真的被分析了?
这张漏斗对应实验的四个阶段。前两层红色是同一件事的两面:check_setup 点名时钟缺失,普通路径报告却“查无此路”。橙色是名字不匹配导致的部分失效,绿色是修复基线。读图时记住——OpenSTA 不会因为 Warning 而返回非零退出码,四个用例的 CASE_EXIT 全部是 0。
一、警告与路径报告的原始输出
实验设计极小:两个 DFF(u_a、u_b)经一级 BUF 串联,时钟输入端口 clk,数据输入 d、输出 q,外加一个虚构的最小 liberty 时序库。下面的终端卡是诊断脚本 diag.tcl 的对照输出——建钟前后各跑一次 check_setup:
建钟前四条警告含义各不相同:2 input ports missing set_input_delay 与 1 output port missing set_output_delay 是 IO 约束缺失,与时钟无关;2 unclocked register/latch pins 指 u_a/C、u_b/C 两个时钟引脚没有时钟驱动;3 unconstrained endpoints 则把两个寄存器输入端和输出端口都算作没有时序要求的终点。在端口 clk 上执行 create_clock -name clk -period 1.0 [get_ports clk] 之后,unclocked 警告整条消失,unconstrained 从 3 降到 2(剩下的是尚未写 set_input_delay/set_output_delay 的两个 IO 端点)。这就是“时钟是时序起点”最直接的证据。
case1 演示完全不建钟时的误导性。默认 report_checks 打印 No paths found.,但加了 -unconstrained 之后,u_a→u_b 的真实路径完整列出,结尾明写:
Startpoint: u_a (rising edge-triggered flip-flop)
Endpoint: u_b (rising edge-triggered flip-flop)
Path Group: (none)
0.050 0.150 0.350 ^ u_buf/Y (BUF_X1)
0.050 0.000 0.350 ^ u_b/D (DFF_P)
0.350 data arrival time
-------------------------------------------------------------------
(Path is unconstrained)Path Group: (none) 加 (Path is unconstrained) 意味着工具算出了 0.350ns 的到达时间,却没有任何捕获沿去减它——slack 不存在,而非 slack 为正。这与“时序收敛”毫无关系。无约束端点的完整审计口径见 report_checks 全 unconstrained 指南。
二、三种漏约束的对照
| 用例 | SDC 写法 | check_setup 关键警告 | 路径报告 | 后果 |
|---|---|---|---|---|
| case1 完全不建钟 | 无 create_clock | 2 unclocked pins,3 unconstrained endpoints | 受约束组 No paths found.;u_a→u_b 落在 -unconstrained | 全部寄存器路径未分析 |
| case2 建错对象 | create_clock ... [get_pins u_a/C] | 1 unclocked pin(u_b/C),3 unconstrained endpoints | u_a 显示 clocked by clk,u_a→u_b 仍 unconstrained | 时钟源被放进芯片内部,只覆盖一半时序弧 |
| case3 名字不符 | 建钟名 sys_clk,后用 -clock clk | clock 'clk' not found 两条;unclocked 消失 | 路径组 sys_clk 正常分析,slack MET 0.55 | 内部路径有约束,IO 延迟约束全部丢失 |
| fixed 修复 | 建在 [get_ports clk],IO 约束引用同名时钟 | check_setup 无输出 | Path Group: clk,slack (MET) 0.550 | 全路径受约束 |
case2 最值得细讲。时钟对象确实建成了(CLOCK=clk,SOURCE=u_a/C),但时钟源被定义在芯片内部的寄存器时钟引脚上:u_a 的时钟引脚被当成理想时钟源,所以它自己显示 clocked by clk;可时钟不会穿过一个时序单元的 C 引脚往上游传播到输入端口,也不会按真实时钟网络那样扇出到 u_b——u_b/C 仍然 unclocked,报告里 u_a→u_b 依旧 (Path is unconstrained)。create_clock 的合法落点是时钟真正进入设计的地方:顶层输入端口(或 PLL 之后的生成时钟节点用 create_generated_clock)。
case3 则是另一半失效。时钟建在端口上且名字叫 sys_clk,寄存器到寄存器路径完全正常,报告给出 Path Group: sys_clk、slack MET 0.550ns;但紧接着两行 Warning:
Warning: case3_namemismatch.tcl, 6 clock 'clk' not found.
Warning: case3_namemismatch.tcl, 8 clock arg 'clk' clock not found.第 6 行的 get_clocks clk 取不到对象,第 8 行的 set_input_delay -clock clk 0.2 [all_inputs] 整条被丢弃——工具不会报错中止,CASE_EXIT 仍是 0。后果是输入端口 d 没有输入延迟约束,输出路径的对齐要求也缺失。内部路径 MET 不代表 SDC 全部生效:只要 Warning 里出现 clock ... not found,就要按行号回查那一条约束引用的时钟名。
三、根因:时钟是时序路径的两个锚点
读这张图抓住两个锚点:发射沿在 u_a/C、捕获沿在 u_b/C,两者都来自同一个从 clk 端口进入的时钟。缺了时钟对象(case1),两个锚点都不存在;时钟源挪到 u_a/C(case2),捕获沿锚点悬空;只有时钟名对不上(case3),锚点都在、但挂在时钟名上的 IO 约束挂空。修复后 1.0ns 周期减去 0.10ns 库建立时间得到 0.900ns 要求时间,再减 0.350ns 到达时间,正是报告里的 slack (MET) 0.550。
四、排查清单
- 每个 SDC 版本先跑
check_setup,并把unclocked register/latch pins当作必须清零的项;不要用 sta 退出码判断成败——Warning 下退出码恒为 0。 - 出现 unclocked pins 时,先
all_clocks确认时钟对象是否存在,再用get_property $c sources打印每个时钟的源对象:必须是顶层输入端口(或生成时钟节点),不能是寄存器内部引脚。 - 默认
report_checks回No paths found.时,不要据此认为“没有违规路径”;紧跟一条report_checks -unconstrained,路径在那里出现就是漏约束而非真收敛。 - 见到
clock '<name>' not found,按 Warning 给出的文件行号回查:是建钟的-name拼错,还是set_input_delay/set_false_path等引用了不存在的时钟名。 - 修复判据三条同时满足:check_setup 中 unclocked/clock 相关警告为零(IO delay 警告按工程需要补齐)、
report_clock_properties显示预期周期与波形、路径报告出现Path Group: <clock>和带符号的 slack。
| 看到的输出 | 直接含义 | 处理 |
|---|---|---|
N unclocked register/latch pins | N 个时钟引脚无时钟到达 | 补/修 create_clock 的源对象 |
N unconstrained endpoints | N 个端点无时序要求 | 先查时钟,再查 IO delay 与例外约束 |
默认报告 No paths found. | 受约束组为空 | 改跑 report_checks -unconstrained |
clock 'x' not found | 按名引用的时钟不存在 | 对齐 -name 与引用处拼写 |
Path Group: (none) + (Path is unconstrained) | 路径未被分析 | 时钟网络未覆盖该路径 |
五、修复 SDC 与复跑确认
修复脚本把时钟建回它进入设计的位置,并让 IO 约束引用同一个名字:
create_clock -name clk -period 1.0 [get_ports clk]
set_input_delay -clock clk 0.2 [get_ports d]
set_output_delay -clock clk 0.0 [get_ports q]复跑后 ALL_CLOCKS_COUNT=1,report_clock_properties 显示 clk 1.00 0.00 0.50,CHECK_SETUP_BEGIN/END 之间再无任何行,u_a→u_b 报告落在 Path Group: clk,数据到达 0.350ns、要求 0.900ns、slack (MET) 0.550。运行命令:
docker run --rm -v "$PWD":/work -w /work geek-eda:0.3-sta bash run.sh脚本顺序执行 diag、case1–case3 与 fixed,五个场景的 CASE_EXIT 都是 0——这恰好是本篇要留下的教训:OpenSTA 的警告与退出码是两套体系,约束完整性只能靠检查报告内容把关。
代码获取:极简 RTL、最小 liberty 库、五个 TCL 场景(diag/case1–case3/fixed)与一键复现脚本位于公开仓
experiments/ts-opensta-no-clock-startpoint/。执行bash run.sh后,case1/case2 应见 unconstrained 路径、case3 应见两行clock not found、fixed 段应见slack (MET) 0.550。
实验环境
- 机器/系统:Linux x86_64 Docker 容器 / Ubuntu 24.04(镜像 geek-eda:0.3-sta)
- 工具版本:OpenSTA 2.0.17(Debian 官方包)
- 日期:2026-11-02
- 完整命令:
bash run.sh这一篇在知识体系里的位置
本篇处理“时钟没建对”这一类约束缺失。OpenSTA no clocks found处理设计中连时钟对象都不存在的更极端报错,OpenSTA unconstrained endpoint 排查与无约束端点审计方法给出端点级审计流程,false_path 匹配不到则是例外约束按名失配的姊妹问题——表现同为 Warning 不中止、约束静默失效。
总结
OpenSTA 时序起点无时钟警告要按三条线索联读:check_setup 的 unclocked register/latch pins 指出时钟未到达的寄存器引脚,默认 report_checks 的 No paths found. 要用 -unconstrained 戳破,clock 'x' not found 则按行号追查时钟名失配。根因都是 create_clock 没建在时钟真正进入设计的输入端口(或建在内部引脚、或名字与后续 SDC 引用不一致);修复判据是 check_setup 无时钟类警告且路径报告出现带 Path Group 的 MET slack,而不是 sta 的退出码。