Formality 编码乱码报错:中文注释、BOM 与 locale 速查
更新时间:2026-10-23。FAQ 速查页,属于 EDA 工具排错系列。Formality(fm_shell)做形式验证第一步是
read_verilog/read_vhdl把参考设计与实现设计读进来,而读入阶段的词法解析对字节极其诚实:注释里一个 GBK 字节、文件头一个 BOM、行尾一个\r,都能让解析器在无辜的代码行上报错。特征是报错行号指向的 module/always 完全正常,真正的坏字节藏在注释、文件头或行尾。同系列对照见 SpyGlass 编码乱码报错;如果「找不到文件」类的读入失败,先看 Formality 路径不存在。
本文要回答的问题
read_verilog报 illegal character,指向的行明明是正常的 always 块?- 同一份 RTL 在 Windows 上编辑后,Linux 下 fm_shell 一读就炸?
- fm_shell 日志里中文路径、中文注释全变问号,但文件本身没坏?
- Tcl 脚本里写了中文注释或中文路径,source 时报 syntax error?
一、报错原文与现象
Error: illegal character '0xd6' in file ./rtl/ctrl_ref.v at line 42 (near 'always')
Error: ./rtl/top.v:1: syntax error at or near token 'module' (VER-936)
Error: Cannot open file 'rtl/ctrl.v\r' (No such file or directory)
Warning: non-ASCII characters in script ./setup/run_fm.tcl replaced with '?'
(fm_shell 日志中显示:读取设计 /proj/䏿–‡è·¯å¾„/top.v 失败)第一条报错的第 42 行通常是一条普通的 always @(posedge clk)——坏的是它上方某行中文注释里的 GBK 字节,词法器咬到非法字节后状态错乱,把锅甩给下一个 token。第二条是典型 BOM:报错信息里  正是 EF BB BF 三字节被按 Latin-1 显示出来的样子。第三条路径尾巴上的 \r 来自 CRLF 换行,文件名实际多了一个不可见回车符。编码问题的第一戒律:报错行往上翻,先看注释、文件头和行尾,再看代码。
还有一个容易误判的现象:同一份文件在 GUI(formality 图形界面)里打开看代码完全正常,批跑 fm_shell 却报错——这不是工具不稳定,而是 GUI 按本地编码猜对了字节、命令行解析器按 UTF-8/ASCII 严格解析。判别试金石:把报错文件换一台机器、换一个编辑器打开看,不同环境下「看起来」不一样,九成是编码问题而不是设计问题。
另一个常见的误判方向是把编码报错当语法报错去改代码:工程师盯着第 42 行的 always 块反复检查括号配对、敏感列表写法,折腾半天一无所获——因为那一行本来就是对的。只要报错信息里出现 0xd6、0xc4 这类十六进制字节值,或者报文里夹着 、ä¸ 这类「西文重音字母串」,就可以立即停止语法方向的排查,直接转到本篇的字节验证流程,平均能省半小时冤枉路。
二、可能原因总览
| 现象特征 | 可能原因 | 处置 |
|---|---|---|
| illegal character 指向含中文注释的段落 | 注释按 GBK 保存 | iconv 转 UTF-8 |
| 第 1 行 syntax error、报文含  或 \xEF\xBB\xBF | UTF-8 BOM 头 | sed 去 BOM 重存 |
| Cannot open file 路径尾巴带 \r | Windows 编辑引入 CRLF | dos2unix 转 LF |
| 日志/报告里非 ASCII 全变 ? 或问号 | locale 是 POSIX/C | export LANG=en_US.UTF-8 |
| Tcl 脚本中文注释后 source 报 syntax error | 脚本本身是 GBK 或混合编码 | 转 UTF-8 并统一团队模板 |
| 终端显示乱码但落盘 log 正常 | SSH 客户端码页非 UTF-8 | 改终端解码,不动服务器文件 |
| 同一文件 A 人跑正常 B 人跑报错 | 两人编辑器保存编码不同 | 仓库级 UTF-8 无 BOM 强制 |
三、排查决策图
编码类报错的排查顺序有一条铁律:先验字节、再查 locale、最后怀疑查看端。下面这张图把「现象 → 判别 → 修复 → 防复发」串成一条决策路径,红色是入口现象与命中的根因,蓝色是排查动作,橙色是修复动作,紫色是收尾防复发。主线是先用 od/file 把字节事实钉死,这一步能同时区分 BOM、GBK、CRLF 三种情况,是整张图性价比最高的动作;locale 与终端两条支线都发生在「源文件本身干净」之后,别在文件没验干净前就去改环境。
四、逐条排查展开
第一步永远是看字节,不是看内容。 三条命令把编码事实钉死:
file rtl/ctrl_ref.v
head -c 16 rtl/ctrl_ref.v | od -c | head -2
grep -nP '[^\x00-\x7F]' rtl/ctrl_ref.v | head -5file 会直接告诉你是 UTF-8 Unicode (with BOM) 还是 Non-ISO extended-ASCII(后者在中文团队八成是 GBK);od -c 看文件头,357 273 277(即 EF BB BF)就是 BOM;最后一条把全文非 ASCII 字节列出来,注释里的中文立刻现形。GBK 与 UTF-8 的判别技巧:GBK 中文是连续的两个高位字节(如 D6 D0),UTF-8 中文是三个一组且首字节落在 E4-E9 段——od 输出里看到 E4 B8 AD 这种三字节节奏就是 UTF-8,看到 D6 D0 两字节节奏就是 GBK。
GBK 注释:Windows 编辑器的历史遗产。 中文团队的 RTL 注释大量诞生于 Windows 记事本/老编辑器时代,默认按 GBK 保存;Formality 的 Verilog 解析器按 UTF-8(或 ASCII)读,GBK 字节落在注释里通常不炸(注释整体被跳过),但一旦某个 GBK 尾字节恰好是 0x2F(/)或 0x5C(\),注释边界就被吃掉,后面的代码被当成注释的一部分,词法器在错误位置重新同步,报出指向无辜行的 illegal character。处置一把梭:
iconv -f GBK -t UTF-8 rtl/ctrl_ref.v -o rtl/ctrl_ref.v.utf8 && mv rtl/ctrl_ref.v.utf8 rtl/ctrl_ref.v注意 iconv 转一半报 illegal input sequence 中断时,千万不要直接覆盖原文件——那是混合编码文件(一半 GBK 一半 UTF-8,典型成因是老文件被新编辑器追加过注释),转出来的结果是残缺的。先 cp 备份,再定位断点分段转码,或者干脆让文件责任人用编辑器重存一份干净 UTF-8。
BOM 头:咬第一行 module 的隐形三字节。 某些编辑器另存为 UTF-8 时会在文件头写入 EF BB BF。Formality 把它当普通字符读,于是 read_verilog 在第一行 module top; 前面多出一个不可见前缀,VER-936 语法错误指向第 1 行——而肉眼看第 1 行毫无问题。判别见上面的 od -c;处置:
sed -i '1s/^\xEF\xBB\xBF//' rtl/top.vCRLF:行尾那个看不见的回车符。 在 Windows 上编辑过的 filelist、Tcl 脚本、甚至 RTL 源文件,换行符是 \r\n。后果很分裂:filelist 里每行路径尾巴带 \r,Formality 报 Cannot open file 'rtl/ctrl.v\r'——路径明明存在却说找不到,这条最容易被误判成路径问题去白查一圈(与 Formality 路径不存在 的区分点就在这里:ls 能看到文件、工具却说读不到,先验行尾);Tcl 脚本里 \r 还可能让 source 报莫名其妙的 syntax error。处置:
dos2unix project.f run_fm.tcllocale 决定日志里的非 ASCII 是字还是问号。 fm_shell 进程在 POSIX/C locale 下,多字节字符按单字节拆,日志里的中文注释、中文路径全变 ? 或干脆截断。查环境:
locale | grep -E "LANG|LC_ALL"处置:作业封装脚本统一 export LANG=en_US.UTF-8(兼容面最宽)或 zh_CN.UTF-8(日志要保中文)。注意提交到计算集群的作业继承提交时刻的环境——交互 shell 改了没用,必须写进提交封装脚本;GUI 会话则吃启动终端的 locale。还有一个隐蔽变体:locale 是 UTF-8 但 SSH 客户端按 GBK 解码,屏幕上 log 全是乱码、落盘的 log 文件其实完好——判别方法是把 log 拉到本地用编辑器按 UTF-8 打开,正常就只是终端显示问题,别去改服务器上的文件。
Tcl 脚本里的中文:注释与路径双重坑。 run_fm.tcl 里写中文注释,脚本本身若按 GBK 保存,source 时会遇到与 RTL 相同的词法问题;脚本里写中文路径(如 set rpt_dir /proj/报告输出/),在非 UTF-8 locale 下路径字节被错误解码,file mkdir 创建出来的目录名和你看到的不是同一个。纪律:Tcl 脚本同样按 UTF-8 无 BOM 保存,路径尽量纯 ASCII,报告目录名用英文加日期。
五、预防与纪律
- RTL 与脚本三统一:UTF-8、无 BOM、LF 换行,写进编辑器团队模板,CI 里用
file批量扫一遍仓库就能出编码合规报告。 .gitattributes强制*.v text eol=lf、*.tcl text eol=lf:Windows 侧 clone 出来也是 LF,从源头消灭 CRLF。- 作业封装统一 export locale:提交脚本里写死 LANG/LC_ALL,不依赖提交人的交互环境。
- iconv 转码前先备份原件:混合编码文件转码必断,断点之前的内容丢了找不回。
- 第三方 IP 入库先验编码:IP 交付物的注释编码五花八门,入库门禁加一道
file扫描,非 UTF-8 的先转码再合入,别把别人的历史包袱接进自己仓库。 - 路径与目录名纯 ASCII:中文路径在不同 locale 下字节表现不一致,是最难复现的一类问题,从命名习惯上直接规避。
这一篇在知识体系里的位置
本篇是 EDA 排错速查簇中「字符编码」节点的 Formality 篇,与 SpyGlass 编码乱码报错 同构互参;编码导致的「文件存在却读不到」需与真正的路径问题区分,见 Formality 路径不存在;读入成功后的设计匹配类报错(如模块名对不上)见 Formality 模块未找到。
一句话总结
Formality 编码乱码报错按「文件字节 → 进程 locale → 查看端」三层排查:od -c 看字节节奏一把区分 BOM(头三字节 EF BB BF)、GBK 注释(两字节高位节奏)与 CRLF(行尾 \r);GBK 用 iconv 转 UTF-8、BOM 用 sed 去头、CRLF 用 dos2unix、locale 在作业封装里统一 export UTF-8、终端乱码只改 SSH 客户端解码;根治靠「UTF-8 无 BOM + LF」三统一写进编辑器模板与 .gitattributes,让编码问题从玄学变常识。