Yosys ABC 工艺映射流程:abc -liberty 如何把网表落到标准单元
更新时间:2026-11-03。综合是什么 讲过 RTL 到门级网表的全过程,但
synth跑完得到的还是 Yosys 自家的$_NAND_、$_DFFE_PN0P_一类内部原语,没有任何晶圆厂认识它们。让网表第一次和具体工艺挂钩的,就是本篇要讲的 Yosys ABC 工艺映射流程:先用dfflibmap落实触发器,再用abc -liberty把组合逻辑落到标准单元。本篇只讲这一步本来该怎么操作。
本文要回答的问题
- Yosys ABC 工艺映射流程在整条综合链路里处于哪一步,吃什么、吐什么?
dfflibmap和abc -liberty为什么是两条命令,-script、-D、-constr、-dff各在什么场景用?- 映射前后的
stat和网表具体差在哪里,Chip area数字怎么核对? - 找不到库、锁存器残留、映射后仍有
$单元这三类高频问题怎么检查?
先给一张映射前后的对照卡,本篇的全部操作都是为了把左边变成右边:
读这张卡抓住一点:映射不改变电路功能,改变的只是「用哪一套积木名字描述电路」。映射是否成功,最直接的判据就是 stat 里还有没有 $ 开头的单元。
一、这一步在流程里的位置
Yosys 的 synth 是一条宏流程:read_verilog 读入 RTL,proc 把 always 展平成 $dff/$mux 一类 RTL 单元,opt 做化简,techmap 把算子降成自家门级原语,结尾还会跑一次不带工艺库的 abc 做通用门映射。到这里电路已是门级网表,但和任何具体工艺无关。工艺映射接在这之后,分两小步:dfflibmap 负责时序单元(触发器、带置复位的寄存器),abc -liberty 负责剩下的组合逻辑。
读这张图注意颜色:蓝色是 Yosys 内部世界,橙色两步是本篇的工艺映射,绿色才是工艺世界。为什么触发器不让 ABC 一起映射?ABC 的核心数据结构是 AIG(与非图),只描述组合逻辑;寄存器由 Yosys 自己的 dfflibmap 按 liberty 里 ff(...) 段的时钟沿、复位极性做模式匹配。只有显式加 -dff 时,ABC 才会把触发器也带入重定时,常规流程不要开。
二、命令逐段解读
最小映射脚本只有四行,每一行都不能随意省:
read_verilog counter.v
synth -top counter
dfflibmap -liberty tiny.lib
abc -liberty tiny.lib
stat -liberty tiny.lib| 片段 | 作用 | 不写会怎样 |
|---|---|---|
dfflibmap -liberty tiny.lib | 读 liberty 中时序单元的 ff 描述,把 $_DFFE_PN0P_ 匹配成 DFFRN 等库单元 | 触发器留在内部原语,后续 ABC 只处理组合逻辑,网表里寄存器无人认识 |
abc -liberty tiny.lib | 把组合门级网表交给 ABC,按库单元的逻辑功能、面积与延时表做映射 | 永远停在 $_NAND_ 等内部原语,没有 Chip area |
stat -liberty tiny.lib | 按库的 area 属性汇总面积 | 只能数单元个数,看不到面积口径 |
abc 常用选项按场景选:-liberty <file> 指定单元库,是标准单元流程的主入口;-script <file> 自定义传给 ABC 的命令序列,文件名以 + 开头时可把命令直接内联(逗号会被替换成空格),不传则用内置默认脚本;-D <ps> 给延时目标,默认脚本中的 {D} 占位符会替换为该值并插入重定时;-constr <file> 给输入驱动单元与输出负载(文件只有 set_driving_cell 与 set_load 两行),开启后 ABC 会追加 buffer; upsize; dnsize; stime 做门尺寸调整;-dff 把触发器也送进 ABC(多时钟域会自动按域分区);-fast 用更短的脚本换运行时间、牺牲质量。本次真跑的默认脚本可以在日志里看到一串真实调用:strash; &get -n; &fraig -x; &put; scorr; dc2; dretime; ... &dch -f; &nf; &put,分别对应结构化、冗余消除、冗余剪除、延时重算与最终工艺覆盖映射。
三、输入输出文件
映射这一步吃三样东西。一是映射前门级网表,在内存中由 synth 产出,单元名形如 $_DFFE_PN0P_;二是 liberty 库,描述每个单元的引脚方向、逻辑功能、面积和延时,本次用的 tiny.lib 一段长这样:
cell(NAND2) {
area : 1;
pin(A) { direction : input; capacitance : 0.002; }
pin(B) { direction : input; capacitance : 0.002; }
pin(Y) { direction : output; function : "(!(A*B))";
timing() { related_pin : "A";
cell_rise(delay_3x3) { values("0.035,0.055,0.110",
"0.040,0.060,0.115", "0.055,0.075,0.130"); } }
}
}库里一共 10 个单元:BUF/INV/NAND2/NOR2/AND2/OR2/XOR2/MUX2 加 DFF/DFFRN 两种触发器。三是可选的约束文件,本例 constr.txt 内容为 set_driving_cell INV 与 set_load 0.05。
产出也有三样:映射后的工艺网表(write_verilog 导出,例化语句已是 DFFRN _74_ (...)、NOR2 _71_ (...) 这样的库单元)、ABC 的逐类计数(ABC RESULTS: NOR2 cells: 10)、以及带面积的 stat 报告。映射前报告是 28 个内部原语、无面积行:
同一条 stat -liberty 在映射后变成 30 个库单元并给出面积:
74 这个数可以手算核对:8 个 DFFRN(面积 5)加 4 个 AND2(面积 2)、10 个 NOR2(面积 1)、4 个 XOR2(面积 3)、2 个 NAND2 与 2 个 INV(各面积 1),即 40+8+10+12+2+2=74。注意它只是库内相对面积权重,不是任何工艺节点的平方微米;stat 面积口径的细节见 stat 面积为 0 的排查。单元数从 28 变 30 也正常——工艺映射允许增删缓冲与门数,比的是面积和延时而非门的个数。这是教学用最小库,产线流程必须替换为 PDK 提供的完整标准单元库。
四、常见错与检查点
| 现象 | 先查什么 | 纠正 |
|---|---|---|
dfflibmap 报 Can't open liberty file | 库路径与当前工作目录、容器挂载点 | 用容器内可见的绝对路径,先 ls 确认 |
abc 报看似无关的 Can't open ABC output file output.blif | 上一行 liberty 是否没被 ABC 成功读入 | 这个报错的真实原因仍是库文件没找到或解析失败,专项排查见 ABC 找不到单元库 与 liberty 读取错误 |
映射后 stat 里仍有 $_DLATCH_、$lut 等 $ 单元 | 库里是否缺对应功能单元,或自定义 -script 是否用了不完整的 ABC 命令序列 | 补库单元或改回默认脚本;$ 残留数应为 0 |
加了 -D/-constr 结果毫无变化 | 库里是否只有单一驱动强度 | upsize/dnsize 需要同名 X1/X2/X4 多强度单元才能换管,教学库没有,所以结果不变 |
锁存器是最值得专门看的一类残留。把一个缺 else 的组合 always(latch.v)走同一流程,由于库里没有任何 latch 单元,ABC 直接跳过组合提取,stat 里留下一个 $_DLATCH_P_:
Extracted 0 gates ... Don't call ABC as there is nothing to map 不是 ABC 故障,而是网表里根本没有组合逻辑可映射;真正的问题在更上游——RTL 写出了锁存器。多数数字流程里应当回到设计补全分支,而不是给库硬塞 latch 单元,机制见 推断出锁存器怎么办 和 触发器与锁存器。检查动作收尾时固定跑一次 check,正常应输出 Found and reported 0 problems,再人工扫一遍 stat 确认没有 $ 开头的行。缺库时两条命令的真实报错对比如下:
五、真实跑一遍:计数器映射对照
实验在 geek-eda:0.3-sta 容器中进行(Yosys 0.33),一条 make run 串起五段:映射前 stat、默认脚本映射、-D 2000 -constr 延时驱动映射、锁存器反例、两种缺库报错。延时驱动段日志里确实执行了 upsize -D 2000; dnsize -D 2000; stime -p,但单元构成与面积仍是 30 个单元、74——因为教学库里每种逻辑功能只有一个单元,ABC 没有更强驱动的管子可换。这恰好验证了一个常识:延时优化的上限由库决定,工具只能在库提供的候选里选。映射后导出的网表头部长这样,端口仍是原来的 clk/rst_n/en/q/carry,内部已全部是库单元例化:
/* Generated by Yosys 0.33 (git sha1 2584903a060) */
module counter(clk, rst_n, en, q, carry);
wire _00_;
...
DFFRN _74_ (.CK(clk), .D(_44_), .Q(q[0]), .RN(rst_n));这份网表配合 liberty 与 SDC 约束,就可以进入静态时序分析或后端流程;在更宏观的图景里,这一步的位置见 RTL 到门级。
实验环境
- 机器/系统:macOS(Darwin 25.5,Apple Silicon),实验在 geek-eda:0.3-sta Linux 容器中运行。
- 工具版本:Yosys 0.33 (git sha1 2584903a060)。
- 日期:2026-11-03。
- 完整命令:在实验目录执行
make run(即docker run --rm -v "$PWD":/work -w /work geek-eda:0.3-sta bash run.sh),脚本依次输出教学库单元清单、映射前 stat、dfflibmap + abc -liberty映射全段与映射后 stat、映射网表例化摘录、-D/-constr延时驱动结果、锁存器反例残留以及两种缺库报错。
代码获取
计数器与锁存器 RTL、教学用 tiny.lib、约束文件、Yosys 脚本与一键复现脚本已同步到公开仓库:geek-doc.cn/experiments/flow-yosys-abc-mapping,克隆后进入该目录执行 make run 即可复现本文引用的全部映射输出。
这一篇在知识体系里的位置
- 想先理解综合全流程与「为什么要做工艺映射」:读 综合是什么 与 RTL 到门级;
- 想学会读映射前后那份 stat 报告的每个字段:读 Yosys stat 报告怎么读(同批姊妹篇);
- 映射中的报错专项:ABC 找不到单元库、liberty 读取错误、推断出锁存器怎么办,更多入口见 EDA 工具实践目录。
总结
Yosys ABC 工艺映射流程接在 synth 产出的通用门级网表之后:dfflibmap -liberty 按 ff 描述把触发器匹配成 DFFRN 一类时序库单元,abc -liberty 再把组合逻辑按功能、面积、延时表覆盖映射成 NAND2/NOR2/XOR2 等工艺单元;-script 换 ABC 脚本、-D 加延时目标、-constr 加驱动与负载、-dff 让寄存器进 ABC、-fast 换轻量脚本。真跑结果显示,计数器从 28 个 $_ 内部原语变为 30 个带库名的单元、Chip area 74 可按面积权重手算核对;映射成功的硬判据是 stat 中 $ 单元清零、check 报 0 问题,锁存器残留与缺库报错都要回到「库是否提供对应单元、路径是否可见」这两个源头处理。