最近提取一个超大版图LEF,约200K个端口,abstract直接跑速度慢到让人崩溃,几天都出不来结果,个人感觉可能是NET太多了,就想着把GDS处理一下,只出有label的layer, 速度能快点。
但这次我没用到常用的SRVF——主要是TVF能和TCL完美嵌套,而且金属层的连接本身有固定连续性,用for循环就能批量处理,效率比纯SRVF高太多。
输入输出预览
输入
输出

整个流程一共8个核心步骤,从基础设置到最终导出简化GDS,每一步都会讲清做什么、为什么做、代码怎么理解,新手也能跟着操作。
先说明下核心工具
TVF是Calibre的 TCL Verification Format 规则格式,能融合TCL的循环、变量定义和Calibre的版图处理命令,批量处理多层版图时特别灵活,这也是这次选它而非SRVF的核心原因。
所有代码都基于Calibre运行,正常 calibre -drc , rule选中 tvf就行了。
第一步:基础环境设置
先做全局的基础配置,定好精度、文本处理规则这些核心参数,避免后续处理出现精度偏差、文本抓取错误的问题。 这部分是所有TVF规则的基础,直接复制就能用,个别参数可根据自己的版图精度调整。
#!tvf 声明这是TVF格式规则,让Calibredrv识别
# 导入TVF核心命令空间,否则后续TVF命令无法执行
namespace import tvf::*
# VERBATIM块:写入纯Calibre DRC/SRVF命令,和直接写DRC规则一致
VERBATIM {
PRECISION 1000 # 版图精度,1000表示单位是nm,根据自己的工艺改
RESOLUTION 1 # 版图分辨率,和PRECISION匹配即可
DRC KEEP EMPTY NO # 不保留空的结果层,减少冗余
TEXT DEPTH PRIMARY # 只抓取顶层的text/label,避免底层冗余文本干扰
DRC CELL TEXT NO # 不处理单元内部的文本,只关注顶层pin/label
DRC MAP TEXT YES # 开启文本层映射,让文本和版图层关联
DRC MAP TEXT DEPTH PRIMARY # 文本映射也只处理顶层
DRC MAXIMUM RESULTS ALL # 保留所有处理结果,避免关键层被过滤
}第二步:定义MAP层列表
把需要处理的金属层、过孔层、文本层的层号、数据类型、层名整理成列表,后续用循环批量做层映射,不用重复写LAYER命令——如果后续要加层/改层,只需要改这个列表,不用动循环代码,维护起来超方便。
列表里的每一行格式是:层号.数据类型 自定义层名 类型(TEXTTYPE/DATATYPE),比如45.0 M6T TEXTTYPE表示45层0数据类型是M6的文本层,38.0 M6A DATATYPE表示38层0数据类型是M6的金属主体层。
# 定义需要映射的层列表MAPLIST,按「层号.数据类型 自定义层名 类型」整理
set MAPLIST [list \
"45.0 M6T TEXTTYPE" \
"44.0 M5T TEXTTYPE" \
"43.0 M4T TEXTTYPE" \
"42.0 M3T TEXTTYPE" \
"38.0 M6A DATATYPE" \
"39.0 V56 DATATYPE" \
"33.0 M5A DATATYPE" \
"32.0 V45 DATATYPE" \
"31.0 M4A DATATYPE" \
"29.0 V34 DATATYPE" \
"28.0 M3A DATATYPE" \
"38.1 M6R DATATYPE" \
"33.1 M5R DATATYPE" \
"31.1 M4R DATATYPE" \
"28.1 M3R DATATYPE" \
]第三步:批量映射版图层
这一步是核心循环之一,遍历第二步定义的MAPLIST,把物理层(层号+数据类型) 映射成Calibre可识别的逻辑层号,再给逻辑层绑定自定义层名(比如M6A、V56)。
为什么要做层映射?因为不同工艺的物理层号可能零散,映射成连续的逻辑层后,后续用自定义层名操作更直观,也避免层号冲突。
# 遍历MAPLIST,批量做层映射和层名绑定
foreach LAY1 $MAPLIST {
# 拆分每一行的参数,提取层号、数据类型、自定义层名、类型
set LAY1_NU [lindex [split [lindex $LAY1 0] "."] 0] # 物理层号(整数部分)
set LAY1_DT [lindex [split [lindex $LAY1 0] "."] 1] # 数据类型(小数部分,无则为0)
set LAY1_NA [lindex $LAY1 1] # 自定义层名(如M6T、V56)
set LAY1_TY [lindex $LAY1 2] # 层类型(TEXTTYPE/DATATYPE)
# 生成唯一的逻辑层号:层号*100+数据类型,避免不同层/数据类型映射冲突
set LAY1_MP [expr $LAY1_NU*100+$LAY1_DT]
# 核心映射命令:把物理层映射成逻辑层号
LAYER MAP $LAY1_NU $LAY1_TY $LAY1_DT $LAY1_MP
# 给逻辑层绑定自定义层名,后续直接用层名操作(如M6A)
LAYER $LAY1_NA $LAY1_MP
}第四步:定义金属/过孔处理顺序
按上层金属→过孔→下层金属的顺序定义列表,后续的文本绑定、层连接都会按这个顺序来,保证金属层之间的电气连续性——比如M6→V56→M5→V45,这是芯片版图中金属层的常规连接顺序,大家根据自己的工艺层顺序修改即可。
# 定义金属层+过孔层的处理顺序,按「上层金属 过孔 下层金属 过孔」排列
set ALLLAYER [list M6 V56 M5 V45 M4 V34 M3]第五步:将Label/Text绑定到对应金属层
先对金属层做预处理(排除金属层的冗余区域),再把顶层的label/text绑定到对应金属层——只有完成绑定,后续才能识别“哪个label属于哪个金属层的网络”,这是提取带label网络的关键。
用for循环步长设为2,只遍历列表中的金属层(M6、M5、M4、M3),跳过过孔层,批量处理所有金属层的文本绑定,不用重复写代码。
# 遍历ALLLAYER,步长2→只处理金属层(M6、M5、M4、M3)
for {set i 0} {$i<[llength $ALLLAYER]} {incr i 2 } {
set MX [lindex $ALLLAYER $i] # 提取当前金属层(如M6、M5)
# 金属层预处理:保留金属主体层(${MX}A),排除金属冗余层(${MX}R)
setlayer $MX = ${MX}A NOT (OR ${MX}R)
# 指定当前金属层对应的文本层(如M6对应M6T)
TEXT LAYER ${MX}T
# 核心命令:把文本层/${MX}T的label绑定到预处理后的金属层/$MX上
ATTACH ${MX}T ${MX}
}第六步:批量建立金属层的电气连接
按第四步定义的顺序,用CONNECT命令建立上层金属→过孔→下层金属的电气连接,让Calibre识别出连续的电气网络——只有识别出完整网络,后续才能精准提取带label的网络区域,避免网络断裂导致的LEF提取错误。
循环到列表倒数第二个元素,避免下标越界,自动匹配「金属-过孔-下层金属」的组合。
# 遍历ALLLAYER,建立金属层之间的电气连接,循环到倒数第二个元素
for {set i 0} {$i<[llength $ALLLAYER]-2} {incr i 2 } {
set MU [lindex $ALLLAYER $i] # 上层金属(如M6)
set VA [lindex $ALLLAYER [expr $i+1]] # 上下金属之间的过孔(如V56)
set MD [lindex $ALLLAYER [expr $i+2]] # 下层金属(如M5)
# 核心命令:建立电气连接,MU和MD通过VA连通,形成完整网络
CONNECT $MU $MD BY $VA
}第七步:提取带Label的目标网络层
这是整个流程的核心步骤,目的是从超大版图中筛选出带有label的金属层网络,去掉所有无label的冗余版图区域——这一步做完,GDS的冗余信息会大幅减少,后续abstract提LEF的速度会质的提升。
分两步处理:先给文本层做微小扩展生成标记层,再通过网络面积占比筛选出带label的网络,最后保留这些目标层。
# 遍历金属层,批量提取带label的目标网络层
for {set i 0} {$i<[llength $ALLLAYER]} {incr i 2 } {
set MT [lindex $ALLLAYER $i] # 提取当前金属层(如M6)
# 生成文本标记层:将文本层微小扩展(0.002um),避免文本和金属层错位导致匹配失败
# PRIMARY ONLY:只保留顶层的标记,排除底层冗余
SETLAYER ${MT}_MK = EXPAND TEXT \"?\" ${MT}T BY 0.002 PRIMARY ONLY
# 把标记层和金属层建立连接,让工具识别标记对应的金属网络
CONNECT ${MT} ${MT}_MK
# 嵌套循环:提取所有金属层在当前标记层内的网络(带label的网络)
for {set j 0} {$j<[llength $ALLLAYER]} {incr j 2 } {
set MX [lindex $ALLLAYER $j] # 遍历所有金属层(如M6、M5、M4、M3)
# 筛选网络:MX金属层和MT标记层重叠面积占比>0的区域,即为带label的有效网络
SETLAYER ${MX}_${MT}TEXT_NET = NET AREA RATIO ${MX} OVER ${MT}_MK >0
# 保留筛选后的网络层,为后续导出GDS做准备
RULECHECK ${MX}_${MT}TEXT_NET {COPY ${MX}_${MT}TEXT_NET}
}
}小技巧
EXPAND TEXT的BY值(这里是0.002)可以根据自己的版图精度调整,目的是让文本层和金属层有微小重叠,避免因版图绘制的微小错位导致label和金属层匹配失败,一般设0.001~0.005um即可。
由于在循环中,这一步包含所有 结果,如下:
RULECHECK M6_M6TEXT_NET ... TOTAL Result Count = 6 (6)
RULECHECK M5_M6TEXT_NET ... TOTAL Result Count = 3 (3)
RULECHECK M4_M6TEXT_NET ... TOTAL Result Count = 3 (3)
RULECHECK M3_M6TEXT_NET ... TOTAL Result Count = 3 (3)
RULECHECK M3_M3TEXT_NET ... TOTAL Result Count = 6 (6)第八步:导出简化后的GDS
最后一步,用RULECHECK OUTGDS命令把第七步筛选出的带label的目标网络层导出成新的GDS文件,这个新GDS只保留了核心的带label网络,冗余信息全部被过滤,后续用这个GDS跑abstract提取LEF,速度会大幅提升!
代码里只举了M6和M3的例子,大家可以根据自己的需求,按「目标层 物理层号」的格式添加其他层。
# 导出简化后的GDS文件,核心只保留带label的目标网络层
RULECHECK OUTGDS {
DFM RDB GDS texted.gds \ # 导出的GDS文件名,可自定义
M6_M6TEXT_NET 38 \ # 目标层→映射到物理层38,和原M6A层号一致
M3_M3TEXT_NET 28 # 目标层→映射到物理层28,和原M3A层号一致
# 如需导出其他层,按「层名 物理层号」的格式继续加即可,比如M5_M5TEXT_NET 33
}运行方法
所有代码写完后,保存为extract_texted_layer.tvf, 在 calibre DRC 时, rule选择 extract_texted_layer.tvf ,其它正常设置, 点 run 就行。
实用避坑小贴士
- 层号/数据类型一定要对应自己的版图:MAPLIST里的层号、数据类型是我项目里的,大家要先从自己的GDS里查清楚金属层、文本层、过孔层的物理层信息,再修改列表,这是最容易踩的坑;
- 金属层顺序不能乱:ALLLAYER里的「金属-过孔-金属」顺序要和自己的工艺层一致,否则会出现连接错误;
- 精度参数匹配:PRECISION要和自己的版图单位匹配(比如1000是nm,10000是0.1nm),避免版图缩放错误;
- 扩展值合理调整:EXPAND TEXT的BY值不要太大,否则会导致标记层覆盖过多冗余区域,失去简化意义。
最后总结
这次的核心思路就是**「先筛选再处理」**:针对20万Pin的超大版图,不用工具直接处理原始GDS,而是用TVF+TCL的循环优势,精准抓取带label的核心网络层,过滤掉所有无label的冗余版图信息,让后续的LEF提取(abstract)少做无用功,从根源上解决速度慢的问题。
整个规则的灵活性很高,不管是增加金属层、修改层号,还是调整筛选条件,只需要改对应的列表和参数,不用重写核心逻辑,大家可以直接把这份代码当成模板,套到自己的超大版图项目里!
阅读原文, 获取完整代码
完整代码
#!tvf
namespace import tvf::*
VERBATIM {
PRECISION 1000
RESOLUTION 1
DRC KEEP EMPTY NO
TEXT DEPTH PRIMARY
DRC CELL TEXT NO
DRC MAP TEXT YES
DRC MAP TEXT DEPTH PRIMARY
DRC MAXIMUM RESULTS ALL
}
set MAPLIST [list \
"45.0 M6T TEXTTYPE" \
"44.0 M5T TEXTTYPE" \
"43.0 M4T TEXTTYPE" \
"42.0 M3T TEXTTYPE" \
"38.0 M6A DATATYPE" \
"39.0 V56 DATATYPE" \
"33.0 M5A DATATYPE" \
"32.0 V45 DATATYPE" \
"31.0 M4A DATATYPE" \
"29.0 V34 DATATYPE" \
"28.0 M3A DATATYPE" \
"38.1 M6R DATATYPE" \
"33.1 M5R DATATYPE" \
"31.1 M4R DATATYPE" \
"28.1 M3R DATATYPE" \
]
foreach LAY1 $MAPLIST {
set LAY1_NU [lindex [split [lindex $LAY1 0] "."] 0]
set LAY1_DT [lindex [split [lindex $LAY1 0] "."] 1]
set LAY1_NA [lindex $LAY1 1]
set LAY1_TY [lindex $LAY1 2]
set LAY1_MP [expr $LAY1_NU*100+$LAY1_DT]
LAYER MAP $LAY1_NU $LAY1_TY $LAY1_DT $LAY1_MP
LAYER $LAY1_NA $LAY1_MP
}
set ALLLAYER [list M6 V56 M5 V45 M4 V34 M3]
for {set i 0} {$i<[llength $ALLLAYER]} {incr i 2 } {
set MX [lindex $ALLLAYER $i]
setlayer $MX = ${MX}A NOT (OR ${MX}R)
TEXT LAYER ${MX}T
ATTACH ${MX}T ${MX}
}
for {set i 0} {$i<[llength $ALLLAYER]-2} {incr i 2 } {
set MU [lindex $ALLLAYER $i]
set VA [lindex $ALLLAYER [expr $i+1]]
set MD [lindex $ALLLAYER [expr $i+2]]
CONNECT $MU $MD BY $VA
}
for {set i 0} {$i<[llength $ALLLAYER]} {incr i 2 } {
set MT [lindex $ALLLAYER $i]
SETLAYER ${MT}_MK = EXPAND TEXT \"?\" ${MT}T BY 0.002 PRIMARY ONLY
CONNECT ${MT} ${MT}_MK
for {set j 0} {$j<[llength $ALLLAYER]} {incr j 2 } {
set MX [lindex $ALLLAYER $j]
SETLAYER ${MX}_${MT}TEXT_NET = NET AREA RATIO ${MX} OVER ${MT}_MK >0
RULECHECK ${MX}_${MT}TEXT_NET {COPY ${MX}_${MT}TEXT_NET}
}
}
RULECHECK OUTGDS {
DFM RDB GDS texted.gds \
M6_M6TEXT_NET 38 \
M3_M3TEXT_NET 28
}