Featured image of post 用TVF输出只有label的LAYER简化GDS

用TVF输出只有label的LAYER简化GDS

最近提取一个超大版图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]                          # 自定义层名如M6TV56
  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]  # 提取当前金属层如M6M5
  # 金属层预处理:保留金属主体层(${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 就行。

实用避坑小贴士

  1. 层号/数据类型一定要对应自己的版图:MAPLIST里的层号、数据类型是我项目里的,大家要先从自己的GDS里查清楚金属层、文本层、过孔层的物理层信息,再修改列表,这是最容易踩的坑;
  2. 金属层顺序不能乱:ALLLAYER里的「金属-过孔-金属」顺序要和自己的工艺层一致,否则会出现连接错误;
  3. 精度参数匹配:PRECISION要和自己的版图单位匹配(比如1000是nm,10000是0.1nm),避免版图缩放错误;
  4. 扩展值合理调整: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 
}
陕ICP备20000710号
本站已运行15年5月16天
发表了398篇文章 · 总计14万1千字
最后更新:2026-01-31
Built with Hugo
Theme Stack designed by Jimmy