说是脚 本,其实还是一个
tvf, 上期《用TVF输出只有label的LAYER简化GDS》反响不错, 其实稍加更改可以输出指定名称的net的GDS, 每个名称输出一个GDS, 尤其适用于power和ground的提取。用calibre -drc跑一遍这个tvf就可以了
对比
与《用TVF输出只有label的LAYER简化GDS》相比以有下变化
- 前者是抓取
所有text, 当前是 抓取指定TEXT - 前者用了
net area ratio, 当前直接用net - 前者输出所有结果到
同一GDS, 当前输出到不同GDS
预览
- 输入

- out1

- bit out

相同部分
第一到第六步不变, 同 《用TVF输出只有label的LAYER简化GDS》
第七步:提取指定列表NET-GDS
这是整个流程的核心步骤,目的是从超大版图中筛选出指定label的金属层网络,结果将GDS分割成以 label名为名称的NET的网络的多个GDS, 方便查看每个NET或抽取RCX。
分两步处理:先用NET 命令抓取所指定 layer , 再用 DFM 命令将结果按 layer Number 写成新的 GDS 。
# 定义需要单独导出GDS的NET名称
set outNetList [list vss vdd net<4>]
# 外层循环:遍历NET, 并为每个NET导出GDS
for {set i 0} {$i<[llength $outNetList]} {incr i } {
# 提取当前遍历到的原始网络名(如vss、net<4>)
set NET [lindex $outNetList $i]
# 处理网络名中的特殊字符:尖括号< >在Calibre/文件命名中是非法字符,会导致层名/文件名报错
# 判断当前网络名是否包含右尖括号>,若包含则进入字符替换逻辑
if {[string last ">" ${NET}] != -1} {
# 将网络名中的<和>统一替换为#,生成合法的网络别名(如net<4> → net#4#)
set NET_ [string map {"<" "#" ">" "#"} ${NET} ]
} else {
# 无特殊字符的网络名(如vss/vdd),直接使用原名称作为合法别名
set NET_ ${NET}
}
# 内层循环:遍历所有版图层列表ALLLAYER(金属/过孔层,如M6/V56/M5),为当前网络提取各层的版图区域
for {set j 0} {$j<[llength $ALLLAYER]} {incr j 1 } {
# 提取当前遍历到的版图层名(如M6、V56、M5)
set MX [lindex $ALLLAYER $j]
# 核心TVF命令:创建专属层,层名为「合法网络别名_版图层名」,层内容为「当前版图层中属于目标网络的区域」
# 例:vss_M6 → M6层中所有属于vss网络的版图区域
SETLAYER ${NET_}_${MX} = ${MX} NET \"${NET}\"
# 保留新建的专属层,COPY关键字表示将该层结果存入Calibre结果集,为后续导出GDS做准备
RULECHECK ${NET_}_${MX} {
COPY ${NET_}_${MX}
}
}
# 核心导出命令:为当前目标网络导出专属GDS文件,仅包含该网络在各层的版图区域
# 解决超大版图整层导出冗余的问题,实现「单网络精准导出GDS」(如仅导出vss网络的所有层GDS)
RULECHECK ${NET_}_${MX}_GDS {
# 导出GDS文件,文件名为「合法网络别名_最后一个版图层名.gds」,存储该网络的所有层版图
DFM RDB GDS ${NET_}_${MX}.gds \
${NET_}_M6 38 \ # 映射:网络专属层vss_M6 → 物理层号38(原M6A层,与前文层映射一致)
${NET_}_V56 39 \ # 映射:网络专属层vss_V56 → 物理层号39(原V56层)
${NET_}_M5 33 \ # 映射:网络专属层vss_M5 → 物理层号33(原M5A层)
${NET_}_V45 32 \ # 映射:网络专属层vss_V45 → 物理层号32(原V45层)
${NET_}_M4 31 \ # 映射:网络专属层vss_M4 → 物理层号31(原M4A层)
${NET_}_V34 29 \ # 映射:网络专属层vss_V34 → 物理层号29(原V34层)
${NET_}_M3 28 # 映射:网络专属层vss_M3 → 物理层号28(原M3A层)
}
}核心代码知识点解析
这部分代码融合了TCL 基础语法和Calibre TVF 专属命令,是实现「指定网络精准导出」的关键,这里拆解几个核心点,方便大家理解和修改:
- TCL 列表遍历:
llength $outNetList获取网络列表长度,lindex $outNetList $i按索引提取单个网络,是 TCL 遍历列表的标准写法,适配任意长度的网络列表; - 特殊字符处理:
string last ">" ${NET}判断是否包含尖括号,string map实现字符批量替换,解决 Calibre层名 / 文件名非法字符问题(Calibre 不支持< > / \ :等特殊字符); - TVF 层创建:
SETLAYER A = B NET "C"是 TVF 筛选网络的核心语法,表示从 B 层中提取属于 C 网络的区域,创建为新层 A,精准过滤非目标网络的版图; - GDS 导出映射:
DFM RDB GDS 文件名.gds 新层名 物理层号实现 Calibre 结果层到 GDS 物理层的映射,层号需与原始版图的 GDS 层号一致,否则后续工具无法识别。
实操关键注意事项
实际跑脚本时,这几个点直接决定脚本能否正常运行,也是新手最容易踩坑的地方,务必注意:
1. 提前定义ALLLAYER列表
脚本中用到的ALLLAYER是所有需要提取的金属 / 过孔层列表,需在该段代码前提前定义,格式如下(根据自己的工艺层调整):
# 定义需要提取的金属层+过孔层,按从上到下/从下到上顺序均可
set ALLLAYER [list M6 V56 M5 V45 M4 V34 M3]2. 支持任意数量的目标网络
outNetList列表可按需增删,比如只需提取电源地则写[list vss vdd],需要提取多个信号网络则写[list vss vdd net<0> net<1> core_vdd],脚本会自动遍历并逐个导出 GDS;
# 定义需要单独导出GDS的NET名称
set outNetList [list vss vdd net<4>]3. 特殊字符处理可拓展
若你的网络名包含其他非法字符(如/、*),可在string map中补充替换规则,示例:
# 拓展处理/和*,替换为_
set NET_ [string map {"<" "#" ">" "#" "/" "_" "*" "_"} ${NET} ]4. 层号映射必须与工艺一致
DFM RDB GDS后的数字层号是 GDS 的物理层号,不是工艺层名,需与你的工艺 GDSII 层表完全匹配,若层号错误,导出的 GDS 会出现层丢失 / 层识别错误;
运行TVF
脚本编写完成后,将其保存为extract_net_gds.tvf(命名随意)
启动 calibre DRC GUI, rule部分填入 extract_net_gds.tvf, 推荐顺手 Load 一下。 正常 run DRC
运行结果
- 脚本运行完成后,在当前目录下会生成多个 GDS 文件,文件名格式为
网络名_最后一层名.gds(如vss_M3.gds、net#4#_M3.gds),每个文件对应一个指定网络的所有层版图。
-rwxr-xr-x 1 root root 1.5K mmm dd hh:mm net#4#_M3.gds
-rwxr-xr-x 1 root root 1.5K mmm dd hh:mm vdd_M3.gds
-rwxr-xr-x 1 root root 1.5K mmm dd hh:mm vss_M3.gds- DRC report
可以手动点亮

总结
本文在上一期 TVF 提取 label 层的基础上,通过指定网络筛选、循环遍历导出、特殊字符处理,实现了单个网络精准导出 GDS的功能,尤其适合电源(vdd)、地(vss)网络的提取,是超大规模版图设计中提升后端处理效率的实用小技巧。
该方法的核心是Calibre TVF 的SETLAYER ... NET语法和TCL 的循环遍历,代码简洁、易修改、可拓展,只需根据自己的工艺层和目标网络做简单调整,即可快速落地使用。
后续会继续分享 Calibre TVF/SVRF 的实用技巧,比如如何批量提取版图中的接触孔、如何自动生成版图报告等,感兴趣的小伙伴可以持续关注~
如果在使用过程中遇到问题,欢迎在评论区留言交流,一起探讨解决!
完整代码
#!tvf
#####################################
# release `by chiplayout.net`
# calibre version: 2016
# var: MAPLIST, ALLLAYER, outNetList
####################################
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 $LAY1 0]
set LAY1_DT [lindex $LAY1 1]
set LAY1_NA [lindex $LAY1 2]
set LAY1_TY [lindex $LAY1 3]
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
}
set outNetList [list vss vdd net<4>]
for {set i 0} {$i<[llength $outNetList]} {incr i } {
set NET [lindex $outNetList $i]
if {[string last ">" ${NET}] != -1} {
set NET_ [string map {"<" "#" ">" "#"} ${NET} ]
} else {
set NET_ ${NET}
}
for {set j 0} {$j<[llength $ALLLAYER]} {incr j 1 } {
set MX [lindex $ALLLAYER $j]
SETLAYER ${NET_}_${MX} = ${MX} NET \"${NET}\"
RULECHECK ${NET_}_${MX} {
COPY ${NET_}_${MX}
}
}
RULECHECK ${NET_}_${MX}_GDS {
DFM RDB GDS ${NET_}_${MX}.gds \
${NET_}_M6 38 \
${NET_}_V56 39 \
${NET_}_M5 33 \
${NET_}_V45 32 \
${NET_}_M4 31 \
${NET_}_V34 29 \
${NET_}_M3 28
}
}