iverilog include 文件找不到:-I 搜索路径与编译目录排查
iverilog include 文件找不到 发生在预处理阶段:编译器还没开始检查模块连接,就因为 `include 指定的头文件无法打开而退出。这个错误与 iverilog include 文件找不到之外的语法错误 不同,单纯调整源码写法通常无效,要先还原搜索目录。
本文要回答的问题
Include file ... not found中的文件名和行号分别指向什么?`include "local.svh"会自动相对当前.sv文件查找吗?- 相对写法的
-Iinclude以哪个目录为基准,多个-I怎样配合? - 为什么换一个终端目录,同一份源码的报错会改变?
这张漏斗的关键是“逐项补路径”。一次补上所有目录也能成功,却不容易看出哪一项真正生效;先让错误从 local.svh 推进到 config.svh,可以确认前一个目录已经被搜索,剩下的问题才属于后一个头文件。
一、完整报错:第一个 include 就中止预处理
实验只有一份测试平台和两个头文件。src/top.sv 第 2 行包含同目录的 local.svh,第 3 行包含独立目录中的 config.svh;前者定义常量 42,后者定义位宽 6。使用 Icarus Verilog 13.0,从实验根目录执行:
iverilog -g2012 -s tb_include -o build/root-no-I.vvp src/top.sv合并标准输出和标准错误后,完整诊断为:
src/top.sv:2: Include file local.svh not found
Preprocessor failed with 1 errors.
root-no-I exit=1 (expected=1)src/top.sv:2 是触发包含动作的源码位置,不是头文件里的错误行;local.svh 则是预处理器最终没能打开的名字。命令没有显式 -I,退出码为 1,也没有可供 vvp 使用的仿真文件。
批处理中不要忽略这个退出码后继续运行旧的 .vvp。旧文件即使能跑出 PASS,也只能证明上一次编译产物可用,不能证明本次头文件修复成功。可靠入口应当在编译失败时立即停止,只有退出 0 才进入仿真。
这里最值得注意的现象是:源文件明明位于 src/,src/local.svh 也真实存在,从根目录启动时仍然找不到它。因此不能把“include 与源文件同目录”当成必然有效的保证,至少要结合编译器版本和启动目录实测。
二、根因:本例由 cwd 与 -I 共同决定搜索位置
当前工作目录(current working directory,cwd)是启动 iverilog 时所在的位置。相对形式的 -Iinclude 不是相对 src/top.sv,而是相对这个 cwd;传给 -o 的输出路径也遵循同样的目录基准,但两者互不影响。
同一实验切换到 src/ 后执行 iverilog ... top.sv,第 2 行的 local.svh 已经能够找到,错误推进到了第 3 行:
top.sv:3: Include file config.svh not found
Preprocessor failed with 1 errors.
src-cwd-no-I exit=1 (expected=1)对比两次输出可以划出边界:在 Icarus Verilog 13.0 的这次双引号 include 实验中,根目录运行不会自动进入源文件目录找 local.svh;改从 src/ 启动后它才可见。这里不要外推到其他工具或其他 include 形式,工程上显式写出 -I 更稳定。
| 编译入口 | 可见的头文件 | 首个失败点 | 退出码 |
|---|---|---|---|
根目录,无 -I | 两个目录都未显式加入 | local.svh | 1 |
根目录,-Isrc | src/local.svh | config.svh | 1 |
根目录,-Isrc -Iinclude | 两个头文件 | 无 | 0 |
src/,无 -I | src/local.svh | config.svh | 1 |
src/,-I../include | 两个头文件 | 无 | 0 |
三、排查清单:让错误推进,而不是反复重跑同一条命令
下面用红色表示失败入口,蓝色表示核对动作,橙色表示补充搜索路径,绿色表示验收,紫色表示需要固化的入口约定。它适用于典型的“文件存在但编译器找不到”,不替代权限与拼写检查。
实际执行时,先复制诊断中的文件名,检查大小写和扩展名;.svh 只是常用约定,并不会让编译器自动知道目录。随后在实际启动编译的位置运行 pwd,再用 ls -l src/local.svh include/config.svh 核对文件。若命令来自 Makefile 或 CI,要检查它是否先 cd,不能用另一个交互终端的目录代替。
只补 -Isrc 后,报错从第 2 行变为第 3 行,说明修复方向正确但路径尚未补齐:
src/top.sv:3: Include file config.svh not found
Preprocessor failed with 1 errors.
root-src-only exit=1 (expected=1)如果始终报同一个名字,再检查 -I 是否指错层级,例如目录实际为 include/config.svh,就应写 -Iinclude,而不是 -Iinclude/config.svh。-I 接收目录,不接收头文件本身。
四、修复命令:两个头文件目录都显式给出
测试平台保留两条 include,不需要把宏定义复制进 .sv,也不需要把源码中的路径改成依赖某台机器的绝对路径:
`include "local.svh"
`include "config.svh"
module tb_include;
initial begin
if (`LOCAL_VALUE === 8'h2a && `CFG_WIDTH == 6) begin
$display("PASS: LOCAL_VALUE=%0d CFG_WIDTH=%0d",
`LOCAL_VALUE, `CFG_WIDTH);
end else begin
$fatal(1, "FAIL: include values are incorrect");
end
$finish;
end
endmodule从实验根目录编译时,同时加入 src 与 include:
iverilog -g2012 -Isrc -Iinclude -s tb_include -o build/root-with-I.vvp src/top.sv
vvp build/root-with-I.vvp若统一从 src/ 启动,同目录的 local.svh 已能找到,只需把另一个目录写成相对当前 cwd 的 ../include:
cd src
iverilog -g2012 -I../include -s tb_include -o ../build/src-cwd-with-I.vvp top.sv
cd ..
vvp build/src-cwd-with-I.vvp两种写法没有优劣上的绝对结论,重点是整条构建链使用同一入口。若在根目录命令里照抄 -I../include,它会被解释为实验根目录的上一级,而不是 src/ 的上一级,这正是相对路径修复后仍失败的常见原因。
| 修复方式 | 优点 | 主要代价 | 适合场景 |
|---|---|---|---|
保留短 include,配置 -I | 目录调整不必逐文件改源码 | 调用方必须带对搜索路径 | 多模块共享头文件 |
| include 写完整项目相对路径 | 单个文件能看出目标位置 | 仍受 cwd 影响,目录迁移要改源码 | 目录长期稳定的小工程 |
| include 写绝对路径 | 当前机器上路径明确 | 换用户、机器或 CI 后容易失效 | 仅作临时定位,不建议入库 |
本例选择第一种,是因为它把“源码引用什么”与“工程把目录放在哪里”分开。团队协作时可以把搜索目录集中写进同一个脚本或 Makefile,新增头文件目录只改一处;不要依赖每位开发者碰巧从同一个目录打开终端。
还要防止同名头文件遮蔽。多个 -I 都存在 config.svh 时,最终选中的内容可能与预期不同。本实验用宏值自检捕捉这种错误;更大的工程还应保持头文件命名有模块边界,并在修改搜索顺序后跑一次完整回归。
五、复跑 PASS:两种目录约定都得到相同宏值
下面的记录依次覆盖根目录缺少路径、只补一个目录、完整修复,以及切换到 src/ 后的缺失与修复。每个用例都单独打印退出码,最后的总 PASS 表示所有结果符合预期,并不表示故障编译也成功。
两种修复编译都退出 0,对应仿真均打印 PASS: LOCAL_VALUE=42 CFG_WIDTH=6 并退出 0。这不仅证明文件能够打开,还确认两个宏确实展开为预期值;如果包含到同名但内容不同的头文件,仅靠编译成功未必能发现,自检提供了第二层验收。
实验环境
- 机器/系统:Intel x86_64 Mac / macOS 26.5.2。
- 工具版本:Icarus Verilog 13.0 (stable) (v13_0),预处理器与代码生成器同版本。
- 日期:2026-10-30。
- 完整命令:在实验目录运行
bash run.sh;脚本从根目录和src/两种 cwd 执行五组编译,并运行两组修复后的仿真,所有输出合并标准输出与标准错误。
代码获取
测试平台、两个头文件、复现脚本和原始运行记录见公开仓 experiments/ts-iverilog-include-file-not-found。进入目录后运行 bash run.sh,即可复现全部诊断和退出码。
这一篇在知识体系里的位置
本篇属于 iverilog include 文件找不到所在的 EDA 报错排查系列,关注预处理器如何定位头文件。如果文件已找到但宏没有定义,可继续阅读 Icarus Verilog undefined macro 排查;若预处理已经完成、错误指向标识符附近的语法,则转到 iverilog include 文件找不到之后的语法诊断。
总结
iverilog include 文件找不到 时,先记录缺失文件名、源码行号和编译 cwd,再逐项补充 -I。本版实测中,从根目录编译不会自动找到源文件旁的 local.svh;根目录使用 -Isrc -Iinclude 后成功,从 src/ 启动则只需补 -I../include。相对 -I 始终按编译入口理解,最后用宏值自检和退出码确认修复。