Formality 找不到文件路径:Can't open file 速查
更新时间:2026-10-23。FAQ 速查页,属于 EDA 工具排错系列。fm_shell 在
read_verilog、read_file -f filelist.f、read_db、read_svf读参考设计或实现设计时报「Can't open file / File not found」,文件十有八九就在磁盘上——Formality 找不到它的原因集中在五类:search_path变量没设导致相对文件名无处可循、filelist 里的相对路径是相对于 fm_shell 启动目录而非 filelist 自身所在目录、$env(VAR)或 Tcl 变量引用的环境变量没展开、ip/current类活动软链跨机悬空、路径含空格或中文被 Tcl 当成多个参数。报错显示模块已读入但例化缺定义时,是另一个问题,见 Formality 模块未定义 module not found。
本文要回答的问题
- 文件明明存在,为什么 Formality 说 Can't open file?
search_path设了和没设,read_verilog 的行为差在哪?- filelist.f 里写的相对路径,到底相对谁?
- 本地能跑、批量回归上找不到文件,怎么查?
- 路径里有空格或中文,fm_shell 里要怎么写才安全?
一、报错原文与现象
Error: Can't open file 'chip_top.v' (UID-xx)
File not found in search path.
Error: Can't open file '../rtl/chip_top.v' (UID-xx)
Warning: Unable to resolve environment variable 'RTL_HOME'.
Error: Can't open file '/proj/chip/ip current/pll/pll.v' (UID-xx)
Error: Can't open file 'filelist.f' (UID-xx)先盯住报错里那条路径的原始形态:裸文件名(不带任何目录)是 search_path 问题;以 .. 或 ./ 开头的是相对路径与启动目录问题;含变量名的是展开问题;报错路径里出现空格被截成两截的是 Tcl 参数分割问题;报错路径指向软链名的是链接失效。五种形态五条通道,先归类再动手。
二、可能原因总览
| 报错现象 | 可能原因 | 处置 |
|---|---|---|
| 裸文件名找不到 | search_path 没加对应目录 | set_app_var search_path 补上并 echo 验证 |
| 本地能跑、CI 找不到 | CI 启动目录与本地不同 | 调用 fm_shell 前 cd 固定工程根 |
| filelist 里相对路径报错 | 相对路径按启动目录解析 | filelist 用绝对路径或统一启动目录 |
| 变量路径展开成空 | shell 变量没 export / Tcl 内没 env() | env 单一来源 + $env(VAR) 显式取值 |
| 昨天能跑今天不行 | 软链指向已删除的旧版本目录 | readlink -f 查链接目标有效性 |
| 换机器就找不到 | 挂载点不同(/proj vs /nfs/proj) | 路径走变量拼、不写死挂载点 |
| 含空格路径报错 | Tcl 把空格当参数分隔 | 路径整体加大括号或双引号 |
| 中文目录下找不到 | 编码或 locale 差异 | 工程路径全 ASCII,治本地貌 |
三、排查决策图
动手改任何东西之前,先按报错路径的形态把问题分流到正确的通道,每条通道的判别动作都是一条命令级。
路径找不到 -> 报错路径形态分流
报错路径形态分流 -> 裸文件名: printvar search_path
报错路径形态分流 -> 相对路径: pwd实测启动目录
报错路径形态分流 -> 含变量路径: echo env变量并ls验证
报错路径形态分流 -> 软链或换机失效: readlink与mount核对
报错路径形态分流 -> 含空格或中文: 引号包裹重试
printvar search_path -> 修复: search_path补全设计目录
pwd实测启动目录 -> 修复: cd固定根目录或filelist绝对路径
echo env变量并ls验证 -> 修复: env脚本统一source并export
readlink与mount核对 -> 修复: 重建软链与统一挂载路径
引号包裹重试 -> 修复: 大括号包裹与路径改ASCII
修复: search_path补全设计目录 -> 路径规范建档防复发
修复: cd固定根目录或filelist绝对路径 -> 路径规范建档防复发
修复: env脚本统一source并export -> 路径规范建档防复发
修复: 重建软链与统一挂载路径 -> 路径规范建档防复发
修复: 大括号包裹与路径改ASCII -> 路径规范建档防复发
路径找不到.style: { fill: "#FDECEC"; stroke: "#D9534F" }
报错路径形态分流.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
裸文件名: printvar search_path.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
相对路径: pwd实测启动目录.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
含变量路径: echo env变量并ls验证.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
软链或换机失效: readlink与mount核对.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
含空格或中文: 引号包裹重试.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
修复: search_path补全设计目录.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
修复: cd固定根目录或filelist绝对路径.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
修复: env脚本统一source并export.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
修复: 重建软链与统一挂载路径.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
修复: 大括号包裹与路径改ASCII.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
路径规范建档防复发.style: { fill: "#F1ECFD"; stroke: "#7A5BD4" }颜色按语义分角色:红是报错入口,蓝是排查动作,橙是修复,紫是收尾防复发。读这张图抓一条主线:报错原文里路径的写法就是分流依据——裸文件名查 search_path,相对路径查启动目录,含变量查展开,软链或换机失效查链接与挂载,含空格或中文查引号包裹。先花三十秒形态归类,比一头扎进工程目录翻文件快得多。
四、逐条排查展开
裸文件名全靠 search_path 认领。 read_verilog chip_top.v 这类只给文件名的调用,Formality 按 search_path 变量里的目录列表逐个目录找,没找到就报 Can't open file。新开的 fm_shell 会话 search_path 默认只有当前目录,脚本里如果忘了继承设置就会失败。判别:
printvar search_path如果列表里没有设计文件所在目录,补上再读:
set_app_var search_path ". ./rtl ./ip $env(DESIGN_HOME)/lib"
read_verilog chip_top.v改完重读,报错消失即闭环。工程级根治:把 search_path 的设置收进工程统一的 fm 公共脚本,任何 run 脚本开头 source 它,不让每个脚本各自维护一份。
filelist 的相对路径是相对「启动目录」,不是相对 filelist 自己。 这是 Formality 场景里最经典的误会:read_file -f ./cfg/filelist.f 能读到列表,但列表里一行 ../rtl/chip_top.v 的解析基准是 fm_shell 启动时的当前目录,与 filelist.f 躺在 ./cfg/ 没有任何关系。交互调试时你恰好在工程根启动,路径碰巧对;回归系统从构建工作区启动,同一份 filelist 立刻全灭。判别:在报错环境里跑 pwd,再手工拼「启动目录 + filelist 里的相对路径」用 ls 验证。根治二选一:调用 fm_shell 的包装脚本第一行 cd 到固定工程根目录;或 filelist 生成时用环境变量拼绝对路径,彻底摆脱对启动目录的依赖。
环境变量先 echo 再谈其他。 Tcl 脚本里 $env(RTL_HOME) 展开成空时,拼出来的路径会变成 /ip/pll/pll.v 这种幽灵路径。三类高发错法:当前 shell 没 source env 脚本;变量定义在 Makefile 里(make 子进程有值,你直接敲 fm_shell 的 shell 没有);回归系统用不同 shell 起进程、env 脚本语法只对其中一种成立。判别:
echo $RTL_HOME
ls $RTL_HOME根治只有一个:路径变量单一来源,交互与回归统一 source 同一个 env 脚本,Tcl 内一律用 $env(VAR) 显式取值,读完立刻 ls 一次验证。
软链失效是「昨天还好好的」的标准指纹。 工程里常见 ip/current -> ip/pll_v3.2/ 这类活动软链,旧版本目录被清理后链接悬空,报错形态与文件不存在完全一样;换机器场景再加一层——开发机挂 /proj/chip/,回归节点挂 /nfs/proj/chip/,同一个软链在一台有效、另一台悬空。判别:
readlink -f <软链路径>
mount | grep -i projreadlink -f 解析不出就是断了,重建指向有效交付目录;挂载点不同则根治于「路径里不写挂载点,全部从变量拼」。
含空格或中文的路径是 Tcl 参数分割的坑。 /proj/chip/ip current/pll/pll.v 这类路径在 Tcl 命令里不加引号会被当成两个参数,报错路径正好在空格处截断——这是本类问题最硬的指纹。临时处置是给路径整体加大括号或双引号:
read_verilog {"/proj/chip/ip current/pll/pll.v"}但工程级根治是不要给这条路留活口:工程路径全 ASCII、无空格,写进工程公约;中文目录除了引号问题还叠加 locale 差异,回归机上 locale 不一致时同一条路径行为不可预期,一律绕道。
五、预防与纪律
- search_path 集中管理:只在工程公共 fm 脚本里 set_app_var,run 脚本一律 source,不散落各自维护。
- 包装脚本第一行 cd 固定工程根:相对路径只允许出现在「工作目录被脚本强制固定」的前提下。
- filelist 生成走绝对路径:由生成脚本用环境变量拼,人手工维护的列表只允许裸文件名加 search_path 模式。
- 路径变量单一来源并 export:env 脚本只有一份,交互与回归统一 source;Makefile 里的变量从 shell 继承。
- 软链指向纳入交付检查:current/latest 类活动链接每次交付自动
readlink -f验证可达。 - 路径全 ASCII 无空格公约:新目录命名在 review 阶段拦下空格与中文,不留 Tcl 引号陷阱。
- 启动工具前存在性断言:流程脚本对 filelist、svf、库文件逐一
test -e,缺哪个报哪个,不把 shell 层的错带进 fm_shell 日志。
这一篇在知识体系里的位置
本篇是 EDA 排错速查簇中「路径解析」节点的 Formality FAQ 页,与 SpyGlass 找不到文件路径 同族;文件读进来之后的下一层问题——模块定义缺失见 Formality 模块未定义 module not found,链接阶段符号解析失败见 Formality 未定义符号 undefined symbol。
一句话总结
Formality 报找不到文件路径时先看报错里路径的形态分流:裸文件名用 printvar search_path 查搜索路径并补全;filelist 里的相对路径相对启动目录而非列表自身,包装脚本第一行 cd 固定工程根或列表改绝对路径根治;含变量的路径先 echo 再 ls,变量只在单一 env 脚本维护并 export,Tcl 内用 $env(VAR) 显式取值;软链与换机失效用 readlink -f 和 mount 核对,路径不写死挂载点;含空格或中文的路径用大括号包裹应急、公约层面全 ASCII 根治;最后用「search_path 集中管理 + 软链交付检查 + 存在性断言」三条纪律防复发。