Vivado 重复定义(already declared / duplicate module)速查
更新时间:2026-10-22。FAQ 速查页,面向 FPGA 入门 阶段的同学。症状很典型:工程一直好好的,今天手滑多点了一次 Add Sources,或者把 IP 生成物目录也加进了工程,再点 Run Synthesis 就满屏红色——同一个名字被定义了两次。本篇只回答一件事:怎么从报错原文出发,十分钟内找到多出来的那一份定义并把它请出 fileset。
本文要回答的问题
already declared in this scope和Module ... previously defined是同一类问题吗?- 明明只写了一个
uart_rx,为什么 Vivado 说它被定义了两次? .vh头文件被多个文件`include,什么情况下只是警告、什么情况下直接报错?- SystemVerilog package 和 VHDL entity 的重复编译各是怎么发生的?
- 为什么用
glob批量加文件的脚本,重建工程时最容易把 IP 生成物收进来?
一、报错原文:三处阶段,一套语义
Vivado 的「重复定义」会在三个阶段冒出来,文案不同但语义一致:同一个编译作用域里,同名单元出现了第二份定义。
综合阶段(Vivado 内置综合器,[Synth 8-xxxx]):
ERROR: [Synth 8-xxxx] net 'demo_top' already declared in this scope [/proj/demo/rtl/demo_top.v:23]
ERROR: [Synth 8-xxxx] duplicate definition of module 'uart_rx' at rtl/uart_rx.v:12 and ip/uart_0.gen/sources_1/uart_rx.v:12仿真编译与展开阶段(xvlog/xvhdl 编译、xelab elaborate,[VRFC 10-xxxx]):
ERROR: [VRFC 10-xxxx] Module 'uart_rx' previously defined at file 'uart_rx.v' line 12
ERROR: [XSIM 43-xxxx] A name field in this design contains multiple definitions for 'fifo_pkg'.先记住一个关键读法:重复定义的报错位置是「第二次出现的地方」,真正的线索是报错里两处定义的文件路径。两份路径指向同一个文件,病根在 include 或编译单元;指向两个不同文件,病根在 fileset 里收了重复源。拿这个分叉去查,比在报错行本身上下翻有效得多。还分不清综合与仿真两条报错链路的,先看 Vivado 语法/解析报错速查 的阶段划分。
二、病因速查表
| 报错现象 | 最可能根因 | 快速验证 | 处置 |
|---|---|---|---|
| 同一模块名在两个路径下各定义一次 | 同一源文件被 add_files 加了两遍,或 Add Sources 点了两次 | get_files 输出里搜文件名,有两行命中 | remove_files 移除多余一份 |
重复路径之一在 *.gen/sources_1 下 | glob *.v 把 IP 生成物目录也收进了工程 | 看报错路径是否含 .gen/sources_1 | glob 收窄到 src/,排除 .gen |
| wrapper 与手写顶层同名 | IP 例化生成的 wrapper 与自己写的 demo_top 撞名 | Sources 窗口看顶层下是否有两个同名模块 | wrapper 改名或手写顶层避让 |
| 仅在仿真报重复,综合不报 | sim_1 与 sources_1 各加了一份同名模块 | 分别列出两个 fileset 的文件清单 | 从 sim_1 移除重复源 |
.vh 里的 typedef/parameter 重复报错 | 头文件无 include guard,被多个文件 `include | 看报错两处是否都指向同一个 .vh | 套 `ifndef/`define 守卫 |
| 只有宏重复且是 Warning | `define 同名宏重复定义,后定义覆盖先定义 | Warning 文案含 redefining macro | 同样加守卫,警告别放过 |
SV package multiple definitions | package 被多个编译单元重复编译 | 查 package 源文件被 xvlog 编了几次 | package 源只编译一次,其余文件只 import |
| VHDL entity 重复 | 同一 entity 被编译进同一 library 两次 | xvhdl 日志里 entity 出现两遍 | fileset 去重,确认 library 归属 |
两个目录各有一个同名 .v | glob **/*.v 全收,新旧版本并存 | 按模块名在工程根下 find | 删除或改名旧版,filelist 单一来源 |
三、排查决策图
读这张图只抓一个分叉:红色入口之后,蓝色判定只问一句——报错里的两处定义是不是同一个文件。同文件重复(左侧)是语言层问题,去查 include guard 和 package/entity 的编译次数;两个文件重复(右侧)是工程组织问题,去查 fileset 清单。两条橙色修复线互不相干,混着查只会绕弯;绿色收尾是统一动作,因为 Vivado 的增量运行可能掩盖修复效果。
四、逐条排查展开
第一步:列出真正参与编译的文件清单。 在 Tcl 控制台按 fileset 分别列出:
get_files [get_filesets sources_1]
get_files [get_filesets sim_1]把重复的模块名拿去两份清单里搜。综合报重复查 sources_1,仿真报重复查 sim_1——仿真 fileset 单独持有一份同名模块是高频坑:testbench 连同旧版 RTL 一起被加进了 sim_1,综合看不见、xelab 编译两遍。确认多余文件后用 remove_files 把它移出 fileset(这只是解除工程引用,不删磁盘文件),再顺手检查 Add Sources 脚本是不是被执行了两次。
第二步:路径里出现 .gen,就是 glob 惹的祸。 脚本化工程爱用 add_files [glob *.v] 一把收,但 IP 执行 Generate Output Products 后会在 <ip>.gen/sources_1/ 下吐出综合包装文件,再 glob 一次就把 IP 自己那份模块和你手写的同名模块同时塞进工程。修法是把通配收窄到源码目录:
add_files [glob ./src/rtl/*.v]并显式排除 .gen、.runs 等生成物目录。IP 的正确用法是只加 .xci,由 Vivado 自己管理产物,见 使用 IP 核。block design 的 wrapper 与手写顶层撞名也属此类——wrapper 默认叫 <bd名>_wrapper.v,手写顶层别取同名。
第三步:同一文件被 include 多次,给头文件加守卫。 Verilog 的 `include 是纯文本插入,没有自动去重。要分清报错级别:`define 宏重复通常只是 Warning(后定义静默覆盖先定义,更危险,因为结果可能不是你以为的那份);而 .vh 里的 parameter、typedef、localparam 重复展开是硬 Error。统一修法是 include guard:
`ifndef UART_PKG_VH
`define UART_PKG_VH
// parameter / typedef / 宏定义
`endifSystemVerilog package 另说:xsim 下每个 package 源文件只需编译一次进入 work 库,使用方文件只写 import pkg::*; 即可;若把 package 源本身加进多个编译单元、或同一份 .sv 在 filelist 里出现两行,xelab 展开时就会报 multiple definitions。VHDL 同理,同一 entity 不要重复编译进同一个 library,换 library 重编译前先确认旧编译产物已被清掉。跨语言混编工程还应确认每个文件归属的 fileset、library 与源文件属性(Verilog / SystemVerilog / VHDL)设置正确,属性设错有时也会让同一文件被按两种模式处理。
第四步:检查加文件脚本的幂等性。 能反复执行而不出错的脚本才是好脚本。add_files 前先判断文件是否已在工程内,或干脆用 create_project -force 每次干净重建,这也是 Vivado 模块找不到 推荐的工程迁移方式——两类报错其实是同一个 fileset 卫生问题的正反面:一份是缺了,一份是多了。链接阶段的重复符号排查见 Vivado 未定义符号速查。
五、修复后验证与防复发
修完别急着关工程。先 reset_run synth_1(仿真则 Reset Behavioral Simulation)清掉增量结果再重跑,确认重复定义的 Error/Warning 全部消失——Warning 级的宏重定义也要逐条核对,确认最终生效的是你想要的那一份,别因为不红就放过;再在 Sources 窗口按 Hierarchy 视图扫一眼,确认每个模块名下只有一份文件,Sources 窗口里也没有重复出现的 IP 生成物条目。
把同类问题挡在工程之外,靠四条习惯:一是全工程只维护一个 filelist 来源,GUI 手工添加和脚本添加不要混用;二是建工程脚本可重入,create_project -force 重建而不是在旧工程上追加;三是所有 .vh 出生就带 include guard,模块定义只放 .v、不放头文件;四是 IP 输出目录(.gen/.runs/.srcs 下的生成物)永远不手工加源,只引用 .xci 与 block design。工程脚本化的整体流程可对照 Vivado 快速上手。
这一篇在知识体系里的位置
本篇属于 FPGA 工具报错速查簇,与 Vivado 模块找不到(fileset 缺文件的反面)、Vivado 未定义符号(链接期符号问题)、Vivado 语法/解析报错(解析期问题)互为姊妹篇;IP 与 fileset 的正常用法见 使用 IP 核,仿真 fileset 与 testbench 组织见 Testbench 仿真入门。
一句话总结
Vivado 重复定义的本质是同一编译作用域里同名单元出现两份,先读报错里的两处定义路径做唯一分叉:路径相同就查 include guard 与 package/entity 的重复编译(宏重复只是覆盖警告,typedef/parameter 重复才报错),路径不同就用 get_files 按 fileset 列出清单、remove_files 移除多余源,并把 glob 收窄到 src/ 排除 IP 的 .gen 生成物;根治靠单一 filelist、可重入的建工程脚本和带守卫的头文件,修完 reset_run 干净重跑闭环。