[Common 17-55] 'set_property' expects at least one object 排查:XDC 端口名写错导致约束被静默丢弃
更新时间:2026-10-26。报错排查篇,属于 FPGA 入门排错簇。
CRITICAL WARNING: [Common 17-55] 'set_property' expects at least one object是写 XDC 约束时几乎必撞的一条警告:set_property PACKAGE_PIN或set_property IOSTANDARD后面跟的get_ports xxx一个对象都没匹配到,属性没有作用目标。它坑人的地方在于不致命——synth_design 照样跑完、exit=0,表面上「综合成功」,实际上那几行约束被整个丢掉了:时钟约束丢了时序分析失真,引脚约束丢了要等到 bitgen 阶段才以 DRC NSTD-1 / UCIO-1 的形式炸出来。XDC 的基础写法见 XDC 约束入门。
首屏答案:17-55 的直接含义是「set_property 拿到的对象列表是空的」,而真正的定位线索是它上面一行的 WARNING: [Vivado 12-584] No ports matched 'clkk'——工具明确告诉你哪个名字没匹配上。九成原因是端口名拼错(clk 写成 clkk)、大小写不对、或层次路径写错(子模块里的信号没用 get_pins/get_nets 加层次)。修法是回到 RTL 核对真实端口名,改对 XDC 后重跑,确认日志里 12-584 / 17-55 彻底消失——只看 exit=0 不够,因为带错的那轮 exit 也是 0。本文用 Vivado 2017.4 + xc7a35tcpg236-1 实测:把 clk 误写成 clkk,3 行 XDC 全部失效、synth_design completed successfully;改回正确名字后同一条流水线 XDC 解析零警告。
本文要回答的问题
[Common 17-55]到底在说什么,它和[Vivado 12-584]、[Vivado 12-4739]三条消息什么关系?- 为什么它只是 CRITICAL WARNING 而不是 ERROR,综合凭什么还能 exit=0?
get_ports从「名字字符串」到「设计对象」的查找是怎么走的,在哪一环会落空?- 端口名、大小写、层次路径、总线下标这几类常见写法各自错在哪?
- 怎么在 CI / 批处理里把「约束被静默丢弃」拦下来,而不是等上板才发现?
一、报错原文与现象
复现工程是一个最小 LED 闪烁模块 blink:一个时钟端口 clk、一个输出 led,器件 xc7a35tcpg236-1。XDC 里故意把端口名写错——真实端口叫 clk,约束里写成 clkk:
# blink_bad.xdc —— 端口名拼错:真实端口是 clk,这里写成了 clkk
set_property PACKAGE_PIN W5 [get_ports clkk]
set_property IOSTANDARD LVCMOS33 [get_ports clkk]
create_clock -period 10.000 -name sys_clk [get_ports clkk]用 vivado -mode batch 跑 read_verilog → read_xdc → synth_design,综合进入约束解析阶段时连炸三组消息:
读这段输出抓四个关键点。第一,每组消息都是成对出现的:先一条 WARNING: [Vivado 12-584] No ports matched 'clkk',紧跟着一条 CRITICAL WARNING: [Common 17-55] 'set_property' expects at least one object,两条还都带 [blink_bad.xdc:1] 这样的文件名加行号——12-584 说「哪个名字没匹配上」,17-55 说「所以这条 set_property 没有作用对象」,前者才是定位线索。第二,第三行 XDC 是 create_clock,它落空时报的不是 17-55 而是 CRITICAL WARNING: [Vivado 12-4739] create_clock:No valid object(s) found——同一个根因(get_ports 返回空),不同命令报不同的编号。第三,每条后面跟的 Resolution: 行是工具给的官方提示:「检查 get_* 命令是否返回了至少一个有效对象」。第四,也是最关键的:这批 CRITICAL WARNING 之后日志继续往下走,最终 synth_design completed successfully、exit=0——流程没有失败,但三行约束全部没生效。
日志尾部还有一处容易漏看的复读:Translating synthesized netlist 之后 XDC 被再解析一遍,同样三组消息原样再报一轮(日志第 263-273 行)。所以一份错 XDC 在一次综合里会留下 6 条 Critical Warnings——摘要行 15 Infos, 8 Warnings, 6 Critical Warnings and 0 Errors 里的 0 Errors 正是 exit=0 的依据。
二、根因推演:空对象为什么不致命,却比报错更坑
要理解 17-55 为什么只是 CRITICAL WARNING,得看 Vivado 读 XDC 时 get_ports 这一类命令的语义定位。
XDC 本质是 Tcl 脚本,get_ports clkk 不是「引用一个叫 clkk 的端口」这么简单,它是一次查询:拿字符串 clkk 去当前设计的端口数据库里做名字匹配,返回一个对象列表。匹配到了,列表里是端口对象;匹配不到,返回空列表——而空列表在 Tcl 里不是错误,只是一个合法的空值。于是 set_property PACKAGE_PIN W5 [get_ports clkk] 就变成了「把 PACKAGE_PIN 属性设置到零个对象上」。对工具来说这有两种可能的解释:一是用户写错了名字,二是用户故意写了一个在某种配置下才存在的可选端口(IP 核参数化生成时很常见)。Vivado 选择相信后者——报一条 CRITICAL WARNING 提醒你,然后跳过这条命令继续跑。
这个设计在 IP 场景合理,落在「端口名拼错」场景就成了陷阱,后果按约束类型分两路:
- 丢的是引脚/电平约束(
PACKAGE_PIN/IOSTANDARD):综合和实现都不查这两个属性,设计带着「无引脚约束」一路绿灯跑到write_bitstream,被前置 DRC 以[DRC NSTD-1]/[DRC UCIO-1]拦下——报错现场离真正的错因(XDC 里一个拼错的字母)已经隔了整条实现流水线,排查链路见同簇的 DRC NSTD-1 / UCIO-1 无法生成比特流排查。 - 丢的是时钟约束(
create_clock):更隐蔽。没有create_clock,时序引擎没有这个时钟域的概念,report_timing_summary里相关路径要么落到 unconstrained 里、要么按默认要求分析,时序报告看起来可以是全绿的——但那是「没按你的 100 MHz 要求检查过」的绿,不是「满足 100 MHz」的绿。这种设计上板后时序余量不足、温度一高就误码,回看综合日志才发现时钟约束压根没进去。
一句话:17-55 的非致命性让「约束没生效」混进了「综合成功」的同一条日志里,这是它比直接报 ERROR 更危险的原因——ERROR 逼你当场修,CRITICAL WARNING 放你带着隐患继续走。
顺带纠正一个常见误解:有人看到 CRITICAL WARNING 就以为是「比 ERROR 低一级、可以先不管」。在 Vivado 的消息体系里,CRITICAL WARNING 的原始定位确实是「不阻断流程的严重异常」,但它的语义是「工具发现了一个它无法替你裁决的异常,决定权交还给你」——可能是无害的可选配置,也可能是约束整行作废。判读方法不是看级别,而是看这条警告之后你想做的事情还成不成立:17-55 之后,那行 set_property 等于没写,你要的属性就不在设计里。凡是以「约束是否生效」为前提的后续分析(时序签核、引脚审查),都必须以零条 17-55/12-4739 为入场券。
三、分析与设计:get_ports 从名字到对象的查找链
把「一行 XDC 为什么失效」拆开,查找过程有四级,任何一级断了都会以空对象收场:
读这张图抓住两个岔口。第一个岔口在「名字匹配」:Vivado 的端口名查找区分大小写且不做任何模糊匹配,clk 和 CLK、clkk 是三个完全不同的字符串——RTL 里 input wire clk 定义的端口,XDC 里写 get_ports CLK 一样落空。第二个岔口在「作用域」:get_ports 只查顶层端口,想约束子模块内部的寄存器引脚要用 get_pins u_sub/reg/D 这种带层次的写法,想约束内部网线用 get_nets——拿内部信号名去 get_ports 里查,永远查不到。颜色约定:红是落空与后果链,蓝是查找过程,灰是中间结果,绿是命中,橙是修复入口。
还有一个设计要点是生效时机。XDC 在综合里被解析两次(开头一次、翻译网表后再一次),17-55 也报两轮;属性真正写进设计数据库是在 Start Applying 'set_property' XDC Constraints 这一步——空对象的约束根本到不了这一步,所以日志里这一段对坏 XDC 是「秒过」的,没有任何属性被应用。这也解释了为什么修复后必须重跑综合:属性只在解析阶段写入,事后补 XDC 不重跑等于没补。
四、常见写错对照:四类让 get_ports 落空的写法
把实测与常见踩坑归成三类,对照正确写法一次说清:
这张卡的主线是「12-584 报的名字就是案发现场」。三类的修法各不相同,但定位动作是同一个:把 12-584 引号里的名字抠出来,去 RTL 的 module 端口声明里找——找不到就是拼错或大小写,找到了但带下标就是总线写法问题,找到了却在内部 always 块里就是作用域问题。作用域类里还藏着一个顺序变体:批处理脚本把 read_xdc 写在 read_verilog 前面时,端口表根本还没建起来,所有 get_ports 全部落空;create_generated_clock 引用一个还没 create_clock 创建的时钟也属此类,12-4739 的 Resolution 文本明确提示了这一点,调整读取顺序即可。
GUI 工程里的等价坑也顺带记一笔:在 Vivado 图形界面里用 Elaborated Design 的 I/O Ports 视图手动分配引脚时,工具生成的约束名字是从视图里选的,一般不会错;但手工编辑 XDC 后改了 RTL 端口名却没有同步改约束,是 GUI 流程里 17-55 的最高频来源——重命名端口属于「改两处」的操作,RTL 与 XDC 必须绑在同一次改动里。
五、定位与修复:改对一个字母,XDC 解析干净
定位动作在上一步已经完成:12-584 说 No ports matched 'clkk',回 RTL 一看端口叫 clk,多打了一个 k。把 XDC 三行全部改回正确名字:
# blink_ok.xdc —— 与 RTL 端口名逐字符一致
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -period 10.000 -name sys_clk [get_ports clk]RTL 和 Tcl 流程不动,同一条 vivado -mode batch 流水线重跑:
# command: vivado.bat -mode batch -source v4_fix.tcl
# date: 2026-10-26
# host-toolchain: Vivado 2017.4 / Windows 批处理 / 器件 xc7a35tcpg236-1
---
Parsing XDC File [C:/eda-lab/b4/v4-common-17-55/blink_ok.xdc]
Finished Parsing XDC File [C:/eda-lab/b4/v4-common-17-55/blink_ok.xdc]
INFO: [Project 1-236] Implementation specific constraints were found while reading constraint file [blink_ok.xdc]. These constraints will be ignored for synthesis but will be used in implementation.
...
synth_design completed successfully
synth_design: Time (s): cpu = 00:01:03 ; elapsed = 00:01:09 . Memory (MB): peak = 796.887 ; gain = 519.531
exit=0对照前一轮的三个关键差异。其一,Parsing XDC File 与 Finished Parsing XDC File 两行之间干干净净——上一轮这里夹着三组 12-584 + 17-55/12-4739,这一轮一条警告都没有,说明每个 get_ports clk 都真实命中了端口对象。其二,多出来一条 INFO: [Project 1-236] Implementation specific constraints were found ... will be used in implementation——这是 PACKAGE_PIN/IOSTANDARD 这类实现期约束被正确收下的凭证(综合阶段不用它们,转交实现阶段),上一轮约束落空时这条 INFO 根本不会出现,它可以当作「引脚约束真的进去了」的正向确认信号。其三,摘要从 6 Critical Warnings 降到 0,剩下的警告只剩 Synth 8-6014 这类 RTL 自身的提示,与约束无关。
判断「这次是真修好」的标准因此是双重的:负向看 12-584 / 17-55 / 12-4739 全部消失,正向看 1-236 这条约束转交 INFO 出现。只看 synth_design completed successfully 没有意义——两轮它都在。
六、预防与纪律
- XDC 从模板抄,不手打端口名:板厂 master XDC 里的端口名需要自己对齐 RTL,但引脚号和电平是权威的;粘贴端口名而不是凭记忆敲,拼错类问题从源头上消失。
- 批处理脚本 grep 三条消息:CI 里对综合日志跑
grep -E "12-584|17-55|12-4739",命中即判失败——这比只盯 exit code 强得多,因为带错那轮 exit=0。退出码语义的完整排查见 Vivado 批处理退出码非零速查。 - 盯 Critical Warning 计数,不允许缓慢增长:综合摘要里的
N Critical Warnings应该趋向 0;团队约定「新增 CRITICAL WARNING 必须在提交说明里解释」,防止约束失效混在既有警告里被淹没。实现期 Critical Warning 的处置口径见 Vivado 实现期 DRC 与 Critical Warning 排查。 - 时钟约束丢了看时序报告看不出来:
create_clock落空后时序分析可能全绿,怀疑时钟约束没生效时直接report_clocks看时钟在不在,比翻时序路径快。 - 引脚约束丢了的下游表现是 UCIO-1:如果你是在 bitgen 阶段被 DRC UCIO-1 拦下才倒查到这一篇,记住两条报错的因果链——UCIO-1 是「没有约束」,17-55 是「约束写了但没生效」,后者修复后前者自然消失。
实验环境
- 机器/系统:Windows 主机 / 批处理驱动
- 工具版本:Vivado 2017.4(
vivado.bat -mode batch),器件 xc7a35tcpg236-1(WebPACK 免费器件范围) - 日期:2026-10-26
- 完整命令:
vivado.bat -mode batch -source v4_err.tcl(bad XDC,端口名 clkk,复现 12-584 + 17-55 + 12-4739,exit=0 但约束全丢);vivado.bat -mode batch -source v4_fix.tcl(同一 RTL + 改正端口名的 blink_ok.xdc,验证修复,exit=0 且 XDC 解析零警告);两个 Tcl 脚本均为read_verilog→read_xdc→synth_design -top blink -part xc7a35tcpg236-1
代码获取
复现用 RTL(LED 闪烁模块)、好坏两份 XDC(bad.xdc / good.xdc 对照)、Tcl 流程脚本与两轮完整日志在公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/vivado-set-property-empty。按目录内 README 用 vivado -mode batch -source 分别跑 err / fix 两个脚本,即可重现「端口名拼错 → 约束静默丢失但 exit=0 → 改名后解析干净」的完整对照。
这一篇在知识体系里的位置
本篇是 FPGA 入门排错簇中 Vivado 约束系列的一员:约束语法入门看 XDC 约束入门 与 XDC 引脚与时序约束;17-55 丢引脚约束的下游爆炸形态看 DRC NSTD-1 / UCIO-1 无法生成比特流排查;实现期其它约束类警告的处置看 Vivado 实现期 DRC 与 Critical Warning 排查;批处理退出码的通用语义看 Vivado 批处理退出码非零速查。本篇管的是「约束写了但没生效」这一特定失败形态——日志里出现 12-584 报名字时,先想端口名与作用域。
一句话总结
[Common 17-55] 'set_property' expects at least one object 的含义是 get_ports/get_pins 查询返回了空列表、属性没有作用目标,它只是 CRITICAL WARNING——综合照样 exit=0,但那几行约束被静默丢弃:时钟约束丢了时序分析失真,引脚约束丢了要到 bitgen 才被 UCIO-1 拦下。定位靠它上面一行的 [Vivado 12-584] No ports matched 'xxx'——引号里的名字就是案发现场,回 RTL 逐字符核对端口名(区分大小写、总线加下标、内部信号换 get_pins),改对后重跑综合,以「12-584/17-55/12-4739 全消失 + 出现 1-236 约束转交 INFO」为真修复标准,并在 CI 里 grep 这三条消息把静默丢失拦住。