4. 综合流程的自动化封装
当设计规模增大、工艺角增多、综合策略需要反复迭代时,手工在终端逐条敲 dc_shell 命令既低效又不可复现。综合流程自动化的核心目标,是把“一次综合”抽象成一条可参数化、可并行、可归档、可追溯的流水线。本章围绕四个工程要点展开:用 Makefile 与 Shell 脚本驱动综合、参数化配置、多配置并行综合、结果归档与日志管理。
一、为什么用 Makefile 而不是纯 Shell
纯 Shell 脚本能顺序执行,但缺少“目标—依赖—增量”的语义。综合流程天然存在依赖关系:RTL 文件列表变化才需要重跑 analyze,约束文件变化才需要重跑 compile,而报告生成又依赖网表。Makefile 的 target/prerequisite 模型正好表达这种依赖,配合 .PHONY 与时间戳判断,可以避免无谓的全量重跑。
典型做法是:Makefile 负责调度与依赖判断,Shell 脚本负责具体执行与错误捕获,Tcl 脚本负责在 dc_shell 内部完成读入、约束、编译与报告。三层分工清晰,便于维护。
# Makefile 片段(示例)
DESIGN ?= aes_core
CONFIG ?= wc_bc_125c
OUTDIR := build/$(DESIGN)/$(CONFIG)
.PHONY: all syn reports clean
all: syn reports
syn: $(OUTDIR)/$(DESIGN).mapped.v
$(OUTDIR)/$(DESIGN).mapped.v: rtl/filelist.f constraints/$(CONFIG).sdc scripts/syn.tcl
mkdir -p $(OUTDIR)
./run_syn.sh $(DESIGN) $(CONFIG) $(OUTDIR)
reports: $(OUTDIR)/$(DESIGN).mapped.v
./gen_reports.sh $(OUTDIR)
clean:
rm -rf build/$(DESIGN)/$(CONFIG)
这里 DESIGN、CONFIG 用 ?= 定义,允许命令行覆盖,例如 make syn DESIGN=aes_core CONFIG=wc_bc_m40c。这就是参数化的入口。
二、参数化配置:把“变化点”抽出来
参数化的关键不是把所有东西都变成变量,而是识别哪些是真正会变的维度。综合流程中常见的可变维度包括:设计名、工艺角(corner)、目标频率、库文件路径、约束文件、编译策略(如 -map_effort high)、是否插入时钟门控等。
推荐用“配置文件 + 环境变量”双层结构:环境变量提供默认值与路径,配置文件(如 config/<corner>.mk)提供该工艺角特有的库与约束。这样新增一个工艺角只需新增一个配置文件,无需改动主流程。
# config/wc_bc_125c.mk(示例)
TARGET_PERIOD := 2.5
LIB_DB := /pdk/lib/wc_bc_125c.db
SDC_FILE := constraints/wc_bc_125c.sdc
MAP_EFFORT := high
CLOCK_GATING := true
在 syn.tcl 中通过 set 读取这些变量,再调用 read_db、read_verilog、read_sdc、compile_ultra。参数化后,同一套脚本可以覆盖多个工艺角,避免复制粘贴带来的不一致。
三、多配置并行综合
多工艺角综合是签核前的常规需求。串行执行会拉长反馈周期,因此需要并行。并行有两种粒度:一是用 Makefile 的 -j 并行多个 target;二是用 Shell 的 & 或 xargs -P 控制并发数。前者更自然,因为每个 target 对应一个独立的输出目录,天然隔离。
# 并行跑三个工艺角(示例)
make -j3 syn DESIGN=aes_core CONFIG=wc_bc_125c \
DESIGN=aes_core CONFIG=wc_bc_m40c \
DESIGN=aes_core CONFIG=tc_bc_25c
注意:dc_shell 本身是单线程综合,并行靠多个进程实现。并发数应受限于 license 数量与机器内存,通常设为 2~4 个进程。每个进程必须使用独立的 OUTDIR 与独立的 dc_shell 工作目录,否则中间文件会互相覆盖。
四、结果归档与日志管理
综合完成后,需要归档三类产物:网表(.mapped.v)、约束(.mapped.sdc)、报告(面积、时序、功耗、QoR)。日志则包括 dc_shell 的 stdout、命令日志 .cmd 与错误日志。
归档策略建议按 build/<design>/<config>/<timestamp>/ 组织,保留最近 N 次结果,旧结果压缩或清理。日志中必须记录:运行时间、工具版本、库版本、关键参数、最终 WNS/TNS、面积与功耗。这样当某个工艺角结果异常时,可以快速定位是参数问题还是库问题。
run_syn.sh 中用 tee 同时输出到终端与日志文件,并在脚本退出时检查 dc_shell 返回码,非零则标记该配置失败,避免“假成功”。五、一个可复用的执行脚本骨架
下面给出 run_syn.sh 的关键逻辑,重点在参数校验、日志分流与返回码处理。
#!/bin/bash
set -euo pipefail
DESIGN=$1
CONFIG=$2
OUTDIR=$3
mkdir -p "$OUTDIR"
LOG="$OUTDIR/syn.log"
# 加载该配置的参数
source "config/${CONFIG}.mk"
echo "[INFO] design=$DESIGN config=$CONFIG period=$TARGET_PERIOD" | tee "$LOG"
dc_shell -f scripts/syn.tcl \
-output_log_file "$OUTDIR/dc_shell.log" \
-x "set DESIGN $DESIGN; set CONFIG $CONFIG; set OUTDIR $OUTDIR" \
2>&1 | tee -a "$LOG"
# 检查关键产物是否存在
if [[ ! -f "$OUTDIR/${DESIGN}.mapped.v" ]]; then
echo "[ERROR] mapped netlist not found" | tee -a "$LOG"
exit 1
fi
echo "[DONE] $DESIGN/$CONFIG" | tee -a "$LOG"
脚本中 set -euo pipefail 保证任一命令失败即退出,避免错误被掩盖。tee 让日志同时可见于终端与文件,便于交互调试与事后追溯。
六、结果判读与常见问题
综合完成后,首先看 dc_shell 日志末尾是否有 Error 或 Warning: Cannot 类信息;其次看时序报告中的 WNS/TNS 是否满足目标周期;再看面积是否在预算内。若某配置 WNS 为负,先确认约束是否与目标周期一致,再检查库是否选错(例如把 wc 库用在了 tc 配置上)。
dc_shell 工作目录或同一个 .synopsys_dc.setup 中的临时文件,可能出现文件竞争导致结果不可信。务必为每个配置指定独立的 OUTDIR 与独立的工作目录。