Vivado 构建缓存陈旧:改了 RTL 结果没变速查
更新时间:2026-10-22。FAQ 速查页,面向 FPGA 入门 读者。先说一个容易误导新手的前提:Vivado 工程模式里没有传统编译器那种 object cache,所谓"缓存陈旧",实质是 runs 状态机(synth_1/impl_1 的状态)与磁盘上的产物不同步——状态机认为输入没变、产物是新的,于是跳过综合,而你拿到的行为还是上一轮的。它的反面是另一篇讲的问题:什么都没改却被反复判定过期、停不下来地重综合,见 Vivado 工程反复重综合(Out-of-Date 循环)怎么排查。两篇分工明确:本篇管"该重不重、产物陈旧",那篇管"不该重却一直重"。
本文要回答的问题
- RTL 明明改了,Vivado 却提示
Run is up to date直接跳过 synth_1,行为为什么没变? - 改了 IP 参数,综合报
cannot find port、端口对不上,旧 wrapper 为什么还在被用? - Block Design 修改后不点 validate / 不重新 generate wrapper 会怎样?
reset_run、launch_runs -force、手工删.runs/.gen/.cache分别清掉什么,顺序是什么?- 切 git 分支后为什么容易出诡异报错,CI 怎样彻底绕开这套状态机?
一、先认清机制:up-to-date 是怎么判出来的
工程模式下,Vivado 对每个 run 维护一份状态:synth_1、impl_1 各自有 STATUS(not started / in progress / complete 等)与 NEEDS_REFRESH 标记。判定依据是源文件集的文件时间戳与内容校验和、约束、IP 输出产物、Block Design wrapper、策略与工具版本。输入逐项比对一致,run 就保持 complete 并被跳过;任一输入变化,状态转为 needs refresh,下次 launch_runs 才会重跑。
关键坑在于:状态机只认真正被工程登记且时间戳变化的输入。三类典型操作会把"文件已改"这件事瞒过去:
- 在工程外直接覆盖源文件(脚本生成、从别处拷贝),新文件保留了旧的 mtime,时间戳判定不变;
add_files加进来的是软链接,链接指向的内容换了、链接本身的 mtime 没变;- 从压缩包解出或跨机器拷贝的工程目录带着旧时间戳,Vivado 比对后认为产物比输入"还新"。
IP 与 Block Design 则是另一层:它们是"生成式输入"——你改的是定制参数或框图,综合真正吃的是生成出来的 target 产物和 wrapper 顶层。只改参数、不执行 generate_target / make_wrapper,对状态机来说输入就没有变,这是工程模式下最高频的一类"Vivado 构建缓存陈旧"。
二、四类典型现象与报错锚点
以下信息以 /proj/demo、顶层 demo_top 占位,报错编号各版本不同,用 [Synth 8-xxxx] 占位,以本机安装版本实际输出为准。
# 现象 1:RTL 改了,综合被跳过(Tcl 控制台或 Messages)
Starting synth_1...
Run is up to date. # 状态机认为无需重综合
# synth_1 进度直接写 complete,开始时间还是上一轮的
# 现象 2:IP 改参数后旧 wrapper 端口对不上
[Synth 8-xxxx] cannot find port 'adc_valid' on module 'demo_top'
[Synth 8-xxxx] port 'data_out[15:0]' does not exist in instance wrapper
# 新参数下端口位宽/名称已变,参与综合的仍是旧 IP 产物与旧 wrapper
# 现象 3:Block Design 改过但未重新生成 wrapper
# bd 里已删掉一条 axi 接口,例化与综合仍按旧 bd_wrapper 走,报接口不存在或连线悬空
# 现象 4:切 git 分支后,.runs 里残留旧分支产物
# 新分支没有的模块仍出现在综合日志,或报找不到上一分支才有的源文件现象 2、3 有个迷惑性:它们会报错,不像现象 1 那样安静地给旧结果。但根因相同——参与综合的产物不是你以为的那一版。对照下表先分诊:
| 现象 | 根因 | 快速验证 | 处置 |
|---|---|---|---|
改 RTL 后提示 Run is up to date | 工程外覆盖文件,mtime 未变;或软链换内容 | ls -l 对比源文件与 .runs/synth_1 产物时间 | reset_run synth_1 后重跑;更新文件走正常编辑/版本库 |
IP 改参数后报 cannot find port | 只改了定制参数,未 generate_target | get_property IS_LOCKED [get_ips] 返回 1 | IP 三件套:upgrade_ip、generate_target all、export_ip_user_files |
| BD 修改后综合仍用旧接口 | 未 validate design、未重新 generate wrapper | 对比 bd_wrapper.v 内容与当前框图 | validate 后 make_wrapper -force 重新生成 |
| 切分支后报上一分支的模块 | .runs/.gen/.cache 残留旧产物 | 综合日志里出现新分支不存在的文件名 | 切分支即 reset_run,产物目录不入库 |
| 解压/拷贝来的工程行为怪异 | 压缩包保留旧 mtime,骗过时间戳 | 产物时间早于源文件却被判 up-to-date | Tcl 脚本重建工程,不拷工程目录 |
launch_runs 不想动状态只想重跑 | 状态机未翻转但确信要重来 | 直接加 -force | launch_runs synth_1 -force 强制重跑 |
| 综合重跑了、实现还是旧结果 | 只 reset 了 synth_1,impl_1 仍引用旧状态 | 查 impl_1 的 STATUS 与网表时间 | 先 reset synth_1,再 reset impl_1 后顺序重跑 |
| 团队多人结果不一致 | 部分人提交了 .runs/.gen,混用新旧产物 | 版本库里是否跟踪产物目录 | 产物全部 gitignore,统一脚本重建 |
三、排查决策图
先看状态、再按输入类别分诊、最后强制重来并核对,是这类问题最短的路径。
颜色按角色区分:红色是问题入口,蓝色是排查动作,橙色是三类修复手段,绿色是收尾核对。读这张图抓住一条主线:别凭感觉判断"它应该重跑了",先让状态机自己说话——report_property 看到的 STATUS/NEEDS_REFRESH 才是 Vivado 真正采信的事实;任何一条修复路径走完,都必须落到绿色节点核对,确认综合日志和板上行为确实是新版本,否则只是心理安慰。
四、处置命令与边界
先看状态,不要上来就删目录:
report_property [get_runs synth_1]
report_property [get_runs impl_1]
get_property IS_LOCKED [get_ips]重点看 STATUS、PROGRESS、NEEDS_REFRESH。确认陈旧后,处置按侵入程度从低到高分四档,能用低档解决就不要拔高。
第一档,强制重跑单个 run,不动任何产物:launch_runs synth_1 -force。它绕过 up-to-date 判定重新执行,但不清输出目录,适合"我确信要重来一次"的快速验证。
第二档,重置 run:reset_run synth_1 清空该 run 的输出并把状态打回 not started;reset_run impl_1 同理作用于实现。注意依赖顺序:impl_1 吃的是 synth_1 产出的网表,重置综合后实现必然失效,正确顺序是先 reset_run synth_1、再 reset_run impl_1,然后依次 launch_runs synth_1、launch_runs impl_1;反过来只重置实现、保留综合,可以单独验证布局布线策略,但解决不了"综合产物陈旧"。
第三档,IP 与 Block Design 的生成三件套——这才是 IP 类陈旧的正解:
upgrade_ip [get_ips]
generate_target all [get_ips]
export_ip_user_files -of_objects [get_ips] -no_script -sync -force
# Block Design 侧:先在 GUI/Tcl 完成 validate_bd_design,再
make_wrapper -files [get_files demo_top.bd] -force
# 确认综合源集引用的是新 wrapper,再 reset_run synth_1 重综合IP 升级(跨 Vivado 版本或 IP catalog 升级)后不 generate,是最典型的陈旧源;版本升级引发的 locked 还涉及版本兼容边界,见 Vivado 版本不兼容报错速查。
第四档,手工删除工程产物目录,最后手段:.runs(综合/实现输出与状态)、.gen(生成的 wrapper 与 IP 目标产物的部分落位)、.cache(下载并综合过的 IP 中间产物)。删 .runs/.gen 等于让工程回到未综合状态,重新跑即可;删 .cache 代价最大——其中有从仓库下载的 IP 产物,删后要重新下载/生成,离线网络环境下可能直接卡住。删之前先关 Vivado,优先用 reset_run 代替手工删除。
五、工程模式、非工程模式与预防
CI 与批处理场景有个更彻底的选择:非工程模式(Non-Project Mode)。流程是 read_verilog → synth_design → opt_design/place_design/route_design → write_checkpoint 每一步显式落 dcp,没有 run 状态机、没有自动增量判定,输入与输出的对应关系完全由脚本决定,可复现性最强。口径是:工程模式增量快、适合日常交互开发,但受状态机影响会有本篇这一类问题;回归与 CI 走非工程模式,把每步 dcp 当显式产物。
日常预防三条纪律:
- 产物目录不进版本库,工程用脚本重建:
.runs、.gen、.cache、*.jou、*.log全部 gitignore,团队只共享 RTL/XDC/IP 定制文件与write_project_tcl导出的重建脚本,从根上杜绝"切分支带旧产物"。IP 版本管理的团队约定见 IP 版本管理与升级策略,版本控制基础见 RTL 版本控制。 - 改 IP 或 BD 后固定动作:改参数必
generate_target all,改框图必先 validate 再make_wrapper -force,随后reset_run synth_1,不依赖工具的自动提示。 - 团队分诊第一步统一:遇到"行为和代码对不上"的诡异问题,先
report_property看状态、再reset_run干净重综合,排除陈旧后再去怀疑 RTL 逻辑本身。
这一篇在知识体系里的位置
本篇是 FPGA 工具排错速查簇中"构建产物一致性"节点的 FAQ 页,工具入门见 Vivado 快速上手,IP 基础用法见 使用 IP 核,Block Design 流程见 Block Design 入门。互补篇:Vivado 工程反复重综合(Out-of-Date 循环)怎么排查 讲"不该重却一直重",本篇讲"该重却没重";跨版本 IP locked 与升级兼容见 Vivado 版本不兼容报错速查,团队级 IP 治理见 IP 版本管理与升级策略。
一句话总结
Vivado 构建缓存陈旧不是传统 object cache,而是 runs 状态机与产物不同步:工程外改文件保留旧 mtime、软链换内容、解压带旧时间戳会让 Run is up to date 误跳综合,IP 改参数不 generate_target、BD 改图不 make_wrapper -force 则报旧端口不存在;排查先 report_property 看 STATUS/NEEDS_REFRESH 与 IS_LOCKED,修复按 -force 重跑、reset_run(先综合后实现)、IP 三件套、手删 .runs/.gen/.cache(最后手段,.cache 删后要重新下载)四档递进,收尾必须核对日志与板上行为;长期靠产物目录不入库、脚本重建工程、改 IP/BD 后固定重新生成三条纪律,CI 则直接用非工程模式消除状态机。