一键导入
eda-scripting
EDA工具脚本开发技能 - TCL/Python/Makefile 自动化流程最佳实践
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
EDA工具脚本开发技能 - TCL/Python/Makefile 自动化流程最佳实践
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
物理设计全流程技能 - 从floorplan到GDSII,完整数字芯片物理设计指导
芯片架构设计技能 - 从需求定义到模块划分,完整指导芯片架构开发
功能安全分析技能 - FMEDA 分析、安全机制设计、ISO 26262 符合
静态时序分析技能 - 时序诊断、收敛优化、签收标准
UVM 验证方法论技能 - 从验证计划到功能覆盖率,完整 UVM 验证平台开发
功耗分析和优化技能 - 功耗计算、IR降分析、电迁移分析、低功耗优化
基于 SOC 职业分类
| name | eda-scripting |
| description | EDA工具脚本开发技能 - TCL/Python/Makefile 自动化流程最佳实践 |
| description.zh | EDA工具脚本开发技能 - TCL/Python/Makefile 自动化流程最佳实践 |
| origin | ICER |
| categories | tools |
当用户需要开发 EDA 工具自动化流程脚本时,启用此技能。涵盖 TCL 流程脚本、Python 结果分析、Makefile 流程管理。
| 脚本类型 | 代码行数 | 开发时间 | 测试时间 |
|---|---|---|---|
| 综合脚本 | 200-500 行 | 1-2 天 | 1 天 |
| 布局布线脚本 | 500-1000 行 | 2-3 天 | 2 天 |
| 仿真脚本 | 100-200 行 | 0.5 天 | 0.5 天 |
| 结果分析脚本 | 100-300 行 | 1 天 | 0.5 天 |
| Makefile | 50-200 行 | 0.5 天 | 0.5 天 |
| 指标 | 标准 | 说明 |
|---|---|---|
| 错误检查覆盖率 | 100% | 所有步骤都有错误检查 |
| 日志完整性 | 100% | 所有关键步骤都有日志 |
| 注释覆盖率 | > 20% | 关键代码有注释 |
| 参数化程度 | > 80% | 配置通过参数传入 |
诊断步骤:
1. 检查日志文件
2. 确认失败步骤
3. 分析错误信息
4. 定位根本原因
常见错误分类:
| 错误类型 | 诊断特征 | 解决方案 |
|---|---|---|
| License 失败 | "License checkout failed" | 检查 license 配置 |
| 文件不存在 | "Cannot find file" | 检查文件路径 |
| 内存不足 | "Out of memory" | 增加内存或减少并行度 |
| 语法错误 | "Syntax error" | 检查脚本语法 |
| 超时 | "Timeout" | 增加超时时间 |
诊断步骤:
1. 对比预期结果
2. 检查输入文件
3. 检查约束文件
4. 检查脚本逻辑
根本原因定位:
| 根本原因 | 诊断特征 | 解决方案 |
|---|---|---|
| 约束错误 | 时序/面积异常 | 检查 SDC 文件 |
| 输入错误 | 结果完全错误 | 检查输入文件 |
| 参数错误 | 结果部分错误 | 检查参数传递 |
| 版本不兼容 | 新版本结果不同 | 检查工具版本 |
做什么: 确定脚本要实现的功能。
怎么做:
输出: 脚本需求表
| 需求项 | 内容 |
|---|---|
| 工具 | Design Compiler |
| 流程阶段 | 综合 |
| 输入 | RTL 文件、SDC 约束、工艺库 |
| 输出 | 网表、SDC、报告 |
| 约束 | 时序、面积、功耗 |
| 运行时间 | < 4 小时 |
做什么: 设计脚本的整体结构。
脚本结构模板:
脚本结构:
├── 配置区(变量定义)
├── 初始化区(创建目录、检查环境)
├── 主流程区(读取设计、综合、优化)
├── 报告区(生成报告)
├── 输出区(保存结果)
└── 清理区(可选)
做什么: 确定哪些参数需要可配置。
| 参数类型 | 参数化示例 |
|---|---|
| 设计相关 | TOP 模块名、文件列表 |
| 工艺相关 | 工艺库路径、操作条件 |
| 约束相关 | 时钟频率、面积目标 |
| 输出相关 | 输出目录、报告目录 |
做什么: 定义所有配置变量。
# #################################################
# 设计配置
# #################################################
# ---------- 基本配置 ----------
set DESIGN "async_fifo" # 设计名称
set TOP "async_fifo" # 顶层模块
set OUTPUT_DIR "./output" # 输出目录
set REPORTS_DIR "./reports" # 报告目录
set LOG_DIR "./logs" # 日志目录
# ---------- 工艺库配置 ----------
# 从环境变量获取,不要硬编码
set STD_CELL_LIBRARY $env(STD_CELL_LIBRARY)
set TARGET_LIBRARY $env(TARGET_LIBRARY)
set LINK_LIBRARY $env(LINK_LIBRARY)
# ---------- 约束配置 ----------
set CLOCK_NAME "clk"
set CLOCK_PERIOD 10.0 # ns
set INPUT_DELAY 2.0 # ns
set OUTPUT_DELAY 2.0 # ns
# ---------- 优化目标 ----------
set OPTIMIZATION_GOAL "timing" # timing, area, power
set MAX_AREA 0 # 0 表示无限制
做什么: 创建目录、检查环境、设置工具选项。
# #################################################
# 初始化
# #################################################
# ---------- 创建输出目录 ----------
file mkdir $OUTPUT_DIR
file mkdir $REPORTS_DIR
file mkdir $LOG_DIR
# ---------- 检查环境变量 ----------
if {![info exists env(STD_CELL_LIBRARY)]} {
error "ERROR: STD_CELL_LIBRARY not set"
exit 1
}
# ---------- 设置库文件 ----------
set_app_var target_library $TARGET_LIBRARY
set_app_var link_library $LINK_LIBRARY
set_app_var search_path [concat $search_path $STD_CELL_LIBRARY]
puts "✓ 初始化完成"
做什么: 实现主要功能逻辑。
# #################################################
# 主流程
# #################################################
# ---------- Step 1: 读取设计 ----------
puts "========================================"
puts "Step 1: Reading RTL design..."
puts "========================================"
read_verilog {
../src/async_fifo.sv
../src/sync_two_stage.sv
}
hierarchy -top $TOP
# ---------- 检查错误 ----------
if {![current_design]} {
error "ERROR: Failed to read design"
exit 1
}
puts "✓ 设计读取完成"
# ---------- Step 2: 读取约束 ----------
puts "========================================"
puts "Step 2: Reading constraints..."
puts "========================================"
read_sdc ../constraints/${TOP}.sdc
puts "✓ 约束读取完成"
# ---------- Step 3: 综合 ----------
puts "========================================"
puts "Step 3: Synthesis..."
puts "========================================"
compile_ultra
puts "✓ 综合完成"
# ---------- Step 4: 生成报告 ----------
puts "========================================"
puts "Step 4: Generating reports..."
puts "========================================"
report_area > $REPORTS_DIR/${TOP}.area.rpt
report_qor > $REPORTS_DIR/${TOP}.qor.rpt
puts "✓ 报告生成完成"
# ---------- Step 5: 输出结果 ----------
puts "========================================"
puts "Step 5: Writing output..."
puts "========================================"
write_verilog -hierarchy -output $OUTPUT_DIR/${TOP}.netlist.v
write_sdc -output $OUTPUT_DIR/${TOP}.sdc
puts "✓ 结果输出完成"
puts ""
puts "========================================"
puts "✅ 全部完成!"
puts "========================================"
✅ 推荐:
snake_case 小写加下划线puts "Step ..."# ---------- 分隔,清晰可读❌ 避免:
/projects/chip/libs/... → 换机器就不能用Python 用于:结果数据分析、流程控制、批量处理、报告生成。
#!/usr/bin/env python3
"""
时序报告分析脚本
功能:从 STA 报告提取 WNS/TNS,生成汇总报告
"""
import argparse
import logging
from pathlib import Path
from typing import Dict, Optional
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def parse_arguments():
"""解析命令行参数"""
parser = argparse.ArgumentParser(
description='Extract timing stats from STA reports'
)
parser.add_argument(
'--input', '-i',
required=True,
type=Path,
help='Input report directory'
)
parser.add_argument(
'--output', '-o',
required=True,
type=Path,
help='Output summary file'
)
return parser.parse_args()
def extract_timing(report_file: Path) -> Dict:
"""从时序报告提取关键数据"""
data = {'wns': None, 'tns': None, 'np': None}
with open(report_file, 'r') as f:
for line in f:
if 'WNS' in line:
data['wns'] = float(line.split()[-1])
if 'TNS' in line:
data['tns'] = float(line.split()[-1])
if 'NP' in line or 'Number of paths' in line:
try:
data['np'] = int(line.split()[-1])
except ValueError:
pass
return data
def main():
args = parse_arguments()
# 验证输入
if not args.input.exists():
logger.error(f"Input directory not found: {args.input}")
return 1
results = {}
for report_file in args.input.glob('*.rpt'):
logger.info(f"Processing {report_file.name}")
results[report_file.stem] = extract_timing(report_file)
# 输出汇总
with open(args.output, 'w') as f:
f.write("Design,WNS,TNS,NP\n")
for design, data in results.items():
wns = data.get('wns', 'N/A')
tns = data.get('tns', 'N/A')
np_val = data.get('np', 'N/A')
f.write(f"{design},{wns},{tns},{np_val}\n")
logger.info(f"Summary written to {args.output}")
return 0
if __name__ == '__main__':
exit(main())
✅ 推荐:
argparse 处理命令行参数logging 输出日志,不要到处 printpathlib 处理路径,比字符串好❌ 避免:
使用 Makefile 管理整个流程,支持增量编译。
# #################################################
# IC 设计流程 Makefile
# #################################################
# ---------- Configuration ----------
DESIGN = my_design
TOP = top_module
TOOL = dc_shell
# ---------- Directories ----------
SCRIPTS_DIR = scripts
OUTPUT_DIR = output
REPORTS_DIR = reports
LOGS_DIR = logs
# ---------- Default target ----------
all: synth place route signoff
# ---------- Synthesis ----------
synth: $(SCRIPTS_DIR)/synth.tcl
@echo "========================================"
@echo "Step 1: Synthesis"
@echo "========================================"
mkdir -p $(OUTPUT_DIR) $(REPORTS_DIR) $(LOGS_DIR)
$(TOOL) -f $< | tee $(LOGS_DIR)/synth.log
@echo "✓ Synthesis completed"
# ---------- Placement ----------
place: synth $(SCRIPTS_DIR)/place.tcl
@echo "========================================"
@echo "Step 2: Placement"
@echo "========================================"
icc2_shell -f $(SCRIPTS_DIR)/place.tcl | tee $(LOGS_DIR)/place.log
@echo "✓ Placement completed"
# ---------- Routing ----------
route: place $(SCRIPTS_DIR)/route.tcl
@echo "========================================"
@echo "Step 3: Routing"
@echo "========================================"
icc2_shell -f $(SCRIPTS_DIR)/route.tcl | tee $(LOGS_DIR)/route.log
@echo "✓ Routing completed"
# ---------- Signoff ----------
signoff: route $(SCRIPTS_DIR)/signoff.tcl
@echo "========================================"
@echo "Step 4: Signoff"
@echo "========================================"
pt_shell -f $(SCRIPTS_DIR)/signoff.tcl | tee $(LOGS_DIR)/signoff.log
@echo "✓ Signoff completed"
# ---------- Clean ----------
clean:
rm -rf $(OUTPUT_DIR)/* $(REPORTS_DIR)/* $(LOGS_DIR)/*
.PHONY: all synth place route signoff clean
| 优点 | 说明 |
|---|---|
| 增量编译 | 只重新运行修改过的步骤 |
| 依赖管理 | 自动处理步骤依赖关系 |
| 并行执行 | 可以并行运行独立任务 |
| 易于使用 | 大家都熟悉 make 命令 |
使用环境变量指定外部依赖路径:
# ~/.bashrc 或者环境配置文件
export PDK_ROOT=/opt/pdk/sky130A
export STD_CELL_LIBRARY=$(PDK_ROOT)/libs.ref/sky130_fd_sc_hd/default
export TARGET_LIBRARY=$(PDK_ROOT)/libs.ref/sky130_fd_sc_hd/fast
export LINK_LIBRARY=$(PDK_ROOT)/libs.ref/sky130_fd_sc_hd/slow
使用 modules 工具管理不同工具版本:
module load synopsys/dc/2021.03
module load synopsys/icc2/2021.03
module load synopsys/pt/2021.03
# 检查命令执行结果
if {[catch {read_verilog design.v} result]} {
puts "ERROR: Failed to read design: $result"
exit 1
}
# 检查设计是否有效
if {![current_design]} {
puts "ERROR: No current design"
exit 1
}
| 错误类型 | 检测方法 | 处理方式 |
|---|---|---|
| License 失败 | "License checkout failed" | 立即退出,提示用户 |
| 文件不存在 | file exists 检查 | 立即退出,提示用户 |
| 内存不足 | "Out of memory" | 立即退出,建议减少并行度 |
| 超时 | 超时检查 | 立即退出,建议增加超时时间 |
每次运行收集关键指标:
| 指标 | 来源 | 收集方法 |
|---|---|---|
| 总面积 | report_area | 正则提取 |
| WNS | report_timing | 正则提取 |
| TNS | report_timing | 正则提取 |
| 功耗 | report_power | 正则提取 |
scripts/
├── synth.tcl # 综合脚本
├── place.tcl # 布局脚本
├── route.tcl # 布线脚本
├── cts.tcl # 时钟树综合
├── signoff.tcl # 签出脚本
└── analyze_timing.py # 时序结果分析
reports/ # 汇总报告
output/ # 输出文件(netlist, sdc 等)
logs/ # 运行日志
脚本写完检查:
❌ 硬编码绝对路径 → 换一台机器就不能运行
❌ 不检查错误 → 失败了还继续,最后找不到哪里错
❌ 没有日志 → 出问题不知道怎么错的
❌ 所有步骤从头跑 → 修改一点等几小时,浪费时间
❌ 二进制结果提交 git → git 很快变大
eda-scripting-developer 代理进行 EDA 脚本开发