StarRC做90nm后端提取这件事,很多人觉得是工艺库和工具的事,脚本只是个"套壳"的活儿。但真正跑过几个项目就知道,越是老的工艺节点,越考验人——ITF文件版本杂、mapping容易错、不同corner的提取条件还得分别处理,手动一个个跑不仅累,还特别容易漏项或者用错配置。我前阵子正好把一个90nm项目的SPEF提取流程整体脚本化,把StarRC的调用、参数设置、多corner批处理、日志检查全部串成Shell自动化,这里把完整思路和踩过的坑都整理出来。
这个内容适合正在做数字后端物理实现、或者刚接手老工艺项目维护的工程师参考。不管你是准备把StarRC的RC提取纳入现有流程,还是纯粹想看看Shell脚本怎么跟EDA工具配合,这篇文章都能给你一套可以直接抄作业的方案。
1. 整体设计思路:为什么用Shell而不是直接敲命令
1.1 手工提取的痛点在哪
90nm节点虽然不算先进工艺,但只要芯片规模稍微上去,要做的事情一点不少。RC提取通常不是只跑一次,而是要覆盖不同的寄生参数corner,比如慢工艺角、快工艺角、功耗分析用的typ corner,甚至还要区分Cworst、Cbest这类主要影响时序和信号完整性的不同提取模式。
手工做的话,每次都要打开终端,敲一遍StarRC命令,填对配置文件路径,盯着log等结果。一次两次没问题,但一个模块跑五个corner,六个模块就是三十次,中间任何一个corner没跑完、或者log里出现了错误被忽略,后面timing signoff的时候就是大坑。
我之前遇到过一个情况:某个模块的worst corner SPEF生成之后,因为mapping文件里有一层金属的itf layer没对上,StarRC在log里打了warning,但退出代码还是0。跑timing的人拿到这份SPEF直接用,结果setup违例数据跟PR阶段对不上,排查了两天才发现是提取这步的数据有问题。
手工流程最大的风险不是命令敲错,而是"看似成功了,实际数据不对"。脚本化的核心价值不只是省时间,是把每一次提取的输入、输出、配置、检查项都固定下来,让流程可复现、可追溯。
1.2 Shell作为自动化胶水的理由
做RC提取的机器通常就是一台Linux工作站,StarsRC本身也是跑在Linux环境下的工具。Shell脚本天然适合做这种"外部流程控制"的活——它不需要像Perl或者Python那样额外考虑包依赖,也不用引入复杂的框架,就是老老实实地串命令、查状态、判断结果、写日志。
有人可能会问,Python不是更强大吗?确实Python在处理文本、做复杂数据校验的时候更强,但这里有个实际项目里的考虑:跑RC提取的机器上环境往往很干净,Python版本、第三方库都不一定能保证;而Shell是每台Linux机器都有的,脚本扔上去就能跑,不会因为缺库启动不了。
另外,StarRC这类EDA工具各自的启动方式、环境变量设置都写在cshrc或者setup文件里,Shell脚本可以很自然地source这些环境文件,然后调用工具。对于多corner批处理这种场景,Shell的循环、条件判断、后台任务切换也都够用了。
1.3 这套流程的预期效果
最终做出来的脚本化流程是这样一个工作方式:给定一个模块名和工艺角,脚本自动找到对应的layout数据库(一般是Milkyway或者OpenAccess数据库)、自动选择正确的itf文件和mapping文件、自动生成StarRC所需的配置文件,然后启动提取。跑完后自动扫log里的ERROR和WARNING,把关键信息汇总到一个报告里。
整个过程,人只需要输入一行命令。原本半天的人工操作,压到十几分钟纯机器时间,而且机器跑的时候人可以去做别的事。再加上统一的日志和结果规范,后续定位问题也省事很多。
2. StarRC提取原理与关键配置拆解
2.1 StarRC到底在做什么
StarRC做的事情,通俗讲就是根据版图上每一条互连线的几何形状、周边环境、材料属性,通过场解算器或者模式化的算法,算出这条线对地的电容、线间的耦合电容,以及金属线的电阻,最后按照标准寄生参数格式写出来。
90nm工艺的铜互连和130nm及以前的铝互连不太一样。铜的电阻率更低,但大马士革工艺导致金属剖面不是理想矩形,所以对电阻的提取需要依赖Foundry提供的工艺文件来做校正。FinFET之前的老工艺节点,沟道漏电没那么夸张,但互连线的RC延迟占比已经非常高,尤其是长走线。这也是为什么后端signoff阶段一定要用提取后的SPEF回标来做时序验证,而不是只靠PR工具里估算的寄生。
StarRC的输入通常需要几样东西:版图数据库(包含物理连线信息)、工艺文件(itf/tluplus)、layer mapping文件、提取规则(nxtgrd文件,一般Foundry直接给)。输出就是SPEF或者DSPF等格式的寄生参数文件。
2.2 三种关键文件的分工
在实际用StarRC的时候,最容易搞混的就是itf、tluplus和mapping文件的关系。
itf是互连工艺文件,描述每层金属的厚度、介电常数、电阻率、最小宽度间距等物理参数。早期工艺节点(包括90nm)的StarRC流程里,常用的是itf文件,启动的时候需要用映射关系把版图数据库里的layer number对应到itf里定义的层上去。
tluplus是StarRC后来主推的加密工艺文件格式,它把itf里各层之间的耦合电容计算所需的数据做了预处理,速度更快,也保护了Foundry的工艺数据。90nm时代Foundry给的往往两个都有,新工具也支持直接用tluplus。
mapping文件是连接"版图层的ID"和"工艺文件中层名"的桥梁。版图数据库里的layer number是数字或者字符串标识,比如METAL1、METAL2、VIA12这些。而itf里层的命名规则可能又是另一套。mapping文件里一行一行的对应关系,告诉StarRC"版图上这一层对应工艺参数里的哪一层"。
这套东西看着简单,实际出问题最多的就是这里。层数一多,加上顶层厚金属、pad层这些特殊层,只要漏映射或者映射错,后面提取结果就是错的。
2.3 命令文件和关键参数
StarRC的运行一般有两种方式,一种是直接写一个command file,用starrc_cmd去执行;另一种是通过命令行把一堆选项塞进去。后续要做脚本自动化,建议统一用command file方式,因为可读性好、容易维护,不同corner之间只差几个参数的时候也方便用sed动态生成。
一个简化的StarRC命令文件结构大概是下面这样:
* Delay Calculation * Unit Capacitance : 1ff * Unit Resistance : 1ohm * Unit Time : 1ns TIMING POWER COUPLE_TO_GROUND NETLIST_SPECIAL_PINS CONNECT_SUPPLY_PINS CONNECT_GROUND_PINS PRIMARY "top_module" DATABASE "/data/design/block_a.mw" MK OPERATING_TEMPERATURE 25.0 EXTRACTION RC_OUTPUT "/data/design/block_a_typ.spef" SPEF RC_OUTPUT_PINS "top_module/CLK" RC_OUTPUT_NO_HIERarchical MAPPING_FILE "/data/tech/90nm_itf.mapping" ITF_FILE "/data/tech/90nm_typ.itf" NDM_PROPERTY ...这些参数里几个值得留意:
- OPERATING_TEMPERATURE:温度对电阻影响很大,尤其是铜互连。同一套版图,25度和125度提取出来的线电阻可能差百分之二三十。所以不同corner必须对应正确的温度设置,不能一个温度打天下。
- RC_OUTPUT_PINS:指定需要特别关注的pin,把这些pin上的寄生参数单独列出,方便调试对比。
- COUPLE_TO_GROUND:这个选项要求把耦合电容都置到地。某些分析场景(比如功耗)用这种模式更快;时序分析一般要保留耦合电容,用COUPLE_TO_GROUND反而会损失精度。
还有一个容易被忽视的时间单位设置。SPEF文件里*UNIT这个数值直接决定后续PT读入后怎么解释。如果单位写错,比如电阻单位写成1ohm实际是1milliohm,读进去的RC数值会差一千倍,timing结果完全乱掉。脚本里应该把这个参数固定死,并且和PT的配置文件保持一致,出现问题的时候第一个检查这个。
3. Shell自动化脚本的完整实现
3.1 脚本结构规划
我把整个流程拆成几层:一个主控脚本、一个公共配置脚本、每个corner一个配置文件。主控脚本负责解析参数、加载公共配置、循环调用处理函数;公共配置脚本存放数据库路径、工具路径、工艺文件路径这些环境信息;corner配置则单独定义温度、itf文件、RC输出后缀。
这样做的好处是,新增一个corner只需要在那个目录下多放一个配置文件,主控脚本一行都不用改。对于一个包含多个模块、多个corner的项目,这种结构维护起来相当舒服。
目录布局大概是这样的:
rc_auto/ ├── run_starrc.sh # 主控脚本 ├── common.cfg # 公共配置 ├── conf/ │ ├── typ.cfg # typical corner配置 │ ├── cworst.cfg # Cworst corner配置 │ └── cbest.cfg # Cbest corner配置 ├── run/ # 临时运行目录 ├── log/ # 日志目录 └── spef/ # 输出的SPEF文件每个corner的配置文件内容很少,只放差异项。比如typ.cfg:
# corner name CORNER="typ" OPER_TEMP=25.0 ITF_FILE="/data/tech/90nm_typ.itf" MAPPING_FILE="/data/tech/90nm_itf.mapping" SPEF_SUFFIX="_typ.spef"这么一搞,不同corner之间的差异一目了然,也方便在脚本里做检查——比如确保typ和cworst不会因为手误用了同一个itf文件。
3.2 主控脚本的核心逻辑
主控脚本的核心流程是:读取输入参数(模块名、corner列表),检查环境变量和文件是否存在,创建运行目录,动态生成StarRC command file,调用StarRC,检查运行结果,归档日志和SPEF。
关键部分用代码来展示:
#!/bin/bash # run_starrc.sh - StarRC SPEF extraction automation # Usage: ./run_starrc.sh -m block_a -c "typ cworst cbest" set -uo pipefail source ./common.cfg while getopts "m:c:h" opt; do case $opt in m) BLOCK="$OPTARG" ;; c) CORNERS="$OPTARG" ;; h) usage ;; *) usage ;; esac done if [ -z "${BLOCK:-}" ] || [ -z "${CORNERS:-}" ]; then echo "ERROR: block name and corner list are required." exit 1 fi for corner in $CORNERS; do echo "=== Extracting $BLOCK @ $corner ===" ./extract_one.sh "$BLOCK" "$corner" if [ $? -ne 0 ]; then echo "ERROR: extraction failed for $BLOCK @ $corner" exit 1 fi done echo "All extraction done."用set -uo pipefail的时候注意,我没有开-e。这是因为StarRC在某些已知warning情况下退出码也可能非零,如果直接set -e会让脚本在中间退出,反而看不到后续的完整日志,不方便批量排错。实际情况是每个corner的提取函数里自己对退出码做判断,再决定是否终止。
3.3 动态生成StarRC命令文件
每个corner跑之前,需要把公共的command file模板和corner配置拼成最终的StarRC输入。我不用复杂的模板引擎,就用最简单的cat加变量展开。
为了保证command file内容不出错,所有配置项都先做存在性检查,路径里尽量不带空格。脚本里加一个专门的阶段,把将要生成的command file打印出来,方便人工快速确认。
# extract_one.sh 片段 generate_cmd_file() { local block=$1 local corner=$2 local cfg="conf/${corner}.cfg" local cmd_file="run/${block}_${corner}.cmd" source "$cfg" cat > "$cmd_file" <<EOF * Generated by run_starrc.sh on $(date) * Block: $block, Corner: $corner TIMING POWER COUPLE_TO_GROUND ... PRIMARY "${block}" DATABASE "${MW_DATABASE}" MW OPERATING_TEMPERATURE ${OPER_TEMP} EXTRACTION RC_OUTPUT "${SPEF_DIR}/${block}${SPEF_SUFFIX}" SPEF RC_OUTPUT_PINS "${HIER_PINS}" MAPPING_FILE "${MAPPING_FILE}" ITF_FILE "${ITF_FILE}" EOF echo "Command file generated: $cmd_file" }注意SPEF_DIR、MW_DATABASE这些变量在common.cfg里定义好,运行目录统一规范,避免跨corner互相覆盖。
3.4 并行执行的细节控制
多corner提取如果机器资源够,可以考虑并行。但StarRC跑起来很吃内存,90nm设计尤其是大型模块,每个实例可能要几个GB内存。并行之前一定查一下机器负载和license。
我做了一个简单的并行控制:用后台任务加等待的方式,最多同时跑两个corner,避免高峰时段把工作站搞到swap。实际代码不算复杂:
pids=() for corner in $CORNERS; do ./extract_one.sh "$BLOCK" "$corner" & pids+=($!) # 简单控制并发数,超过2个就等一个结束 while [ ${#pids[@]} -ge 2 ]; do wait -n pids=($(jobs -pr)) done done for pid in "${pids[@]}"; do wait "$pid" done这里wait -n需要bash 4.3以上,老机器的/bin/bash版本可能不够新。用之前先检测一下,或者干脆退化成串行执行,安全第一。
还有一个重要细节:并发跑StarRC之前,确认license里StarRC的feature数量够不够。有些license只能开一两个实例,强行并行会导致排队或者失败。脚本里最好做一个简单的license可用性探测。
4. 90nm工艺节点的特殊避坑点
4.1 一个典型报错:connected database layer does not have a valid itflayer
这个报错是StarRC用户问得非常多的一款错误,我自己的项目里也踩过。先解释下这句话的含义:database layer是版图数据库里的物理层;itflayer是itf工艺文件里定义的互连层。StarRC在读版图的时候,发现某个layer number在数据库里存在,但mapping文件没有给它对应到任何itf层,或者itf文件本身缺少这层的定义,于是直接报这个错退出。
出现这个报错,第一个要查的是mapping文件完整性。我之前一次是PDK版本更新之后,版图数据库里多了一层用于光刻校准的辅助层(不是电气层),但mapping文件还是旧版,没加这层的对应。StarRC可不认为这是辅助层,它一视同仁地要找到对应的itf定义,找不到就罢工。
排查方法不复杂,先用文本工具把mapping文件里所有layer列出来,再用工具的数据库检查命令把版图库里实际存在的layer列出来,做一个差集,差出来的就是"没有valid itflayer"的层。
常见情况是这几种:
- 顶层铝pad层、redistribution layer在mapping文件里缺失
- 用于dummy填充的金属层没映射(dummy会对耦合电容有影响,不能随便跳过)
- 某些via层在数据库里是自动生成的,但工艺文件里名字拼写有出入
处理办法:不要自己猜测某个层是否电气相关就直接在mapping里注释掉。最稳妥的是回到Foundry给的示例mapping文件,以它为基础,再用数据库的实际层列表去校准。
4.2 90nm节点特有的Layer Mapping敏感区
90nm工艺相比更老节点,一个显著变化是铜互连层数多,低k介质材料开始引入。层数一多,mapping里金属层和通孔层的配对就更重要。mapping文件里经常是一整组METAL1、V1、METAL2、V2这样的顺序排下来,任何一个via层的错位都会导致后续计算出现系统性偏差。
还有一个90nm很典型的事:dummy metal。为了满足CMP平整度要求,很多模块里会自动填大量dummy金属块。这些dummy对时序的影响说大不大说小不小,但既然用了低k材料,耦合电容对dummy的存在还是比较敏感的。StarRC提取时需不需要包含dummy、dummy怎么参与计算,一般由itf文件里的设置和提取模式共同决定。个人经验是,老工艺项目里如果headroom很紧,别图快把dummy相关选项关掉,省下的运行时间后续可能用Timing closure的加班来还。
4.3 多corner提取时的温度与RC模式匹配
做多corner批处理的时候,脚本里最需要盯紧的是corner配置和温度是否匹配。比如Cworst角本意是"电容最大、电阻也偏大"的情况,对应的itf文件可能是按特定温度提取的,如果在脚本里统一用25度去跑所有corner,提取出来的RC可能不是signoff真正要的那个。
90nm时代Foundry提供的itf文件通常是一个基础文件配不同的温度修正,有些是通过RC模式选项来区分。所以我在corner配置文件里把温度项显式写出来,并且加了一个校验函数:读一下itf文件头部的注释,看看它声明的适用温度范围,跟配置里的OPER_TEMP做比对,偏差超过说明的范围就报warning。
这种检查在人工操作时往往被跳过,脚本化之后反而成了标准动作,我觉得这是自动化流程带来的最大附加价值——不是替代人,而是逼着人把该确认的事确认掉。
4.4 低k介质对SPEF的影响
90nm是低k介质刚开始规模应用的节点,k值越低,线间电容越小,RC延迟也相应改善。但低k材料的机械强度差,在封装应力下容易出问题,这是工艺上的话题,跟提取相关的点在于:
提取时用的介电常数精度,会影响线间耦合电容的提取值。StarRC算耦合电容时依赖itf文件里每层间介质的介电常数和厚度,如果Foundry更新过工艺参数,itf文件版本没跟上,提取结果会偏。项目里最好固定一个itf版本,不要中途随意换,一旦换了就要把之前所有corner重新提取一遍,否则前后数据不可比。
5. SPEF文件生成后的质量校验技巧
5.1 SPEF文件的基本结构速览
SPEF文件本质是文本,里面的关键信息分成几个段。头部是版本、设计名字、单位定义;然后是主互连段(*D_NET开头),每个net下面列出它的电阻(*R)、对地电容(*C)、耦合电容(*CC)和pin连接关系(*I)。
一开始不熟悉SPEF格式的工程师,我建议找个简单模块的SPEF打开看一遍。你会发现D_NET下面的R、*C条目不是按物理位置排列的,而是按节点的索引号排列。每个电阻两端各有一个内部节点号,每个电容至少一端连着内部节点,另一端是地(GND)或者另一个net。
学会看这个结构,最大的好处是排查问题时能快速判断文件的合理性。比如某个重要时钟net在SPEF里的对地电容值明显异常偏大或偏小,就能在进PT之前发现问题,而不是等时序分析跑完再回去翻。
5.2 用Shell脚本做快速一致性检查
跑完StarRC之后,我不会急着把SPEF交给后端timing组。先跑一套快速检查脚本,做下面这些事:
- 确认SPEF文件存在且非空
- 检查*UNITS段里的单位定义是否和预期一致
- 统计*d_NET数量,跟设计里的net总数比对
- 检查有没有出现
*WARNING、*ERROR标记
这些检查全部用Shell加awk就能完成,不需要额外工具。比如统计net数量:
printf "Net count: " grep -c '\*D_NET' "${spef_file}"再比如看单位:
grep -A4 '^[*]UNITS' "${spef_file}"还有一个比较实用的检查:如果同一个corner之前已经提取过,新的SPEF出来之后可以做个粗粒度的对比,看看总电容值的偏差在不在合理范围。用简单的文本统计然后比较,突然出现百分之几十的偏差往往说明工艺文件或mapping出了问题。
5.3 一定要留意的WARNING
SPEF文件里的warning不一定致命,但有些必须人工介入确认。我个人关注的是这两类:
一类是RC_OUTPUT_PINS指定的pin在实际版图里没找到。这种warning出现,意味着signoff时候某个关键pin上会看不到期望的寄生值,但文件照样生成。如果pin名字在网表里叫CLK_A,而在版图数据库里因为hierarchy的原因实际全名是TOP/CLK_A,这种warning就会冒出来。
另一类是悬空节点或者节点合并导致的异常。90nm设计里电源地网络通常会做special net处理,如果在提取时没有把电源地正确识别,提取工具可能把它们当作普通信号线,产生大量不必要的寄生,SPEF文件体积飚升,timing工具读起来也变慢。
遇到这两类warning,我的原则是先修后跑,不要在带warning的情况下继续往下走。RC提取是signoff的数据源头,这里脏一点,后面所有流程都会脏,越早拦截成本越低。
5.4 与PT读入数据做交叉核对
最后一道关,是把SPEF读入PrimeTime,随便挑几个关键net看一眼RC总值,和StarRC自己报告出来的net-level总值对一下。两者如果对不上,通常问题出在单位换算或者SPEF文件格式上,不会是因为工具算错。
90nm时代后端工程师之间流传着一个经验:SPEF的可行性检查,25%靠文件内容检查,50%靠PT读入检查,剩下25%靠对比之前版本的差异。我个人觉得这个比例到今天仍然适用。脚本自动化能覆盖前25%和后25%,中间的50%就得靠跑一次PT来完成,流程里也要把这步串进去。
6. 我实际用下来觉得最值得做的事
如果看完这篇只想记住三件事,我个人建议是下面这三个。
第一,mapping文件永远是RC提取排错的第一个怀疑对象。不管是报connected database layer does not have a valid itflayer,还是提取出来某个net的电容明显不对,先回去检查mapping,比在工具选项里瞎试高效多了。
第二,自动化流程里一定要把"日志检查"做成强制步骤。我写的脚本里,StarRC跑完会立刻扫log,凡是有ERROR的corner直接标红输出,有WARNING的汇总到一个单独文件里。这些输出全部集中在运行目录下一个summary.log里,每次提取完扫一眼这个文件,心里就有底。
第三,别图省事把多个corner的输出文件名写重。脚本里SPEF输出路径由block、corner、后缀三部分组成,杜绝了互相覆盖的可能。这点在手工操作时反而更容易犯,因为人总有惯性,复制上一条命令时忘了改corner名,跑完才发现输出的是同一个文件。
老工艺节点做RC提取,看起来技术含量不如先进工艺那么高,但恰恰因为工具链成熟、资料分散,很多细节全靠口口相传。把流程脚本化,不只是解放了手,更是把团队里那些藏在个人经验里的"坑"显性化、固定化。后来接手的人不用再去问"这个mapping为什么这么写""那个corner为什么用这个温度",打开脚本和配置文件一看就全明白了。这大概就是自动化最容易被低估的价值。