[DRC NSTD-1] [DRC UCIO-1] 无法生成比特流排查:Vivado write_bitstream 被 DRC 拦截修复实录
更新时间:2026-10-26。报错排查篇,属于 FPGA 入门排错簇。
ERROR: [DRC NSTD-1]与ERROR: [DRC UCIO-1]是新手用 Vivado 跑第一个上板工程时几乎必撞的一对报错:综合、实现、布线全部绿灯,偏偏在最后一步write_bitstream被一道前置 DRC 检查拦下,Bitgen not run,比特流根本没生成。根子不在布线也不在设计,而是整个工程没有 XDC 约束文件——6 个逻辑端口既没告诉工具用什么电平标准(NSTD-1),也没告诉工具接到哪根物理引脚(UCIO-1)。XDC 的基础写法见 XDC 约束入门,同簇的 DRC 严重级别问题见 Vivado 实现期 DRC 与 Critical Warning 排查。
首屏答案:这对报错是 Vivado 的「最后一道安检」——bitgen 之前强制跑一轮 DRC,发现任何逻辑端口缺 IOSTANDARD(电平标准)或缺 PACKAGE_PIN/LOC(物理引脚位置)就直接拒绝生成比特流,因为带着默认电平或随机引脚上板,轻则点不亮,重则顶坏 IO bank。修法不是降级检查,而是给工程补一份 XDC:set_property PACKAGE_PIN 把每个端口绑到原理图上的真实引脚,set_property IOSTANDARD LVCMOS33(电平按板上 bank 供电定)补齐电平标准。本文用 xc7a35t(Basys3 同级器件)的 4 位计数器工程实测:无 XDC 跑到 write_bitstream 被双 DRC 打死、exit=1;补 6 行约束后同一条流水线 write_bitstream completed successfully、exit=0。
本文要回答的问题
[DRC NSTD-1]与[DRC UCIO-1]两行报错各自在查什么,为什么总是成对出现?- 为什么综合和布线都过了,偏偏在
write_bitstream这一步才炸? set_property SEVERITY {Warning} [get_drc_checks NSTD-1]这种降级绕过能不能用,代价是什么?- 一份最小可用的 XDC 长什么样,引脚号和电平标准从哪里查?
- 修完怎么确认比特流是真的生成了,而不是「这次没报错而已」?
一、报错原文与现象
复现工程是一个最小计数器:时钟 clk、复位 rst_n、4 位输出 q[3:0],共 6 个逻辑端口,器件 xc7a35tcpg236-1。工程里故意不放任何 XDC,用 vivado -mode batch 从 synth_design 一路跑到 write_bitstream。前面所有步骤全部成功,直到最后一步:
读这段输出抓五个关键点。第一,route_design completed successfully 就在报错上方——布线本身是过的,证明设计逻辑没问题。第二,Running DRC as a precondition to command write_bitstream 这一行点明了机制:DRC 不是布线的一部分,而是 bitgen 的前置安检,write_bitstream 命令进来先跑 DRC,过了才往下走。第三,ERROR: [DRC NSTD-1] Unspecified I/O Standard: 6 out of 6 logical ports use I/O standard (IOSTANDARD) value 'DEFAULT'——6 个端口全部用着工具兜底给的 DEFAULT 电平,没有一个被用户显式指定。第四,紧随其后的 ERROR: [DRC UCIO-1] Unconstrained Logical Port: 6 out of 6 logical ports have no user assigned specific location constraint (LOC)——同样 6 个端口,一个物理引脚位置都没分。第五,收尾三连:DRC finished with 2 Errors → ERROR: [Vivado 12-1345] Error(s) found during DRC. Bitgen not run. → ERROR: [Common 17-39] 'write_bitstream' failed due to earlier errors.,批处理脚本拿到 exit=1。
注意夹在两条 ERROR 中间还有一条 WARNING: [DRC CFGBVS-1] Missing CFGBVS and CONFIG_VOLTAGE,它说的是配置 bank 的电压属性没设,只是警告、不拦 bitgen,本篇先记下不展开——两条 ERROR 才是失败点。另外报错文本里 Vivado 自己给了「降级绕过」的提示(把 NSTD-1 严重级别改成 Warning),第四节专门讲为什么不该照做。
二、根因推演:为什么综合布线全绿,最后一步才炸
这是这对报错最迷惑人的地方,值得从 Vivado 的流水线职责分工上推演清楚。
FPGA 实现流水线的每一步只关心自己那一段的正确性:synth_design 把 RTL 映射成 LUT/FF,它不需要知道引脚接哪;opt_design、place_design 在器件内部逻辑资源上做优化和布局,同样不碰板级电气;route_design 连内部走线,也不管 IO 电平。整条链上没有任何一步强制要求 XDC 存在——缺约束时工具一律用默认值兜底(IOSTANDARD 落 DEFAULT,引脚位置干脆悬空不绑定),流程照样走完。所以「前面全绿」不代表约束齐全,只代表 RTL 能映射、能布通。
而 write_bitstream 不一样:它产出的是要真正烧进芯片、驱动物理引脚的配置文件。一旦引脚位置是工具随机占位的、电平标准是默认值,烧上板之后可能发生的是:输出引脚电平和外接器件不匹配造成 IO contention(两个驱动源对打),极端情况直接损伤 IO bank 或板上的外设。Vivado 的设计者把这个风险拦在 bitgen 之前——这就是「precondition DRC」存在的原因。NSTD-1(No STanDard,查 IOSTANDARD)和 UCIO-1(UnConstrained IO,查 LOC/PACKAGE_PIN)正是这道安检的两项必查科目。因为端口约束通常在同一个 XDC 里成对写,缺的时候自然也是成对缺,所以两条报错几乎总是结伴出现——本篇实测 6 个端口双 6/6,是「整份 XDC 都没写」的典型指纹。
如果只有部分端口报(比如 2 out of 6),那就是 XDC 写了但漏了几个端口,常见于后来给 RTL 加了端口却忘了回约束文件补行。排查思路相同,只是补的行数少一点。
三、两类 DRC 的分工:NSTD-1 管电平,UCIO-1 管位置
把两条报错拆开看,它们检查的是端口约束的两个正交维度,缺一不可:
这张表要抓住的要点是「两个维度正交、各有权威来源」。IOSTANDARD 的取值不由引脚决定,而由这个引脚所在 IO bank 的供电电压 VCCO 决定:bank 供 3.3V 就用 LVCMOS33,供 2.5V 用 LVCMOS25,供 1.8V 用 LVCMOS18——查板卡手册的电源章节,或者干脆抄板厂给的 master XDC 模板。PACKAGE_PIN 的取值则完全来自原理图:FPGA 的某根焊球连到了板上的哪个 LED、哪个按键、哪个时钟晶振,原理图上写得清清楚楚,Basys3、Nexys 这类教学板厂商还会发布现成的 master XDC,全部引脚都列好、默认注释掉,用哪根放开哪根即可。
一个必须强调的顺序:先查权威来源再写约束,绝不凭感觉编引脚号。写错 PACKAGE_PIN 的约束文件能通过 DRC——工具只知道这个位置合法、不知道它连错了东西——然后比特流生成成功,烧上板行为错乱,排查代价比 DRC 拦截高得多。DRC 拦你是保护你,绕过去还写错才是真正的坑。
四、定位与修复:从报错行到一份最小 XDC
把完整排查路径画成决策图,颜色按角色区分:红是报错与根因,蓝是排查动作,橙是修复动作,绿是通过态,紫是防复发沉淀。
读这张图抓住一条主线:报错文本自己就带着修复清单。Problem ports: q[3:0], clk, and rst_n 这一行把要补约束的端口名逐个列了出来,不需要另外跑 report;对总线端口(q[3:0])要写按位约束,XDC 里用 q[0]、q[1] 逐位展开或用 get_ports {q[*]} 批量给 IOSTANDARD。主干路径走「读端口 → 查 XDC → 抄权威来源 → 补齐 → 重跑」五步;右下角那条红色岔路是 Vivado 自己在报错文本里建议的降级写法,也是新手最容易被带偏的一条——单列出来讲清楚。
为什么不该用 SEVERITY 降级绕过。 报错原文给的 set_property SEVERITY {Warning} [get_drc_checks NSTD-1] 能把 ERROR 降成 Warning 让 bitgen 继续跑,但它只改检查的严重级别,不改任何电气事实:IOSTANDARD 还是 DEFAULT,引脚还是工具占位。拿这样的比特流上板,等于把 Vivado 帮你挡下的风险原样接回来——DEFAULT 电平在 7 系列上落到 bank 上可能与外设供电不符,占位引脚接的根本不是你以为是的那颗 LED。这个开关的合法用途只有一个:公司内部的纯仿真/时序分析流程,设计永不上板,临时降级省掉约束维护。凡是要烧进真实芯片的,一律走补 XDC 的正路。
本篇的修复是给工程加一份 counter.xdc,6 个端口每个两行(位置 + 电平),引脚号按 Basys3 同级板卡的 master XDC 抄录:
# counter.xdc —— xc7a35tcpg236-1,电平随 IO bank 3.3V 供电
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN R2 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN U16 [get_ports {q[0]}]
set_property PACKAGE_PIN E19 [get_ports {q[1]}]
set_property PACKAGE_PIN U19 [get_ports {q[2]}]
set_property PACKAGE_PIN V19 [get_ports {q[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {q[*]}]批量写法的细节:电平标准对同 bank 的整组端口用 get_ports {q[*]} 一行搞定;引脚号必须逐位指定,没有通配捷径——这正是厂商 master XDC 把每根引脚都列出来的原因。引脚与时序两类约束的完整写法见 XDC 引脚与时序约束。
五、对照实测:补 6 行约束后比特流一次生成
RTL 与 Tcl 流程脚本完全不动,只把 counter.xdc 加进工程(read_xdc counter.xdc),同一条 vivado -mode batch 流水线重跑。结果:前置 DRC 干净通过,bitgen 真正执行:
# command: vivado.bat -mode batch -source v2_fix.tcl
# date: 2026-10-26
# host-toolchain: Vivado 2017.4 / Windows 批处理 / 器件 xc7a35tcpg236-1
---
# write_bitstream counter.bit
Command: write_bitstream counter.bit
Attempting to get a license for feature 'Implementation' and/or device 'xc7a35t'
INFO: [Common 17-349] Got license for feature 'Implementation' and/or device 'xc7a35t'
Running DRC as a precondition to command write_bitstream
INFO: [DRC 23-27] Running DRC with 2 threads
INFO: [Vivado 12-3199] DRC finished with 0 Errors, 1 Warnings
INFO: [Designutils 20-2272] Running write_bitstream with 2 threads.
Writing bitstream ./counter.bit...
INFO: [Vivado 12-1842] Bitgen Completed Successfully.
write_bitstream completed successfully
exit=0对照前一轮的三个关键差异:其一,同样的 Running DRC as a precondition 之后,DRC finished with 0 Errors——两条 ERROR 全部清零,剩下的 1 条 Warning 正是第一节提到的 CFGBVS-1 配置电压提示,不影响 bitgen;其二,Writing bitstream ./counter.bit... 这一行上一轮根本没出现,说明这次 DRC 真的放行了,bitgen 才开始干活;其三,Bitgen Completed Successfully + 脚本拿到 exit=0,产物 counter.bit 落盘,可以直接进 Hardware Manager 烧写。烧写上板的后续流程见 比特流生成与烧写。
确认标志也顺手记一下:判断「这次是真修好」看的是 DRC finished with 0 Errors 与 Writing bitstream 同时出现——只看进程没报错不够,降级绕过也能让进程 exit=0,但那次的 DRC 摘要里 Errors 是被降级掩盖的,Warnings 计数会异常高。
六、顺带的 CFGBVS-1 警告怎么处理
两轮实测的 DRC 摘要里都挂着同一条 WARNING: [DRC CFGBVS-1] Missing CFGBVS and CONFIG_VOLTAGE。它检查的是配置 bank(bank 0)的两个设计属性:CFGBVS 告诉工具配置引脚参考 VCCO 还是 GND,CONFIG_VOLTAGE 告诉它配置 bank 的实际供电电压。比特流生成后,配置阶段要靠这两个值判断 bank 0 的 IO 电压支持范围。它默认只是 Warning 不拦 bitgen,所以本篇的 fix 轮带着它也能成功生成 counter.bit;但对要上板的设计,正确做法是顺手在 XDC 里补两行:
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]取值同样看板卡电源:配置 bank 供 3.3V 就照上面写,供 1.8V 则 CFGBVS GND + CONFIG_VOLTAGE 1.8。教学板(Basys3 等)的 master XDC 里这两行通常已经写好,放开注释即可。把这条警告清掉之后,DRC 摘要变成 0 Errors, 0 Warnings,日志干净,CI 里盯着也省心——混着一条长期挂着的警告,下次真出新警告时反而容易被淹没。
七、预防与纪律
- 新建工程第一件事拷 master XDC:板厂给的模板里全部引脚已列好,用到的放开注释,NSTD-1/UCIO-1 从源头不发生。
- RTL 加端口当天补约束:部分端口缺约束(报错里不是全量 out of)的九成来源就是「改了 RTL 忘了 XDC」,把这两件事绑定成一次提交。
- 电平跟着 bank 供电走,不猜:LVCMOS33 还是 LVCMOS18 查板卡电源章节,写错电平比缺约束更隐蔽。
- CI 里盯 DRC 摘要:批处理脚本 grep
DRC finished with 0 Errors,非零即失败,比只盯 exit code 更能拦住被降级掩盖的问题;批处理退出码的通用排查见 Vivado 批处理退出码非零速查。 - SEVERITY 降级开关不进上板流程:它存在的意义是仿真侧临时豁免,任何要烧芯片的流水线里出现
get_drc_checks降级写法都应该在评审时被拦下。
实验环境
- 机器/系统:Windows 主机 / PowerShell 批处理驱动
- 工具版本:Vivado 2017.4(
vivado.bat -mode batch),器件 xc7a35tcpg236-1(WebPACK 免费器件范围,Basys3 同级) - 日期:2026-10-26
- 完整命令:
vivado.bat -mode batch -source v2_err.tcl(无 XDC,复现 NSTD-1 + UCIO-1 双报错,exit=1);vivado.bat -mode batch -source v2_fix.tcl(同一 RTL + counter.xdc,验证修复,exit=0);两个 Tcl 脚本均为read_verilog→synth_design→opt_design→place_design→route_design→write_bitstream
代码获取
复现用 RTL(4 位计数器)、两份 Tcl 流程脚本与 XDC 约束文件在公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/vivado-drc-nstd-ucio。按目录内 README 用 vivado -mode batch -source 分别跑 err / fix 两个脚本,即可重现「write_bitstream 被双 DRC 拦截 → 补 XDC 后比特流生成成功」的完整对照。
这一篇在知识体系里的位置
本篇是 FPGA 入门排错簇中 Vivado DRC 系列的一员:约束语法的系统入门看 XDC 约束入门 与 XDC 引脚与时序约束;实现期其它 DRC 与 Critical Warning 的处置看 Vivado 实现期 DRC 与 Critical Warning 排查;比特流生成之后的烧写流程看 比特流生成与烧写。本篇管的是「最后一步被安检拦下」这一特定失败形态——流水线全绿、唯独不出比特流时,先想 NSTD-1/UCIO-1。
一句话总结
[DRC NSTD-1] 与 [DRC UCIO-1] 是 Vivado 在 write_bitstream 之前强制 DRC 拦下的两类端口约束缺失——前者管 IOSTANDARD 电平没指定、后者管 PACKAGE_PIN 引脚没分配,综合布线全绿是因为那些阶段根本不查约束;正解是按板卡 master XDC 给每个端口补 PACKAGE_PIN + IOSTANDARD 两行(本篇 6 端口补齐后 DRC 0 Errors、比特流一次生成),而不是用 set_property SEVERITY {Warning} 把安检降级——降级只改检查级别不改电气事实,带着 DEFAULT 电平和占位引脚上板的风险全部自担。