EDA TCL 脚本入门:读设计、综合、select、rename、写报告在 yosys 里逐条实跑
更新时间:2026-10-25。本文是「集成电路大类 · EDA 实验坊」入门层(beginner)的一篇实验文。几乎所有 EDA 工具——开源的 yosys、OpenROAD,商业的 Design Compiler、Innovus——都把 TCL 风格的脚本当作驱动接口,学会「用脚本驱动工具」比记住任何一条命令都值钱。姊妹篇 yosys synth 流程拆解 拆的是
synth内部 proc/fsm/techmap/abc 每一步的资源变化,回答「综合流程内部发生了什么」;本篇回答一个更靠前的问题——**第一次写 EDA 脚本,每行命令到底干了什么、怎么确认它干成了?**我们把读设计、层级检查、统计、对象选择、改名、综合、写网表写成四个小脚本,在 yosys 0.9 里逐个实跑,全部输出原样展示。
本文要回答的问题
- 为什么 EDA 工具清一色用 TCL 风格的脚本接口,而不是各自发明一套?
- 一个
.ys脚本从read_verilog到write_verilog要经过哪几个标准动作? select选中的到底是模块还是单元?rename之后层级报告会发生什么变化?- 怎么用脚本的退出码和 stat 报告确认每一步真的成功了?
先给结论卡,四个数字全部来自后文真实运行日志:
读这张卡抓住主线:四个脚本对应四个递进动作——读进来、看清楚、改一下、综合出去,每个动作都有可核对的输出证据。下面先讲实验设计,再逐脚本看实录。
一、为什么 EDA 工具都用 TCL 风格的脚本
先回答一个新手常问的问题:为什么不是 Python、不是 Lua,偏偏是 TCL?历史原因之外有两个工程上的硬理由。其一,TCL 的「一切皆字符串 + 命令即列表」模型天然适合 EDA 场景——select top_demo/u_add 这种带斜杠的层次路径、{*}acc? 这种通配符,在 TCL 里不需要任何转义和解析框架,命令本身就是参数列表。其二,EDA 流程是「几十上百条命令按序驱动一个巨大的内部数据库」,脚本的职责是编排而不是计算,TCL 这种极简语法反而让脚本读起来像操作清单。yosys 的 .ys 脚本严格说是 TCL 的子集风格(命令逐行执行、支持注释),新版本 yosys 还可以直接 yosys -T script.tcl 跑完整 TCL。
理解了这一点,写 EDA 脚本的心法就一句话:每一行都是对设计数据库的一次操作,操作完要有办法查证。查证手段主要是三个——stat 报告(数量变了没有)、select -list(对象在不在、叫什么名)、以及 shell 层面的退出码(命令成了没有)。三者分别对应「数据、对象、流程」三个层面,缺一不可。
这套心法换个工具完全通用:OpenROAD 的流程脚本同样是「读 LEF/DEF 库与网表 → 布局 → 布线 → 写报告」的一串命令逐行驱动,本文的 s1 到 s4 换个命令名就是后端流程的雏形——读入、查证、修改、产出四段式在任何 EDA 工具里都成立。所以入门阶段与其背命令,不如把「每个动作配一个查证手段」的肌肉记忆练出来。
二、实验设计:一个两级小设计 + 四个递进脚本
演示设计故意保持最小但结构完整:一个 8 位加法器模块 sub_add,顶层 top_demo 例化它,再用一个二选一选择器在「和」与「直通 a」之间切换:
module sub_add (input wire [7:0] a, input wire [7:0] b, output wire [8:0] y);
assign y = a + b;
endmodule
module top_demo (
input wire [7:0] a, input wire [7:0] b,
input wire sel, output wire [8:0] y
);
wire [8:0] sum;
sub_add u_add (.a(a), .b(b), .y(sum));
assign y = sel ? sum : {1'b0, a};
endmodule四个脚本按递进关系组织:s1 只读设计看层级,s2 加 proc 后打印统计,s3 演示 select/rename 对象操作,s4 跑完整 synth 并把网表写出来。数据流用一张图看:
读这张图抓住递进逻辑:s1 回答「设计读进来了吗」(灰色是输入,蓝色是查看类操作),s2 回答「进程展开成网表了吗」,s3 回答「我能精确定位并修改对象吗」(橙色是修改类操作),s4 回答「能综合出网表并交付吗」(绿色是产出)。这四问覆盖了日常 EDA 脚本八成的使用场景。
三、逐条实跑:读设计、看统计、改对象
s1 脚本只有三行,却是所有 EDA 脚本的骨架:
read_verilog demo.v
hierarchy -top top_demo
lsread_verilog 把源码解析成 AST 再转成 yosys 的内部表示 RTLIL;hierarchy -top top_demo 指定顶层并检查例化关系——日志里它打印 Top module: \top_demo 和 Used module: \sub_add,确认例化链完整、没有缺模块;ls 列出当前设计里的模块,输出就两行:sub_add 和 top_demo。这一步退出码 0,说明设计「读进来了」。
别小看这三行的信息量。hierarchy 那两行 Top module / Used module 是脚本化流程里第一道质量门:模块名拼错、文件漏读、例化端口对不上,都会在这一步现形,而不是烂到综合后段才报一个看不懂的错。工程脚本里常把它升级成 hierarchy -check -top top_demo——多一个 -check,发现缺模块或未解析例化时直接报错退出,把「查到问题」和「强制停下来」合二为一。s3 脚本里实际用的就是这个带 -check 的形式。
s2 在 s1 基础上加了两行 proc 和 stat。proc 把 always 块之类的进程结构转成网表原语(本次设计是纯组合 assign,proc 主要做决策树清理);stat 打印统计——顶层 top_demo 有 2 个单元(1 个 $mux 选择器 + 1 个 sub_add 例化),sub_add 内部 1 个 $add 加法器单元。此时设计还是「行为级原语」,没有映射到具体门。
stat 报告的读法也在这时说清,后面每一步都靠它查证:每个模块一段,Number of cells 是单元总数,下面按类型分列($mux、$add 这类带 $ 前缀的是 yosys 内部行为级原语,映射后才变成 $_AND_ 这类门级单元);报告末尾的 === design hierarchy === 段给出全设计汇总——s2 阶段全设计只有 3 个单元,记住这个数字,第四节综合完会变成 51。wires 与 wire bits 两列分别数「信号线根数」和「总位数」,8 位加法器的数据通路会让 wire bits 明显大于 wires,两列差异大是正常的数据通路特征。
s3 是本文的重点,演示对象定位与改名,先上原样日志摘录:
top_demo
top_demo/$ternary$demo.v:14$2_Y
top_demo/sum
top_demo/y
top_demo/sel
top_demo/b
top_demo/a
top_demo/$ternary$demo.v:14$2
top_demo/u_add
sub_add
sub_add/$add$demo.v:3$1_Y
sub_add/y
sub_add/b
sub_add/a
sub_add/$add$demo.v:3$1
Renaming module \sub_add to \sub_adder.这是 select -list 的输出,信息量很大。它列出了设计数据库里的每一个对象,而且对象名自带层次路径:top_demo/u_add 是顶层里那个加法器例化实例,top_demo/$ternary$demo.v:14$2 是综合器给第 14 行三目运算符自动生成的中间单元名——$ternary 前缀、源文件名、行号全在名字里,这就是为什么 EDA 报告里的单元名又长又怪:它们是溯源信息。select -assert-any sub_add 这类断言写法是脚本健壮性的关键——断言「此刻必须能选中 sub_add」,选不中立即报错,避免脚本在后面几十步才以奇怪的方式炸掉。rename 之后日志只有一行 Renaming module \sub_add to \sub_adder,随后的 stat 报告里模块名已经变成 sub_adder,而 === design hierarchy === 段只剩 top_demo 一行顶层计数——改名是数据库级的操作,全设计即时生效。
四、综合与交付:synth 一把梭 + 写网表
s4 是工程上最常用的形态:一个 synth -top top_demo 跑完二十多个内部 pass(proc、opt、fsm、techmap、ABC 映射等),随后 stat 看结果、write_verilog/write_json 把网表落盘。四脚本连跑的完整实录如下(截取尾部综合统计与交付段):
读这份实录抓三个位置:一是 === design hierarchy === 段的单元总数 51——s2 阶段只有 3 个行为级单元,综合后展开成 51 个门级单元($_AND_/$_OAI3_/$_XOR_ 等),这就是「行为原语映射到具体门」的直观体现;二是 write_verilog 和 write_json 两个 backend pass 各打印一行 Dumping,shell 随后 ls -l 确认 synth_out.v(2763 字节)与 synth_out.json(30476 字节)真实落盘;三是末尾的 SKIP: openroad not installed——本次镜像只装了 yosys,OpenROAD 探测步骤按设计跳过,脚本用 command -v 探测而不是硬调,这是可移植脚本的写法。最终 RESULT: PASS,四个脚本退出码全 0。
值得多说一句的是网表写出这一步的工程意义:write_verilog 产出的门级网表是综合与后端之间的标准交接物——布局布线工具(OpenROAD 或商业 P&R)读的就是它;write_json 则是给自定义流程留的口子,网表以结构化 JSON 表达,用任何语言写个脚本就能做自己的分析(统计扇出、找关键路径候选、生成定制化报告)。养成「综合完同时写 Verilog 和 JSON 两份」的习惯,后面接什么工具都不慌。另外注意 synth 一把梭与姊妹篇逐步执行的关系:日常迭代用 synth 省事,出问题时才拆成单步逐个 stat 对比定位——两种形态脚本都该会写。
把四个脚本的关键产出并排成表,方便对照日志核对:
| 脚本 | 核心命令 | 关键输出证据 | 验证方式 |
|---|---|---|---|
| s1_read | read_verilog + hierarchy + ls | 2 个模块:sub_add / top_demo | 退出码 + ls 输出 |
| s2_stat | + proc + stat | top_demo 2 单元($mux + 例化) | stat 报告 |
| s3_select_rename | select -assert-any / -list / rename | sub_add → sub_adder 一行改名日志 | select 断言 + 改名后 stat |
| s4_synth_write | synth + write_verilog + write_json | 51 个门级单元、两个网表文件落盘 | stat + ls -l |
五、上手建议与常见坑
把这次实跑换成几条可直接搬走的习惯。第一,脚本里每一步都留查证点——stat、select -list、ls 的输出顺手 tee 进日志,出问题时能精确定位是哪一步开始不对的,而不是面对一个空网表猜。第二,用断言代替祈祷:select -assert-any <对象> / -assert-none <对象> 是 TCL 脚本里最便宜的防御性编程,对象不存在当场报错。第三,退出码要接住——run 脚本里每个 yosys -s 之后都检查了 rc=0,EDA 流程里一步失败会产生级联的假象错误,早停早查。第四,层次路径里带 $ 的自动命名单元(如 $ternary$demo.v:14$2)在 shell 里引用时要小心变量展开,写 TCL 脚本内部没问题,包到 shell 命令行里要加引号。
再补一条面向下一步的话:本文四个 .ys 只用了「逐行命令」这个最小子集,真正的 TCL 还有变量(set top top_demo)、循环(foreach)、条件(if)和算术(expr)——新版本 yosys 用 yosys -T 跑完整 TCL 后,「对一组顶层模块各综合一遍并汇总 stat」这种批量活就能写成十行循环,而不是复制粘贴几十遍。本文的四个脚本就是未来那个循环的四个迭代体,先手工跑通再自动化,顺序不要反。
想继续深入,synth 内部每一步(proc/fsm/techmap/abc)的资源变化曲线见 yosys synth 流程拆解,stat 报告各字段的读法见 yosys stat 报告解读,常见报错的排查见 yosys 常见报错。
这一篇在知识体系里的位置
- 同层配套:yosys synth 流程拆解(synth 内部 pass 的逐步实测);yosys stat 报告解读;yosys 常见报错;
- 上游衔接:yosys/verilator 一日上手(工具链安装与最小闭环);
- 下游应用:脚本化综合是 yosys show 网表可视化 与后续 OpenROAD 后端流程的共同底座。
一句话总结
EDA TCL 脚本的心法是「逐行操作设计数据库、每步留查证点」:read_verilog + hierarchy 确认设计读进来(2 个模块),proc + stat 看进程转网表,select -list/-assert 精确定位带层次路径的对象,rename 一行改名全库生效,synth 一把梭把 3 个行为单元映射成 51 个门级单元后 write_verilog/write_json 落盘——四个脚本全部退出码 0,每一步都有日志可核对。
实验环境
- 机器/系统:云主机 S6.4XLARGE32 16C32G / Ubuntu 22.04(eda-golden-v1 镜像)
- 工具版本:Yosys 0.9 (git sha1 1979e0b)(照抄日志头部真实输出)
- 日期:2026-10-25
- 完整命令:
bash run.sh(内部依次执行yosys -s s1_read.ys、yosys -s s2_stat.ys、yosys -s s3_select_rename.ys、yosys -s s4_synth_write.ys,每个脚本之后检查退出码,最后探测 openroad 并按缺失跳过)
代码获取
演示设计与四个 .ys 脚本已同步到公开仓库:geek-doc.cn/experiments/exp-tcl-scripting-yosys-openroad,克隆后进入该目录执行 bash run.sh 即可复现本文全部结果(需要本机已装 yosys)。