Yosys read_verilog include 文件找不到:Can't open include file 排查
更新时间:2026-10-29。FAQ 速查页,属于 EDA 报错排查系列。
ERROR: Can't open include file是 Yosys 读带`include的 Verilog 源码时的高频报错。它和 Yosys Can't open input file 长得很像但根因不同——那个是顶层源文件本身路径不对,本篇是顶层文件读到了、里面 include 的头文件找不到,解决方向是给read_verilog加-I搜索路径。
首屏答案:Yosys 的 read_verilog 处理 `include "defs.v" 这类指令时,默认只在当前源文件所在目录下找,不会自动递归搜子目录。工程里常见的 rtl/ 放源码、inc/ 放公共宏定义的布局,直接 read_verilog rtl/top.v 必炸。修法是给 read_verilog 加 -I <目录> 把 include 目录加进搜索路径,例如 read_verilog -I inc rtl/top.v。多个 include 目录就多写几个 -I,或者用 Verilog 2001 标准允许的 `include "inc/defs.v" 相对路径写法绕开。
本文要回答的问题
Can't open include file报错的完整长什么样,哪一行是关键?- Yosys 的 include 搜索规则到底是什么,为什么子目录里的头文件找不到?
-I选项怎么用,多个 include 目录怎么写?- 除了
-I还有没有别的修法,各适合什么场景?
一、报错原文与现象
最小复现是一个典型的两层目录工程:rtl/top.v 是顶层模块,开头 `include "defs.v" 引用公共宏定义;defs.v 放在旁边的 inc/ 子目录里:
// rtl/top.v: 顶层模块,include 子目录里的 defs.v
`include "defs.v"
module top (
input wire [`DATA_W-1:0] d_in,
output wire [`DATA_W-1:0] d_out
);
assign d_out = d_in + `FIFO_DEPTH;
endmodule// inc/defs.v: 公共宏定义,放在独立 inc/ 子目录里
`define DATA_W 16
`define FIFO_DEPTH 8直接跑 read_verilog rtl/top.v,真实报错如下:
读这段输出抓两个点:第一,1. Executing Verilog-2005 frontend: rtl/top.v 说明顶层文件本身已经找到了——如果顶层都找不到,报的是 Can't open input file,那是另一篇的事;第二,ERROR: Can't open include file defs.v'!紧跟着出现,注意它报的是 include 文件defs.v,不是 rtl/top.v。这个区分是收敛方向的关键:问题不在你给的命令行路径,而在源码里 `` include`` 指令的解析。
二、根本原因:Yosys 的 include 搜索规则
很多从 iverilog 或商业工具转过来的人在这里栽跟头,因为直觉是「include 会从源文件的相对路径找」。Yosys 的实际规则更朴素:
`include "defs.v"写的是裸文件名时,Yosys 按搜索路径列表逐目录找;搜索路径默认只有当前文件所在目录(即rtl/),inc/不在列表里,自然找不到。- 不会自动递归子目录——即使
inc/就在rtl/隔壁,Yosys 也不会主动去翻。这一点和 C 预处理器的行为直觉相反:gcc 对#include "x.h"会先找当前文件目录再找-I路径,但至少「当前文件目录」是有的;Yosys 连「工程根目录」都不认,只认源文件所在的那一层。 -I <dir>每出现一次,就往搜索路径列表里追加一个目录,目录多了就多写几个-I;同名文件被多个-I目录命中时,先写先中,顺序就是优先级。- include 文件名带相对路径(如
`include "inc/defs.v")时,相对的是运行 yosys 时的当前工作目录而不是源文件位置。Makefile 里cd过、或者 CI 里换过工作目录,这类写法就会莫名炸掉,排错时先pwd确认。
下面这张漏斗卡把「报错 → 收敛到根因」的分诊过程画出来,颜色按角色分:蓝是排查动作,橙是可选修法,红是最终根因。
这张卡按「先排除什么、再确认什么」的顺序读:先把「顶层文件找不到」和「include 找不到」两种报错分开,再确认文件真实存在(防止是 git 漏提交这类低级问题),最后才落到 Yosys 的搜索路径机制上。绝大多数情况下,漏斗走到底就是「缺 -I」三个字。
顺带一提,iverilog 对同一份代码的表现不一样:iverilog 的 -I 规则类似,但它对裸文件名 include 的默认搜索行为在部分版本里更宽松,所以有些工程在 iverilog 下仿真一切正常、一换 Yosys 综合就炸——这不是代码问题,是两个工具 include 搜索规则的差异。从仿真切到综合时把 -I 列表对齐,能省掉一类「仿真正常综合报错」的迷惑。
三、修复:加 -I 搜索路径并复跑
修法一行:给 read_verilog 加 -I inc,把 inc/ 目录加进 include 搜索路径:
yosys -p 'read_verilog -I inc rtl/top.v; hierarchy -top top; stat'log 第二段就是这条命令的真实复跑输出:Parsing Verilog input from rtl/top.v、Successfully finished Verilog frontend 依次出现,hierarchy -top top 正确识别顶层,stat 打出 3 根线、1 个 $add 单元——宏 DATA_W 和 FIFO_DEPTH 都正常展开了(d_in + FIFO_DEPTH 综合出一个加法器),include 文件确实被读到。整条线从报错到通过闭环。
工程实践里还有三个等价或互补的写法,按场景选:
| 方案 | 写法 | 适用场景 | 注意点 |
|---|---|---|---|
-I 选项(推荐) | read_verilog -I inc -I third_party/include rtl/top.v | 构建脚本 / 综合 flow,目录结构稳定 | 多个目录多写几个 -I,顺序即搜索顺序 |
| include 带相对路径 | `include "inc/defs.v" | 目录浅、脚本总是从工程根目录跑 | 相对的是运行 yosys 的当前目录,换目录跑就炸 |
| 宏定义直接塞命令行 | read_verilog -D DATA_W=16 rtl/top.v | 只有少量宏、根本不想维护 defs.v | 宏多了不可维护,只适合参数化调试 |
推荐第一条:-I 把「目录布局知识」留在构建脚本里,源码保持裸文件名 include,移植和重命名目录时只改脚本一处。
顺带说两个相关但不同的机制,别混着用:
-I管的是`include文件在哪,-D管的是宏直接定义。如果你只是想让DATA_W这类参数生效,其实可以不要defs.v,直接read_verilog -D DATA_W=16 -D FIFO_DEPTH=8 rtl/top.v——但宏一多命令行就没法看,只推荐临时调参用。read_verilog -lib是另一回事:它把文件当「单元库」读、只补缺失模块不参与顶层,和 include 搜索路径无关。黑盒 IP 场景才用它。- 多个
read_verilog调用时,-I每次调用独立生效——给顶层那次加了-I inc,读 IP 核那次没加,IP 核里的 include 照样炸。Makefile 里把公共选项抽成变量(如YOSYS_RD = read_verilog -I inc -I third_party/include)是常见写法。
四、排查清单
拿到 Can't open include file 后按这个顺序过一遍:
- 看报错对象是 include 还是 input——
Can't open input file是顶层路径问题,本篇不适用。 - 确认 include 文件真实存在:
ls inc/defs.v,排除漏提交、文件名大小写(Linux 文件系统大小写敏感,Defs.v和defs.v是两个文件——从 macOS/Windows 同步过来的工程尤其容易踩)。 - 看 include 写的是裸文件名还是带路径——裸文件名必须配
-I;带路径就先pwd确认运行目录。 - 数一遍所有 include 目录是否都给了
-I——工程里常见的坑是给了inc/漏了third_party/include/。 - Makefile / 综合脚本里检查
read_verilog是否被多处调用——顶层一次、IP 核又一次,两处都要带-I。 - 嵌套 include 也要算在内:
defs.v里再 include 别的文件时,被嵌套的文件同样走搜索路径,-I要给全。
修复后复跑看到 Successfully finished Verilog frontend,并且后续 hierarchy / stat 正常输出,才算闭环——只看不报错不够,宏没展开会导致位宽全错,本例 stat 里的 1 个 $add 单元就是宏正常展开的证据。
实验环境
- 机器/系统:Mac mini M4 / macOS 15(Yosys 跑在 geek-eda:0.2 docker 镜像里)
- 工具版本:Yosys 0.33 (git sha1 2584903a060)
- 日期:2026-10-29
- 完整命令:
yosys -p 'read_verilog rtl/top.v'(报错复现);yosys -p 'read_verilog -I inc rtl/top.v; hierarchy -top top; stat'(修复复跑)
代码获取
复现用源码(rtl/top.v 顶层 + inc/defs.v 公共宏定义,两层目录布局)在公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/yosys-read-verilog-include。装好 Yosys 后在工程根目录跑上面两条命令,先复现 Can't open include file,再用 -I inc 验证修复。
这一篇在知识体系里的位置
本篇是 EDA 报错排查系列中 Yosys 前端报错簇的一员:顶层文件路径都找不到看 Yosys Can't open input file;include 找不到看本篇;模块没声明看 ERROR: Module not declared;综合阶段缺单元库看 Yosys abc 报错缺 cell library。开源工具链报错总入口见 开源 EDA 报错排查导航,整个系列的目录在 EDA 报错排查系列。
总结
ERROR: Can't open include file 的根因是 Yosys read_verilog 默认只在源文件所在目录找 include、不自动搜子目录;修法是给命令加 -I <目录> 把头文件目录加进搜索路径,多个目录多写几个 -I、顺序即优先级;工程上推荐把目录知识留在构建脚本里、源码保持裸文件名 include,嵌套 include 和多次 read_verilog 调用时的 -I 遗漏是两大残余坑;复跑到 Successfully finished Verilog frontend 且 stat 正常出统计(宏展开后的单元数符合预期)才算闭环。