
做芯片后端的人一定绕不开寄生参数提取这道工序90nm芯片的RC参数提取更是后端流程里最不能偷懒的一环。StarRC作为Synopsys的主力寄生提取工具负责把版图中的互连电阻电容提取出来输出成SPEF文件交给PrimeTime做时序分析。但问题在于一个芯片往往有几十个模块、好几个工艺角手工一个个敲StarRC命令不仅效率低还容易漏提、错提出片了才后悔就来不及了。所以我做了这套用Shell脚本自动化批量提取SPEF的方案实际用在90nm项目上把原来要跑两三天的提参工作压缩到半天还顺手把一批经典报错给趟平了。这篇东西不绕弯子直接讲思路、贴脚本、列报错适合正要接触后端提参流程的同学也适合被StarRC折腾到头秃的同行参考。1. 项目背景与方案设计思路1.1 90nm节点为什么必须认真提参180nm往上走的时候很多人还能靠前仿真和粗略的线负载模型蒙混过关但到了90nm节点互连线的寄生效应已经完全压不住。线宽变细之后金属线的电阻显著增加线间距离缩小让耦合电容急剧上升这两者叠加起来直接改变了信号从发射端传到接收端的延迟也让串扰噪声成为时序收敛的拦路虎。具体到90nm工艺我举几个实际影响时钟树上的走线电阻如果估算不准时钟偏斜很容易超出budget数据路径上的耦合电容如果不提取hold time检查会冒出一堆违反电源网络上如果少了动态RC分析需要的SPEFIR drop和电迁移分析结果也站不住脚。所以后端signoff阶段寄生提取已经不是“可选项”而是必须把实际版图中的每一根wire、每一个via的RC值都提出来。StarRC在这一环节的角色是把来自Milkyway数据库或者GDSII的版图结合工艺厂提供的TLUPlus文件计算出一张包含电阻、电容、电感的寄生网络表最终写成SPEF格式。SPEF是行业标准格式PrimeTime、RedHawk、Voltus这些工具都能直接读它就像芯片后端各工具之间的通用语言。1.2 为什么要用Shell脚本做自动化最开始在这个90nm项目上提参是手工做的一次提一个模块、一个corner流程大概是准备好Milkyway库和tluplus文件手写一个StarRC命令文件运行检查log再手工整理SPEF。听着不难但一旦模块数量超过十个、corner有三个以上这套流程就变成了体力活而且手工操作最大的问题是不可控。有一次我因为赶进度在某个corner上漏改了tluplus路径跑出来的SPEF是错的数据后面PR和STA全被带偏排查了两天才定位到是提参环节的问题教训极其深刻。那次之后我认真想了一件事提参流程的输入输出非常固定无非是设计名字、corner名字、工艺文件路径、输出目录这几种变量完全适合用Shell脚本把整个流程包起来。选择Shell而不是Perl或者Python原因也很实在后端服务器上Shell是必有的不依赖额外解释器StarRC是命令行工具Shell对这类外部命令的调用、返回码判断、log重定向有着天然优势整个流程不需要复杂的数据结构用变量、数组、for循环足以覆盖绝大部分场景。写起来快跑起来直接排错也直观。1.3 输入输出与目录结构规划自动化方案启动之前目录结构要先定清楚否则脚本跑起来之后文件散落各处找起来会想哭。我在这套方案里用的是下面这段布局$PROJECT_ROOT/ ├── setup/ │ ├── 90nm_typ.tluplus │ ├── 90nm_slow.tluplus │ ├── 90nm_fast.tluplus │ ├── star_mapping.mapping │ └── layermap.txt ├── input/ │ ├── block1/ │ │ └── mw_library/ │ ├── block2/ │ │ └── mw_library/ │ └── block3/ │ └── mw_library/ ├── script/ │ └── run_rc.sh ├── output/ │ ├── spef/ │ ├── log/ │ └── summary/ └── run_rc.logsetup目录放工艺相关文件input目录按模块放各自的Milkyway数据库script目录放脚本output目录按SPEF、log、summary分开存。这样设计的原因很简单工艺文件是只读的放一起方便维护版本设计数据按模块隔离避免不同模块的tluplus或mapping串了output分三个子目录是为了后期检查时能快速从summary总表到具体log再到SPEF逐层定位问题。2. StarRC提取流程的核心要素梳理2.1 StarRC命令文件的基本结构StarRC本身需要一个命令文件来告诉它本次要提哪个设计、用什么工艺参数、输出什么格式。不同版本命令语法略有差异但核心内容不外乎这几类/* 设计名与输入数据库 */ basename block1 milkyway_library /path/to/input/block1/mw_library netlist_file /path/to/input/block1/block1.v /* 工艺文件与映射 */ source -e /path/to/setup/90nm_typ.tluplus set_mapping_file /path/to/setup/star_mapping.mapping set_tlu_plus_files -layermap /path/to/setup/layermap.txt \ -tluplus_typ /path/to/setup/90nm_typ.tluplus /* 提取选项 */ extract -design block1basename就是本次提取的设计名它会出现在输出的SPEF文件的DESIGN字段里milkyway_library指定版图数据库netlist_file是门级网表用于把版图几何信息映射到逻辑单元上。set_mapping_file和set_tlu_plus_files这两行是StarRC把物理layer与工艺层关联起来的关键后面我会专门展开。extract这一行是真正触发提取的命令后面还可以加一些选项比如提耦合电容时用-coupling_cap yes提电阻时用-reduce_res输出噪声参数用-out_noise等。具体选项按项目需求开但默认把R、C提准是第一优先级的。2.2 tluplus和mapping file为什么是重灾区说句实话StarRC运行时的报错至少一半都出在tluplus和mapping file的配合上。tluplus是工艺厂提供的经过压缩和加密的工艺互连参数文件里面定义了每层金属的方块电阻、单位面积电容、边缘电容等物理值mapping file则负责告诉StarRC版图里的layer1对应tluplus里的M1layer2对应M2等等。这两个文件任何一个跟设计不匹配StarRC就会在初始化阶段倒下一片或者提出一堆错误数据。比如把用于10M工艺的tluplus用在了8M工艺的设计上或者mapping里把M4映射到了M5工具不会直接报错但它输出的SPEF里的数值就是错的。这种错误比直接报错更可怕因为很少有人会去复核SPEF里的每一条R、C是否合理。我的经验是在每个corner批量跑之前先挑一个小模块做一次单点验证提取完成后打开SPEF用命令统计一下电阻、电容的量级跟同工艺节点的历史数据比对量级不对就赶紧查tluplus和mapping别等整批跑完再复盘。2.3 SPEF文件关键字段解读自动化脚本跑完我们最终拿到的就是一堆SPEF文件。看懂SPEF的结构对排查脚本问题非常有帮助。下面是一段典型的SPEF头部*SPEF IEEE 1481-1998 *DESIGN block1 *DATE Tue Apr 8 15:30:00 2025 *VENDOR Synopsys StarRC *PROGRAM StarRC *VERSION 1.0 *DIVIDER / *DELIMITER : *BUS_DELIMITER [ ] *NAME_SCOPE LOCAL *UNITS R K M L H C T P这几个字段值得注意*DIVIDER是层次路径分隔符比如top/sub_inst/ff_inst/Q*DELIMITER是实例名与信号名之间的分隔符*BUS_DELIMITER是总线的方括号*UNITS这行定义了电阻单位、电容单位、时间单位等R代表欧姆、K是千欧、M是兆欧C后面跟的是电容单位FA一般表示皮法。这些东西在自动化流程里有一个容易踩坑的地方如果StarRC输出的SPEF里DIVIDER是/而后续PrimeTime脚本里预期的是.或者BUS_DELIMITER是[]但STA环境用的是那么SPEF读进去之后大量pin会映射不上报一堆“Cannot find pin”的错。最稳妥的办法是在StarRC命令文件里显式指定这些与网表命名一致的分隔符而不是依赖工具默认值。3. Shell脚本自动化核心实现3.1 主控脚本框架设计整个自动化方案的中枢是一个主控Shell脚本它负责遍历模块列表和corner列表为每一个组合生成StarRC命令文件、调用StarRC、记录日志、汇总结果。我的脚本框架大致如下#!/bin/bash # run_rc.sh - StarRC SPEF自动化提取脚本 set -euo pipefail PROJECT_ROOT/prod/90nm_rc SETUP_DIR$PROJECT_ROOT/setup INPUT_DIR$PROJECT_ROOT/input OUTPUT_DIR$PROJECT_ROOT/output SPEF_DIR$OUTPUT_DIR/spef LOG_DIR$OUTPUT_DIR/log SUMMARY_FILE$OUTPUT_DIR/summary/run_summary.txt BLOCKS(block1 block2 block3 block4) CORNERS(typ slow fast) export STARRC_HOME/home/eda/synopsys/starrc/VERSION export PATH$STARRC_HOME/linux64/bin:$PATH export LM_LICENSE_FILE27000license_host export SNPSLMD_LICENSE_FILE27000license_host mkdir -p $SPEF_DIR $LOG_DIR $(dirname $SUMMARY_FILE) : $SUMMARY_FILE run_one_extraction() { local block$1 local corner$2 local cmd_file$OUTPUT_DIR/${block}.${corner}.cmd local log_file$LOG_DIR/${block}.${corner}.log local spef_file$SPEF_DIR/${block}.${corner}.spef cat $cmd_file EOF basename $block milkyway_library ${INPUT_DIR}/${block}/mw_library netlist_file ${INPUT_DIR}/${block}/${block}.v source -e ${SETUP_DIR}/90nm_${corner}.tluplus set_mapping_file ${SETUP_DIR}/star_mapping.mapping set_tlu_plus_files -layermap ${SETUP_DIR}/layermap.txt \\ -tluplus_typ ${SETUP_DIR}/90nm_${corner}.tluplus extract -design $block EOF echo [$(date %F %T)] Running $block / $corner ... | tee -a $SUMMARY_FILE starrc -64 -s $cmd_file $log_file 21 local ret$? if [ $ret -eq 0 ] [ -s $spef_file ]; then echo [OK] $block / $corner - $spef_file | tee -a $SUMMARY_FILE else echo [FAIL] $block / $corner. See $log_file | tee -a $SUMMARY_FILE return 1 fi } for block in ${BLOCKS[]}; do for corner in ${CORNERS[]}; do run_one_extraction $block $corner || true done done这个脚本里值得说明的细节有几个。set -euo pipefail里的u如果没设脚本里引用未定义变量时不会报错而一旦变量名拼错它就会静默地拿空值去生成路径这种错误在批量运行时会非常隐蔽加上u之后变量拼错会在第一时间暴露。|| true的作用是单个模块失败时不中断整个循环但因为它会把失败信息写进summary所以检查汇总文件就能知道哪些组合挂了。starrc -64 -s中的-64表示以64位模式运行90nm规模和以上的设计基本都需要开64位32位模式很容易在提大模块时内存耗尽。-s表示后跟的是StarRC命令文件。不同版本参数名称可能叫-cmds或者-f以你本机starrc -help输出为准。3.2 多corner和多模块循环的逻辑芯片项目里通常要提typ、slow、fast三个基本corner有时还会加一个某个电压下的特殊corner。对应到tluplus上不同corner会使用不同的工艺参数文件比如90nm_typ.tluplus、90nm_slow.tluplus、90nm_fast.tluplus它们反映的是不同工艺波动条件下互连线的电阻电容值。循环里动态拼接corner相关路径是我刻意为之的set_tlu_plus_files -tluplus_typ ${SETUP_DIR}/90nm_${corner}.tluplus。这样写虽然看起来只是字符串拼拼凑凑但能够保证如果我想新增一个corner只需要在CORNERS数组里加一个名字同时保证setup目录下有对应的tluplus文件其他逻辑完全不需要动。模块列表在脚本里维护成数组好处是顺序可控、可注释、可增删。项目早期可能只有block1、block2后来加入了新的block3只需要在数组里追加一项批量任务就会自动包含它。如果某次只想重跑某几个模块可以把其他模块临时注释掉或者把这个数组改成从外部参数读取这部分根据个人习惯来就好。3.3 日志与结果归档策略自动化流程里日志管理是做不做得好后期的分水岭。每个模块每个corner的运行log我用${block}.${corner}.log命名放在统一的log目录里如果StarRC运行失败log文件会保留下来方便事后查原因我绝不自动清理。summary文件之所以单独设置是为了在几十上百个运行组合里快速看到整体状态。每次运行结束脚本会把时间、模块名、corner、成功失败、SPEF路径写成一行最终汇总文件就像一张流水账配合grep就能快速找出所有失败项。比如想看这次批量运行有哪些失败一行命令就够了grep FAIL output/summary/run_summary.txt另外每天跑完一批我会顺手把summary文件按日期归档一份防止第二天开跑新一批时把前一天的结果覆盖掉。后面排查问题时如果发现是某个工艺角的数据异常可以直接翻那天的归档记录看看当时用的是什么版本的tluplus非常管用。4. 典型报错与排查技巧实录4.1 头号报错connected database layer does not have a valid itflayer这个报错我在网上搜过很多次应该是用StarRC的人最常撞见的问题之一报错原文比较长核心一句话就是某个database layer在itf/tluplus里找不到有效的itflayer定义。出现这个报错基本可以断定是物理层映射链路出了问题。我详细拆解一下这个报错的成因。StarRC在初始化阶段会把Milkyway数据库里的layer和techfile中的信息加载进来然后通过mapping file把这些layer映射到tluplus里定义的itflayer上。但凡其中一环接不上比如tluplus里根本没有某个layer、mapping文件里把M7写成了M8、或者Milkyway库里的layer名字与tluplus中的命名规则不一致StarRC就会在“connected database layer”这一步检查时给出这个错误。针对90nm项目的排查我建议按下面的顺序走第一步打开log文件用grep定位到第一条报错前后的layer名字确认是哪个金属层或通孔层出了问题。比如报错信息里如果提到M5那就优先检查M5相关的映射和定义。第二步核对tluplus文件来源。90nm工艺的tluplus不同代工厂、不同版本差异很大。确认当前tluplus是工艺厂针对这个项目坐标点和金属层版本提供的那个而不是从其他项目随手拷贝的。第三步打开mapping file逐行核对有问题的layer的映射关系。mapping里一般长这样M5 M5 3意思是指定的milkyway层名、tluplus层名、以及层编号。如果第二列和tluplus中的itflayer名字不一致就会触发这个报错。第四步检查Milkyway库的完整性。有时候库是从旧节点复制过来或者只导入了一部分层次导致某些layer在库里有几何数据但techfile里没有对应定义这种情况同样会报这个错。我在实际项目里遇到这个报错最后定位到是mapping file里M6和M7两行写反了StarRC在映射M6时发现对应tluplus里的itflayer描述不合法直接抛错。修正mapping之后同一套tluplus一次通过。这里要特别提醒改mapping file之前一定先确认工艺文件和tluplus的版本别在错误的地基上修修补补。4.2 其他高频报错速查表除了itflayer问题StarRC实战中还有几类高频报错我把它们整理成速查表方便批处理时遇到了直接对症下药。报错关键字常见原因处理建议Cant open milkyway library库路径写错或读权限不足检查命令文件里的路径确认当前用户对库文件夹有读权限Layer xxx not foundtechfile或mapping里缺少该层核对techfile的layer定义补充mapping映射netlist file not found门级网表路径不对确认网表文件存在且没有拼错名称Memory allocation failed设计规模大而32位模式不够用改用starrc -64检查服务器内存和swapCannot open tluplus filetluplus路径错误检查变量拼接后的完整路径corner名是否拼写正确License checkout failedlicense不可用查license服务器确认StarRC功能feature未被占用满这张表看起来朴素但每一条都是我或身边同事实打实踩过的坑。特别是netlist file not found在批量脚本里特别容易出因为模块的网表命名不统一有的叫block1.v有的叫block1_typ.v脚本一拼路径就找不到了。后来我把输入文件命名规范统一成模块名.v这类报错减少了很多。4.3 排查方法论先看log再猜原因在做自动化脚本的时候我还踩过一个心态上的坑一看到报错就埋头改脚本改来改去发现其实问题压根不在脚本而在StarRC本身的输入数据上。后来我给自己定了一条规矩任何报错先看log再看输入文件最后才动脚本。StarRC的log文件通常会打印出它加载每一个文件的过程包括tluplus路径、mapping内容、layer统计信息。这些信息是判断问题根因的第一手资料。grep -i error\|warning extract.log可以快速抓高亮信息但不要只盯着error看warning有时候才是真凶比如某个layer匹配不上它可能只是个warning但最后输出的SPEF里就少了那一层的RC。批量运行时代价最大的排查方式是跑完一整批之后才发现某一个corner的所有模块都错在同一处。所以我在主循环里额外做了一步一个corner跑完一个小模块之后如果log里error关键字非常多立刻停掉整个批次。这个“先试跑一个再批量展开”的思路在提参这种计算密集任务里能省下大量机时。5. 避坑指南与提效技巧5.1 让脚本更健壮的几个细节脚本能不能真正落地到项目里健壮性比花哨的写法重要得多。我吃过几次亏之后总结了下面几条经验不要在Windows下编辑脚本再传到Linux跑。CRLF换行符会让Shell脚本在运行时报出一堆莫名其妙的$\r: command not found排查起来非常痛苦。用dos2unix转一下就好。路径不要写死。90nm这个项目的工具有可能明天就升级、工艺文件有可能从别的目录迁移脚本里用相对路径加PROJECT_ROOT这种顶层变量来引用能降低后续维护成本。变量拼接处加引号。几乎每一条路径和文件名都要用双引号包起来防止路径里有空格或者特殊字符时被Shell拆开。失败重跑要能接着跑。如果某个模块失败后修复了输入文件重跑时最好只跑失败的组合而不是整个矩阵重来。我一般把summary文件里的成功项过滤出来和完整矩阵做一次差集再只跑差值。这些细节看起来零碎但一个脚本要连续跑几个小时甚至一整晚任何一个细节出问题浪费的都是宝贵的项目时间。5.2 提升批量提取效率的经验提参流程本身是计算密集的90nm节点上一个中等规模的模块跑一个corner可能要几十分钟到几个小时不等。批量自动化之后虽然不用人守着了但机时规划还是要做。我的做法是先用小模块把三个corner都试跑一遍确认tluplus、mapping、输出SPEF都没有问题再把大模块全部丢进队列排成串行或者按license余量开两三个并行。StarRC并行度不是越高越好最明显的瓶颈是license数量。如果公司license只有两三个StarRC feature开十个并行作业大部分时间都花在“checkout license失败后不断重试”上反而比串行还慢。所以在脚本里我加了一个简单的并发控制限制同时运行的StarRC进程数比如用xargs -P 3或者一个计数信号量控制后台进程数。另外批量跑的时候我一般把nice命令加上nice -n 10 starrc -64 -s ...让提参作业让出一些CPU优先级给其他在跑PR的同事这个习惯在多人共用的服务器上非常重要能少很多同事间的摩擦。5.3 输出SPEF的快速质检方法拿到SPEF之后不要直接丢给PrimeTime。哪怕StarRC退出码是0、SPEF文件也不是空的也不能保证数据就一定合理。我习惯在每个批次结束后对生成的SPEF做一次快速质检主要看三类指标文件大小是否在合理范围、电容总量是否和版图面积匹配、电阻数量是否为0。电阻数量为0是初学者最容易忽略的问题。如果extract命令里没有正确开启电阻提取选项或者mapping里没有定义通孔电阻StarRC输出的SPEF里可能只有电容没有电阻。在90nm节点不提电阻等于白提后续时序分析对线路延迟的估算会过于乐观hold time可能漏报。我一般用一个简单脚本统计每个SPEF里的电阻条目数量for f in output/spef/*.spef; do r_count$(grep -c ^\*R $f) c_count$(grep -c ^\*C $f) echo $f R$r_count C$c_count done如果某个模块的R条目数量是0基本可以确定这个SPEF不能用回去查命令文件里有没有把电阻提取打开。这一步花不了几分钟但能在STA之前拦住一堆返工。这个自动化方案落地之后我再也没有回到过“手工跑提参再自己盯log”的日子。每次跑完批量任务对照summary里的成功标记就可以放心进行下一步即使出现失败项也有足够的日志和归档让人快速定位。对于90nm这种工艺节点提参自动化的意义不只是省时间更重要的是把人为操作带来的不确定性压到最低让后端的每一步都站在可控的地基上。