PrimeTime 引用无法解析:get_clocks 空匹配与前向引用速查
更新时间:2026-10-22。FAQ 速查页,属于 EDA 工具排错系列。PrimeTime(简称 PT,Synopsys 的静态时序分析与签收工具,交互壳是
pt_shell)报「引用无法解析」时,本质和综合工具一样:它在名字表里查不到你写的那个对象。但 PT 的坑更阴——对象来自门级网表而不是 RTL,SDC 命令对空集合往往只给一条 Warning 就继续往下跑,于是一条没解析掉的引用会变成报告里一片没人管的 unconstrained 路径,而不是干脆失败。综合侧的同款机理见 Design Compiler 引用无法解析,约束缺失视角见同系列 PrimeTime 时钟/约束缺失,本篇只盯「名字为什么解析不到」这一件事。
本文要回答的问题
- pt_shell 报
Can't resolve reference to clock ...,SDC 在综合阶段明明好好的,问题出在哪? get_clocks/get_pins返回空集合,PT 为什么不直接报错中止?- generated clock 引用 master clock 报「找不到」,是名字错还是顺序错?
- 综合后层次名、寄存器名变了,SDC 里的 RTL 路径怎么改才能对上?
- 名字里带
[、/、通配符时,哪些写法会被 Tcl 或层次分隔符提前吃掉?
一、报错原文与现象
引用解析失败在三个时点冒头:SDC 加载瞬间、约束命令执行时、check_timing 汇总时。典型形态有三种。
形态一,generated clock 引用了尚不存在的 master clock:
pt_shell> source top.pt.sdc
Error: Can't resolve reference to master clock 'SYS_CLK' in
'create_generated_clock'. (UID-89)
Warning: Generated clock 'CLK_DIV2' was not created. (CLKN-501)形态二,对象查询返回空集合,SDC 命令静默打在空集上:
Warning: An sdc collection for '-to' evaluated to an empty
collection. (UITE-379)
Warning: No object matched 'get_pins u_core/u_dma/reg_bank*/Q'. (NED-204)
Warning: 'all_clocks' was evaluated when no clocks were defined. (UID-12)形态三,Tcl 侧的名字解析问题——包、命名空间、转义:
Error: invalid command name "::myproj::derive_mode"
while executing "derive_mode func_ss"
Error: can't read "ports(": no such variable
while executing "get_ports data[0]"判读口径:UID 类管标识符解析(时钟、对象名找不到),NED 类管网表对象查询为空,UITE 类是 SDC 命令收到空集合,UCO 类(unconstrained objects)则是前面这些失败在时序覆盖上的总爆发。各版本的编号数字会变,排查时以检查项名称(empty collection、master clock not found、no object matched)为准,不要背编号。关键认知:形态二全是 Warning,PT 照样出报告——引用解析失败在签收阶段最常见的结局不是脚本挂掉,而是绿灯假签收。
二、报错 → 原因 → 处置速查表
| 报错/现象 | 最可能原因 | 一条命令确认 | 处置 |
|---|---|---|---|
| master clock 无法解析 (UID) | generated clock 写在 master clock 之前 | get_clocks SYS_CLK 何时为空 | 时钟定义集中到 SDC 最前,master 先于派生 |
all_clocks evaluated when no clocks | SDC 在 read_verilog 之前被 source | report_clocks 为空 | 读网表/SPEF 之后再 source SDC |
| UITE-379 空集合 | 对象名与门级网表对不上 | sizeof_collection [get_pins ...] | 按网表层次改路径或加 -hierarchical |
| get_clocks 找不到派生时钟 | 默认不含 generated clock | get_clocks *DIV* -include_generated_clocks | 查询时补 -include_generated_clocks |
| No object matched (NED) | 综合后单元被摊平/改名/uniquify | get_cells -hier *reg_bank* 模糊搜 | 用综合后真实名,SDC 不写死 RTL 内部名 |
invalid command name "::ns::x" | package require/namespace import 缺失 | info commands ::myproj::* | 先 package require 再 source 约束 |
can't read "ports(" | data[0] 被 Tcl 当命令替换 | —— | 总线名一律用花括号 {data[0]} 包起 |
| 时好时坏、换机器就失效 | 层次分隔符非默认 / | get_app_var hierarchy_separator | 显式 set_hierarchy_separator / 统一 |
三、逐条排查展开
根因一:前向引用——对象在定义之前被引用。 pt_shell 解释执行 SDC,遇到名字就查当前的名字表,没有「全篇先扫一遍」这回事。最高频的事故是 generated clock 排在 master clock 前面:
create_generated_clock -name CLK_DIV2 -source [get_pins u_div/CLK] \
-divide_by 2 [get_pins u_div/Q]
create_clock -name SYS_CLK -period 10.0 [get_ports clk]第二行执行时 SYS_CLK 尚不存在,UID-89 当场报出,派生时钟整条作废。同样的前向引用还出现在 set_clock_groups 引用后建的时钟、set_input_delay -clock 引用后建的虚拟时钟上。纪律只有一条:时钟定义(含虚拟时钟)集中在 SDC 文件最前,派生与例外一律在后,推导规则见 SDC 生成时钟推导。
根因二:SDC 读取顺序——网表还没进来就查对象。 在 read_verilog 之前 source SDC,所有 get_ports/get_pins 必然空集;时钟没建成,后面任何 all_clocks 又触发 UID-12。正确顺序固定为:读入网表 → 读入 SPEF → source SDC。判断特征:把 SDC 在交互壳里手动重 source 一遍告警全消,就是加载顺序问题,不是约束内容问题。SDC 跨工具的生命周期管理见 SDC 约束生命周期。
根因三:空集合不报错,约束悄悄打空。 这是 PT 与 DC 最大的体感差异:set_false_path -to [get_pins ...] 中查询为空时,PT 只给 UITE-379 一条 Warning,命令本身「成功」执行——结果就是该摘的路径仍在计时、该约束的端点继续 unconstrained。三个自查动作:
sizeof_collection [get_pins u_core/u_dma/reg_bank*/Q]
get_clocks *DIV* -include_generated_clocks
all_clocks注意 get_clocks 默认不返回 generated clock,查派生时钟必须显式加 -include_generated_clocks,很多人对着空集排查半天,其实时钟早已建好。生产脚本里对关键 collection 一律做非空断言,空集即 error 中止,把 Warning 升级成硬失败。
根因四:层次路径与综合后改名。 RTL 里的 u_dma/reg_bank[3] 经过综合、uniquify、层次摊平后可能变成 u_dma/u_reg_bank_reg_3_/Q 之类的门级名字,照抄 RTL 路径必然 NED-204。先用层次模糊搜索定位真名:
get_cells -hierarchical -quiet *reg_bank*
get_pins -hierarchical -quiet */u_div/CLK-hierarchical 跨层次按叶子名搜,是门级网表里找对象的主力姿势;-quiet 压掉无匹配时的报错便于脚本化。更深的纪律是:签收 SDC 尽量只锚定端口、时钟和层级边界引脚,不要把深层 RTL 寄存器名写死——那是综合随时可以改掉的名字。另外 PT 默认层次分隔符是 /,若脚本或库把它改成了 |,所有旧路径集体失效,用 get_app_var hierarchy_separator 对一眼。
根因五:Tcl 语法层把名字吃掉了。 两类经典。一是总线位选没加花括号:get_ports data[0] 里的 [0] 被 Tcl 当成命令替换,报 can't read "ports(";正解是 get_ports {data[0]},总线通配写 {data[*]}。任何含 [ ]、$、反斜杠的对象名都用花括号整体包住。二是过程来自命名空间或包却没导入:工程把 mode 派生过程封进 ::myproj::,约束脚本直接调 derive_mode 就报 invalid command name;处置是在 source SDC 前 lappend auto_path 加路径、package require myproj 再 namespace import ::myproj::*,与综合侧包导入踩坑同构,可对照 Design Compiler 引用无法解析 的包导入一节。
收尾对拍:让 PT 自己交代解析后果。 引用解析问题最终都体现在覆盖上,修完跑 check_timing -verbose 看 UCO 类计数是否归零,再用 unconstrained 端点审计 的方法对剩余端点逐条归因。约束完备性的自动 lint 见 SDC 约束 lint。
四、排查决策树
读图抓住一条主线:先问「对象此刻在不在」,再问「名字写得对不对」。蓝节点是分流判定——时钟清单为空或 master 未定义,属于顺序问题,调 SDC 排列与 source 时机,不必怀疑名字;网表里有但查不到,才进入层次改名与 -hierarchical 模糊搜索通道;报 Tcl 命令名/变量错则纯是语法层(花括号、包导入),与时序无关。三条路汇合到绿节点:非空断言把静默失败封死,最后用 check_timing 的 UCO 计数确认没有路径因引用落空而脱管。
五、预防与纪律
- SDC 内固定排列:虚拟时钟与 master clock → generated clock → IO delay → clock groups / false path 等例外,杜绝一切前向引用。
- 加载顺序脚本化:读网表 → 读 SPEF → source SDC,任何一步顺序颠倒都让回归脚本直接失败。
- 空集合即失败:关键
get_clocks/get_pins/get_ports后立刻sizeof_collection断言,把 UITE-379 从 Warning 提升为流程中止条件。 - 不写死深层 RTL 名:签收约束锚定端口、时钟与层次边界;必须引用内部对象时用
-hierarchical模式名并在脚本里容错。 - 对象名一律花括号:
{bus[*]}、{a/b}整体引用,避免 Tcl 命令替换与通配意外展开;自定义过程统一在 SDC 头部完成 package require 与 namespace import。
这一篇在知识体系里的位置
本篇是 EDA 排错速查簇中「签收侧名字解析」节点的 FAQ 页:与综合侧 Design Compiler 引用无法解析 共享「单遍顺序敏感、空匹配静默」的底层机理,差别聚焦在门级网表改名、generated clock 前向引用、-include_generated_clocks 与层次分隔符四个 PT 专属坑;解析失败导致的约束脱管见 PrimeTime 时钟/约束缺失,签收整体流程见 PrimeTime 静态时序签收。目录总览见 EDA 工具链。
一句话总结
PrimeTime 报「引用无法解析」先问对象此刻在不在:master clock 未定义是前向引用(时钟定义集中到 SDC 最前),清单整体为空是 SDC 在网表读入前被 source;对象应存在却查不到时用 get_cells/get_pins -hierarchical 模糊搜门级真名、查派生时钟加 -include_generated_clocks,总线位选必须花括号包裹、自定义过程先 package require;根因防线是关键 collection 一律非空断言,把 UITE 空集合 Warning 升级为脚本失败,最后以 check_timing 的 UCO 归零确认没有路径因引用落空而脱管。