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 返回码,非零则标记该配置失败,避免“假成功”。
综合自动化封装:参数化 · 并行 · 归档 参数化配置 DESIGN / CONFIG TARGET_PERIOD LIB_DB / SDC_FILE MAP_EFFORT Makefile 调度 target / prerequisite 增量判断 make -jN 并行 PHONY: syn reports Shell 执行 run_syn.sh tee 日志 返回码检查 错误捕获 dc_shell 综合(每配置独立进程) read_db → read_verilog → read_sdc → compile_ultra → write 配置 A:wc_bc_125c build/aes_core/wc_bc_125c/ 独立 OUTDIR 配置 B:wc_bc_m40c build/aes_core/wc_bc_m40c/ 独立 OUTDIR 配置 C:tc_bc_25c build/aes_core/tc_bc_25c/ 独立 OUTDIR 结果归档与日志管理 网表 · SDC · 面积/时序/功耗报告 · dc_shell 日志 · 参数快照 · WNS/TNS 汇总 参数化入口 → Makefile 依赖调度 → 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 与独立的工作目录。