Quartus 重复定义报错(Duplicate design unit / already defined)速查
更新时间:2026-10-23。FAQ 速查页,面向 FPGA 入门 阶段的同学。症状很典型:工程昨天还能编过,今天在 Quartus 里把某个目录又加了一遍,或者从别处拷来一份同名模块放进
ip/目录,再点 Compile 就弹出 Error 10028 / Duplicate design unit——同一个模块被编译了两遍。本篇只回答一件事:怎么从报错原文出发,十分钟内找到多出来的那一份定义并把它请出工程。
本文要回答的问题
Error (10028): Duplicate design unit和already defined是同一类问题吗?- 明明只写了一个
uart_rx,为什么 Quartus 说它有两个定义? .v文件在.qsf里只出现过一次,为什么还是重复编译?- IP 生成物和自己手写的 wrapper 撞名时,报错长什么样?
- VHDL 同名 entity 放在不同 library 算不算重复?
一、报错原文:分析综合期的统一文案
Quartus 的「重复定义」基本都冒在 Analysis & Synthesis(分析与综合)阶段,文案虽然有几变体,但语义一致:同一个编译库(默认 work)里,同名设计单元出现了第二份定义。
典型的 Verilog 重复模块报错:
Error (10028): Can't resolve multiple constant drivers for net ...
Error (10161): Verilog HDL error at uart_rx_copy.v(12): module "uart_rx" already defined at uart_rx.v(12)Quartus II 13 及更早版本里同类问题的旧文案:
Error: Duplicate design unit "uart_rx" defined in file "uart_rx.v" and file "ip/uart_0/synthesis/uart_rx.v"
Error: Duplicate entity "fifo_ctrl" defined in library "work"VHDL 场景则是:
Error (10413): VHDL error at fifo_ctrl_b.vhd(15): entity "fifo_ctrl" already defined in library "work" at fifo_ctrl.vhd(15)先记住一个关键读法:重复定义的报错位置是「第二次出现的地方」,真正的线索是报错里两处定义的文件路径。两处路径指向同一个文件,病根在 include 重复展开或头文件无守卫;指向两个不同文件,病根在 .qsf 里收了两份源文件。拿这个分叉去查,比在报错行本身上下翻有效得多。还分不清 Quartus 各阶段报错链路的,先看 Quartus 语法/解析报错速查 的阶段划分。
二、病因速查表
| 报错现象 | 最可能根因 | 快速验证 | 处置 |
|---|---|---|---|
| 同一模块名在两个路径下各定义一次 | 同一 .v 被不同路径形式各加了一次进 .qsf | 在 .qsf 里 grep 文件名,出现 ≥2 行 | 删掉多余的 set_global_assignment 行 |
重复路径之一在 ip/ 或 db/ 生成物目录下 | IP 生成物目录被手工 Add 进了工程 | 看报错路径是否含 ip/、db/、output_files/ | 生成物目录不入工程,只引用 .qip |
| wrapper 与手写顶层同名 | IP 例化生成的 wrapper 与自己写的顶层撞名 | Navigator 里 Hierarchy 视图看顶层下是否两个同名模块 | wrapper 改名或手写顶层避让 |
两个目录各有一个同名 .v | 新旧版本并存,.qsf 用通配或手工各加一份 | 工程根下按模块名全局搜 | 删除或改名旧版,filelist 单一来源 |
`include 的 .vh 内容重复报错 | 头文件无 include guard,被多个文件 `include | 报错两处路径是否指向同一 .vh | 套 `ifndef/`define 守卫 |
.vh 本身又被单独加进工程 | 头文件被 `include 展开、又被 .qsf 当源文件编译 | .qsf 里搜 .vh,有命中 | 从 .qsf 移除 .vh 条目 |
| 只有宏重复且是 Warning | `define 同名宏重复定义,后定义覆盖先定义 | Warning 文案含 redefining macro | 同样加守卫,警告别放过 |
| VHDL entity 重复 | 同名 entity 被编译进同一 work library 两次 | 报错两处 library 是否都是 work | fileset 去重或显式分库 |
SV package already defined | package 源被 .qsf 加了两遍,或又 include 又加源 | .qsf 里搜 package 文件名 | package 只加一次,使用方只 import |
三、排查决策图
读这张图只抓一个分叉:红色入口之后,蓝色判定只问一句——报错里的两处定义是不是同一个文件。同文件重复(左侧)是语言层问题,去查 include guard 和「头文件被当源文件加进工程」;两个文件重复(右侧)是工程组织问题,去查 .qsf 清单。两条橙色修复线互不相干,混着查只会绕弯;绿色收尾是统一动作,因为 Quartus 的增量数据库 db/ 可能掩盖修复效果。
四、逐条排查展开
第一步:在 .qsf 里 grep 文件名,数出现次数。 Quartus 工程的全部源文件清单就躺在 .qsf(Quartus Settings File)里,一行一条 set_global_assignment:
grep -i "uart_rx" myproject.qsf如果同一个文件名出现两行——比如一行是相对路径 rtl/uart_rx.v、另一行是 ./uart_rx.v 或绝对路径——Quartus 会把它们当两个文件分别编译,同名模块自然撞车。GUI 里 Add File 时点两次、或先手工加过又跑了加文件脚本,都会留下这种重复行。修法很直接:用文本编辑器打开 .qsf(Quartus 关闭工程时改,或 Project Navigator 里 Remove File),删掉多余的那条 assignment,保一个规范相对路径。
第二步:路径里出现 ip/、db/,就是生成物混进了工程。 Quartus 的 IP 在 Generate 之后会在 ip/<name>/synthesis/ 下吐出综合包装文件,db/ 是编译数据库。这两个目录都不该出现在 .qsf 的源文件清单里——正确的用法是只引用 IP 的 .qip 文件,由 Quartus 自己管理产物。如果你手滑把 ip/ 整个目录 Add 进来,IP 自己那份 uart_rx 和你手写的同名模块就同时进了 work 库。顺带一提:手写顶层别和 IP wrapper 默认名(如 <ip名>_inst.v 或 <ip名>.v)撞车,撞了就改自己这边。
第三步:同一文件被 include 多次,给头文件加守卫。 Verilog 的 `include 是纯文本插入,没有自动去重。两个常见场景:一是 .vh 头文件没有守卫,被 a.v 和 b.v 各 include 一遍,里面的 parameter / typedef 就在同一个编译作用域里展开两次;二是 .vh 本身又被 .qsf 当源文件单独编译了一次——include 展开一遍、单独编译又一遍。后一种在「.qsf 里搜 .vh 有命中」时立刻坐实。统一修法是 include guard:
`ifndef UART_PKG_VH
`define UART_PKG_VH
// parameter / typedef / 宏定义
`endif同时要分清报错级别:`define 宏重复通常只是 Warning(后定义静默覆盖先定义,更危险,因为结果可能不是你以为的那份);parameter、typedef 重复展开是硬 Error。警告别因为不红就放过。SystemVerilog package 同口径:源文件只在 .qsf 里加一次,使用方文件只写 import pkg::*;,不要再 include package 源本身。
第四步:VHDL 场景确认 library 归属。 Quartus 默认所有 VHDL 文件都编进 work 库,同名 entity 两份必然冲突。报错文案里写明 already defined in library "work" 就是这个情况。处置有两种:轻的是去重——.qsf 里同名 .vhd 只留一份;重的是分库——如果两份 entity 是不同来源的同名模块(比如旧 IP 和自己新写的),用 set_global_assignment -name VHDL_FILE <file> -library <lib名> 把它们分到不同库,再在顶层显式 library old_lib; use old_lib.fifo_ctrl.all; 区分。分库前先想清楚:同名模块长期并存本身就是工程卫生问题,能改名先改名。
五、修复后验证与防复发
修完别急着收工。先 Project > Clean Project(或命令行删掉 db/、incremental_db/)清掉增量数据库再全量重编,确认 Error 与「重复定义」相关 Warning 全部消失——Quartus 的增量编译有时会用旧数据库掩盖新改动,必须干净重跑才算数。再在 Project Navigator 的 Files 页扫一眼:每个模块名下只有一份文件,没有 .vh 被列成源文件,ip/、db/ 目录不出现在清单里。
把同类问题挡在工程之外,靠四条习惯:一是全工程只维护一个加文件的入口——要么全走 GUI,要么全走脚本,不混用;二是建工程/加文件脚本可重入,重复执行不产生重复 assignment(先 grep 再追加,或每次从模板干净重建 .qsf);三是所有 .vh 出生就带 include guard,模块定义只放 .v、不放头文件,.vh 永不出现在 .qsf 源清单里;四是 IP 生成物目录(ip/、db/、output_files/)永远不手工 Add File,只引用 .qip。同簇的「找不到模块」问题(.qsf 漏加文件的反面)见 Quartus 找不到模块速查,Vivado 侧同一问题的对应页见 Vivado 重复定义速查——两边病因轴几乎一一对应,换工具时排查思路可以直接搬。
这一篇在知识体系里的位置
本篇属于 FPGA 工具报错速查簇,与 Quartus 找不到模块速查(.qsf 缺文件的反面)、Quartus 未定义符号速查(链接期符号问题)、Quartus 语法/解析报错速查(解析期问题)互为姊妹篇;跨工具对照见 Vivado 重复定义速查。
一句话总结
Quartus 重复定义的本质是同一编译库里同名单元出现两份,先读报错里的两处定义路径做唯一分叉:路径相同就查 include guard 和「.vh 被当源文件加进 .qsf」,路径不同就在 .qsf 里 grep 文件名数出现次数、删多余 assignment,并把 ip/、db/ 生成物目录请出工程;根治靠单一 filelist、可重入的加文件脚本、带守卫的头文件和只引用 .qip 的 IP 用法,修完 Clean Project 干净重编闭环。