SpyGlass 未定义符号:Undefined symbol 与 Cannot find in symbol table 速查
更新时间:2026-10-22。FAQ 速查页,属于 EDA 工具排错系列,总入口见 开源 EDA 报错排错中心。SpyGlass 是 goal-based 流程:
spyglass -project demo.prj -goals lint/lint_rtl启动后,工具先按.prj把文件逐个读入、编译,再在符号表里做名字解析。本篇处理的是比「文件打不开」更靠后的一类:文件都读进来了,编译期却在符号表里找不到某个名字——宏、参数、package 项、跨层级 localparam。它与姊妹篇 SpyGlass 模块/单元找不到速查 的分界是:那篇断在读文件与模块解析,本篇断在符号表;综合侧的同构问题对照 Design Compiler 未定义符号速查。
本文要回答的问题
Undefined symbol和Cannot find ... in the symbol table分别意味着哪个名字没解析到?- 宏(
`define)和参数(parameter)未定义,为什么九成是 include 目录和-define漏传? - SystemVerilog package 里的类型/参数找不到,读入顺序该怎么排?
- 同名符号被静默覆盖、localparam 跨层级引用、增量缓存陈旧这三类暗坑怎么判?
一、报错原文与现象
SpyGlass 的未定义符号报错出现在 goal 编译阶段的 spyglass.log 与 goal 报告里,常见文案有四种(工程名均为占位):
1. 宏或裸名字在符号表里查不到:
Error: [demo_top.v:42]: Undefined symbol 'DEMO_AW'
Warning: Cannot find macro 'CLKEN_GATE' in the symbol table.2. SystemVerilog package 作用域项解析失败:
Error: [demo_dma.sv:18]: Cannot find 'axi_pkg::id_t' in the symbol table
Error: Undefined symbol 'axi_pkg::RESP_OKAY' referenced in module 'demo_dma'3. `ifdef 引用的宏开关没传进来:
Warning: Undefined macro 'FEATURE_DEMO' used within ifdef, taking false branch4. 参数/跨层级引用解析不到:
Error: Unable to resolve parameter 'demo_top.u_core.DW' in hierarchical reference这四种文案的共同结构是「报缺失的名字 + 引用位置的文件行号」。先别急着改代码:名字带 ` 或全大写(DEMO_AW)的多是宏,带 pkg:: 前缀的是 package 项,带点分层级路径的是跨层级引用——名字形态直接指向前三条排查分支。注意第 3 种常以 Warning 出现:`ifdef 一个未定义宏在 Verilog 语义里不算硬错误,工具默默走假分支,定义被整块剔掉,真正的 undefined 在后面才爆,这是最容易误判的一种。
二、排查决策树
下面这张决策树按「名字从哪来」分叉:宏查 include 与 define,package 项查读入顺序,参数查拼写作用域,最后收进增量缓存;命中哪层修哪层。
读这张图抓住「先分类、再定位」:红色起点是报错现象,三个蓝色判定对应宏、package、参数三类名字来源,按报错名字的形态对号入座即可,不需要从头翻日志;灰色 Q4 专门收「肉眼看代码毫无问题」的缓存陈旧情形;绿色节点是统一复验——必须清掉增量产物全量重跑,避免旧符号表掩盖修复。
三、原因速查表
| 报错现象 | 可能原因 | 处置 |
|---|---|---|
| 全大写宏名 Undefined symbol | 定义它的 .vh 没被 include,或 include 目录缺 | 补 set_option incdir 或 filelist 内 +incdir+ |
宏在 .vh 里有定义仍找不到 | .vh 通过 `ifdef 分段,宏开关没传 | .prj 加 set_option define 或 filelist 加 +define+NAME |
`ifdef 走假分支只给 Warning | 未定义宏在 Verilog 语义里合法,定义被静默剔除 | 显式传 -define;关键分支加 `else `error |
pkg:: 项在符号表查不到 | package 文件读入排在使用者之后,或根本漏读 | package 排到 filelist 最前,确认编译无 Error |
| package 内容少了一半 | package 文件被宏开关裁掉,或多 package 同名 | 核对 define 集合;grep 检查重复 package 定义 |
| 参数名拼写/大小写写错 | 引用处 DATA_W,定义处 DATAW | 以定义处为准全局统一,开 -Wall 类严格选项 |
跨层级 top.u_x.DW 解析失败 | localparam 不向下可见,只在本模块作用域 | 改为参数端口上提,或经接口/打包结构传递 |
| 同名宏/参数多处定义 | 后读入的静默覆盖先读入的,值与预期不符 | 梳理 include 顺序,公共宏集中到唯一 .vh |
| VHDL 包里的常量找不到 | library 未 map 或 package 未先 analyze | set_option lib 显式入库,package 先于使用者 |
| 改对后报错纹丝不动 | work/ 下增量数据库(.sql)陈旧 | 删 work/ 与 goal 目录全量重跑 |
四、逐条排查展开
第一步:先看报错名字的形态,确定它该从哪里来。 宏名全大写或在源码里带 ` 前缀,定义只可能来自两处:`include 的头文件,或命令行/filelist 传入的 `define。参数与 package 项则来自正常编译的 RTL 文件。在动手前先 grep 一遍定义本身:
grep -rn "define[[:space:]]*DEMO_AW" --include='*.v' --include='*.sv' --include='*.vh' .
grep -rn "package[[:space:]]*axi_pkg" --include='*.sv' .grep 不到定义,是交付或拼写问题;grep 得到定义,才进入「SpyGlass 为什么没看见它」。
第二步:宏未定义,查 include 目录与宏开关两件事。 头文件目录走独立通道,.prj 里用 set_option incdir {rtl/include ip/demo/include},或在 filelist 行写 +incdir+rtl/include——只把 .vh 所在目录加进 search 没有用,`include 不查搜索路径。目录对了还报错,就查条件编译:定义若包在 `ifdef DEMO_FULL 里,而工程没传 +define+DEMO_FULL(.prj 侧写 set_option define DEMO_FULL),宏定义连同分支一起被预处理器删掉,表现为「头文件 include 成功、宏依然 undefined」。建议关键开关分支补 `else `error "DEMO_FULL required",让缺失在编译期直接暴露,而不是拖到符号解析期。
第三步:pkg:: 找不到,只查读入顺序与漏读。 SystemVerilog 要求 package 先编译后引用,filelist 固定按「package/.vh → 子模块 → 顶层」三段组织,axi_pkg.sv 必须排在 demo_dma.sv 之前。还要看 package 文件自身编译有没有被跳过:它若在更早就报了 Error(缺 include、宏未定义),SpyGlass 跳过该文件,符号表里就没有这个 package,使用者统一报 Cannot find 'pkg::item' in the symbol table。排查时在完整日志里从前往后搜 Error,别只盯最后的未定义汇总。
第四步:参数问题查拼写、作用域与同名覆盖。 拼写和大小写在 Linux 上是硬性区分,DATA_W 与 DATAW 是两个名字,以定义处为准全局统一。作用域上牢记一条:parameter 随参数端口可向下传递,localparam 是模块内局部量,其他模块不能用 top.u_core.DW 这样的分层路径去引用它——跨层级下钻在 lint 阶段就会被拒;需要共享就把常量上提成顶层参数经参数端口传下去,或放进 package 统一 import。同名覆盖则更隐蔽:两个 .vh 都 `define 了同名宏,后读入的静默生效,代码「看着对、值不对」;公共宏应集中到唯一一份头文件,其余只 include 不重定义。
第五步:代码确定无误,清增量缓存全量重跑。 SpyGlass 在工程的 work/(及 goal 目录)下维护增量编译数据库,设计大时编译很快,但文件增删、filelist 顺序调整、宏开关变更后,旧数据库可能与当前文件集不一致:定义早已补上,符号表仍是旧的,报错纹丝不动。这类问题的判据是「命令行直接全量编译能过、走 goal 增量就报 undefined」。处置不犹豫:删掉 work/ 与对应 goal 目录后全量重跑。修复验证也以干净目录的日志为准——0 Error、未定义符号计数归零,再进入违例治理;违例分级与 waiver 纪律见 Lint 与 SpyGlass waiver 签核。
五、预防与纪律
- filelist 三段顺序固化并脚本化生成:package/头文件 → 子模块 → 顶层;生成后逐行
test -f自检,手维护的列表必然随代码增删腐烂。 - 新 IP/新模块接入一次配齐五项:
read_file条目、search目录、incdir目录、define宏集合、VHDL 库映射,缺一项就可能以未定义符号形式爆出来。 - 宏开关留兜底:关键
`ifdef分支补`else`error,把「宏没传」从静默走假分支变成编译期硬报错。 - 公共符号唯一定义:宏集中到一份
.vh,共享常量进 package,杜绝同名多处`define的静默覆盖。 - 动 filelist/宏之后干净重跑:清
work/与 goal 目录全量编译,不以增量结果作为修复结论;完整日志连同.prj、filelist 一起存档,改一处重跑一次。
这一篇在知识体系里的位置
本篇是 EDA 排错速查簇的一员,总入口见 开源 EDA 报错排错中心;上一道工序的「文件/模块找不到」见姊妹篇 SpyGlass 模块/单元找不到速查,综合工具侧的同类问题对照 Design Compiler 未定义符号速查;SpyGlass 的规则体系与门禁定位见 RTL 静态检查(Lint):SpyGlass 规则体系与基线门禁,违例签核见 Lint 与 SpyGlass waiver 签核。
一句话总结
SpyGlass 未定义符号的排查按名字形态三分支收敛:全大写宏名查 include 目录与 -define 宏开关(警惕 `ifdef 静默走假分支),pkg:: 项查 package 文件是否漏读且排在使用者之前,参数查拼写大小写、localparam 不可跨层级引用与同名宏静默覆盖;代码确定无误就删 work/ 增量缓存全量重跑——配合 filelist 三段顺序脚本化与公共符号唯一定义,这类报错基本一轮清零。