20.【RTL_Synthesis】Synthesis Scripts(综合脚本)
合成脚本从临时命令到专业流程前面我们已经学会了 Yosys 的单个命令能够交互式地跑通一个设计。但实际项目里你不可能每次修改一行 RTL 就手动敲几十个命令——太慢、容易出错而且别人也没法复现你的结果。这时候合成脚本就登场了。合成脚本就是把所有命令写在一个文件里通常后缀.ys让工具一次性执行。它像一份“烹饪食谱”只要把食谱交给 Yosys就能稳定地做出同样的“菜”网表。今天我们就来学习如何写出专业级的合成脚本从简单到复杂一步步构建可重复、可维护的流程。1. 为什么需要脚本—— 把“手工”变成“自动”想象你要做一道复杂的菜切菜、热锅、下料、翻炒、调味……如果每次都要重新回忆步骤很容易漏掉一勺盐或者火候不对。但如果你有一份写好的菜谱每次照着做味道就稳定了。合成脚本就是你的“菜谱”。它让你可重复同样输入永远得到同样的输出可记录半年后还能知道当时用了什么优化策略可版本控制用 Git 管理团队协作可参数化换一个工艺角改一个变量就行2. 一个最小脚本长什么样我们先看一个最简单的脚本假设只有一个 RTL 文件# minimal_synth.ys # 注释以 # 开头 # 读入设计 read_verilog design.v # 展开层次自动检测顶层 hierarchy -check -auto-top # 将 always 块转成门级结构 proc # 简单优化 opt # 加载标准单元库 read_liberty -lib cells.lib # 技术映射使用默认策略 abc -liberty cells.lib # 输出网表 write_verilog design_synth.v # 显示统计 stat运行yosys-sminimal_synth.ys这个脚本就能把design.v合成成门级网表。是不是很直观3. 脚本里的“变量”和“配置”硬编码文件名、时钟周期等会让脚本很难复用。所以我们要用变量。# 配置部分 set TOP_MODULE counter_top set RTL_DIR rtl set LIB_DIR lib set CLK_PERIOD 2.0 # 单位 ns set CORNER tt # 读入多个 RTL 文件 read_verilog ${RTL_DIR}/counter.v read_verilog ${RTL_DIR}/adder.v read_verilog ${RTL_DIR}/${TOP_MODULE}.v # 指定顶层 hierarchy -check -top ${TOP_MODULE} # 通用综合 proc fsm memory opt # 加载对应角的库 read_liberty -lib ${LIB_DIR}/stdcells_${CORNER}.lib # 技术映射目标延迟 时钟周期的 85% set TARGET_DELAY [expr {int($CLK_PERIOD * 1000 * 0.85)}] abc -liberty ${LIB_DIR}/stdcells_${CORNER}.lib -D ${TARGET_DELAY} # 输出 write_verilog -noattr output/${TOP_MODULE}_${CORNER}.v stat -liberty这样你只需要改文件开头的几个变量就能合成不同设计、不同工艺角。甚至可以用环境变量传递实现自动化。4. 约束告诉工具你想要什么没有约束合成工具就会盲目优化可能给你一个很小但很慢的电路或者很快但巨大的电路。约束就是你的要求。4.1 时序约束时钟时钟是同步设计的“心跳”。你需要告诉工具你的时钟周期是多少这样它才知道哪些路径需要优化到多快。在 Yosys 中最简单的方式是直接通过abc的-D参数指定目标延迟皮秒abc -liberty cells.lib -D 2000 # 目标周期 2ns 2000ps更复杂的约束如输入/输出延迟、多时钟可以用SDC 文件Synopsys Design Constraints。Yosys 会把 SDC 传递给 ABCabc -liberty cells.lib -constr constraints.sdcSDC 文件示例# constraints.sdc create_clock -period 5.0 [get_ports clk] set_input_delay -clock clk 0.5 [all_inputs] set_output_delay -clock clk 0.5 [all_outputs]4.2 面积约束如果你想限制总面积可以用-A参数单位是库定义的单位通常与面积值一致abc -liberty cells.lib -D 2000 -A 50000工具会在满足时序的前提下尽量不超过面积上限。4.3 时钟的“实际”周期现实中时钟周期不能全部用于逻辑路径因为还有时钟偏斜skew触发器建立时间布线延迟在综合阶段只能估算所以通常会把目标周期设得比理论值稍小。比如你要跑 500MHz周期 2ns可以给 ABC 设置 1.8ns90%的目标延迟为后端留下余量。set CLK_PERIOD 2.0 set LOGIC_BUDGET [expr {$CLK_PERIOD * 0.9}] set TARGET_DELAY [expr {int($LOGIC_BUDGET * 1000)}] abc -liberty cells.lib -D ${TARGET_DELAY}5. 多角合成让芯片在各种环境下都能工作我们之前学过工艺角SS、TT、FF。同一个设计在不同工艺角下时序差异巨大。所以通常需要为每个角单独合成一份网表并确保在 SS 下 setup 满足在 FF 下 hold 满足。5.1 为每个角写独立脚本最简单粗暴的方式分别写synth_ss.ys、synth_tt.ys、synth_ff.ys每个脚本里指定不同的库文件和时钟周期。# synth_ss.ys set CORNER ss set LIB_FILE lib/stdcells_ss_1p62v_125c.lib set CLK_PERIOD 2.2 # 慢角需要更长周期 ...但这样会有大量重复代码。更好的方式是参数化脚本。5.2 参数化多角脚本用一个主脚本通过环境变量或变量传递来选择角# synth.ys set CORNER $env(SYNTH_CORNER) # 根据角选择库和周期 if {$CORNER ss} { set LIB_FILE lib/stdcells_ss.lib set CLK_PERIOD 2.2 } elseif {$CORNER tt} { set LIB_FILE lib/stdcells_tt.lib set CLK_PERIOD 2.0 } elseif {$CORNER ff} { set LIB_FILE lib/stdcells_ff.lib set CLK_PERIOD 1.8 } else { error Unknown corner } # 然后继续合成...运行SYNTH_CORNERss yosys-ssynth.ysSYNTH_CORNERtt yosys-ssynth.ysSYNTH_CORNERff yosys-ssynth.ys5.3 一次性合成所有角更高级的做法在一个脚本里循环处理多个角避免重复读 RTL 等操作。# multi_corner.ys # 读 RTL 一次 read_verilog design.v write_ilang design_rtl.il # 保存状态 # 定义角信息 array set corners { ss {lib/stdcells_ss.lib 2.2} tt {lib/stdcells_tt.lib 2.0} ff {lib/stdcells_ff.lib 1.8} } foreach corner {ss tt ff} { # 重置设计重新加载 RTL design -reset read_ilang design_rtl.il set lib_file [lindex $corners($corner) 0] set clk_period [lindex $corners($corner) 1] hierarchy -check -top my_top proc fsm memory opt read_liberty -lib $lib_file set target_delay [expr {int($clk_period * 1000 * 0.85)}] abc -liberty $lib_file -D $target_delay write_verilog -noattr output/${corner}_netlist.v tee -o output/${corner}_stats.txt stat -liberty }这样一次运行就能得到三个角的网表和统计报告非常适合回归测试。6. 脚本的最佳实践6.1 模块化把配置和流程分开。例如config.ysset TOP_MODULE my_top set RTL_FILES {rtl/cpu.v rtl/alu.v} set CLK_PERIOD 2.0 set CORNER ttflow.yssource config.ys read_verilog $RTL_FILES hierarchy -check -top $TOP_MODULE proc fsm memory opt # ... 后续这样你可以有多个配置文件如config_ss.ys、config_tt.ys而flow.ys保持不变。6.2 目录结构一个好的项目目录结构project/ ├── rtl/ # RTL 源文件 ├── lib/ # Liberty 库文件 ├── constraints/ # SDC 约束文件 ├── scripts/ # Yosys 脚本 ├── output/ # 输出网表和报告 │ ├── ss/ │ ├── tt/ │ └── ff/ └── Makefile # 自动化构建6.3 用 Makefile 管理# Makefile CORNER ? tt synth: mkdir -p output/$(CORNER) cd scripts yosys -s synth_$(CORNER).ys synth_all: $(MAKE) synth CORNERss $(MAKE) synth CORNERtt $(MAKE) synth CORNERff clean: rm -rf output运行make synth_all就自动合成所有角。6.4 错误检查在脚本中加入检查确保每一步都正确执行。例如# 检查是否有未映射的通用单元 select -assert-none t:$ # 检查是否有未连接的端口 check -assert如果出现错误脚本会停止并报错避免输出无效网表。6.5 日志记录用tee命令把输出同时保存到文件tee -o synthesis.log stat tee -a synthesis.log echo Synthesis complete这样所有信息都留存在日志里方便回溯。7. 综合脚本的“进阶技巧”7.1 条件合成调试 vs 发布你可以通过变量控制是否进行某些优化比如调试时保留层次结构set DEBUG 1 if {$DEBUG} { # 调试模式不做层次打平便于查看 hierarchy -check -top ${TOP_MODULE} # 跳过 flatten } else { # 发布模式打平层次激进优化 hierarchy -check -top ${TOP_MODULE} flatten }7.2 多轮优化有时一次opt不够可以跑多次opt opt opt或者在不同阶段分别优化# 第一次通用优化 opt # 映射后再次优化 abc -liberty cells.lib opt clean7.3 与后端的交互合成完成后可以生成 JSON 格式的报告供其他工具使用write_json output/design.json也可以生成可视化图show -prefix output/design -format svg这些对调试和文档非常有用。8. 总结从“临时”到“生产”合成脚本是把你的知识固化下来的工具。它让综合变得可重复任何时候运行结果一致可扩展通过变量轻松应对不同设计、不同工艺角可协作团队共享脚本减少沟通成本可自动化集成到 CI 流程每次提交都自动回归从今天起不要再用手工敲命令了。把你的合成命令写成.ys脚本用yosys -s执行把脚本放进版本控制让团队都受益。下一步我们将学习如何分析合成后的网表解读时序报告确保设计真的满足约束。记住脚本只是工具真正重要的是你理解每一步在做什么并做出正确的权衡。