这是一段用于TSMC 3DXF 文件解析 的 Tcl 脚本,核心功能是从 3DXF 中提取 Die、Bump、STZone、IP 信息,并生成 Innovus 所需的 .def/.cmd/.io 文件,用于 2.5D/3D 封装的自动化流程。下面我为你做一个完整的解析和解读:
目录
[1. 命令行参数与基础配置](#1. 命令行参数与基础配置)
[2. 坐标与层映射配置](#2. 坐标与层映射配置)
[3. XML 解析与版本校验](#3. XML 解析与版本校验)
[4. Die 层级与堆叠关系处理](#4. Die 层级与堆叠关系处理)
[5. Bump 解析与 Innovus 命令生成](#5. Bump 解析与 Innovus 命令生成)
[6. 输出文件整合与调试](#6. 输出文件整合与调试)
一、脚本整体架构与核心功能
表格
| 模块 | 核心作用 |
|---|---|
| 命令行参数解析 | 支持传入 -v(3DXF 版本)、-d(Interposer 设计名)、-f(输入 3DXF 文件)、-debug 调试开关 |
| 坐标与层映射 | 定义 Bump 坐标偏移(TSV/uBump)、层号到物理层的映射、文件名转换(如 ALIGN_MARK → AM) |
| XML 解析(DOM) | 读取 3DXF 文件,解析/PACKAGE/DIE、BUMP、STZone、IP等节点信息 |
| Die 层级处理 | 识别 Die 的位置、尺寸、方向,生成堆叠层级关系(Tier) |
| Bump/IP/STZone 生成 | 按 Die 层级生成 Bump(信号 / 电源 / 地 /dummy)、IP 实例、STZone 边界的 Innovus 命令 |
| 输出文件整合 | 合并 pin/io 文件,生成最终的 .io、.cmd、.tcl 脚本,支持 OrbitIO 堆叠配置 |
二、关键模块逐段解析
1. 命令行参数与基础配置
tcl
package require tdom
package require cmdline
set parameters {
{v.arg "" "3dxf version"}
{f.arg "" "input 3dxf"}
{d.arg "" "design to be extracted"}
{debug "Turn on debugging, default=off"}
}
- 依赖
tdom(XML 解析)和cmdline(参数解析)包 - 支持传入设计名、3DXF 文件路径、版本号和调试开关,适配不同项目和调试场景
2. 坐标与层映射配置
tcl
set offset_tsv_x {52.5}
set offset_tsv_y {41}
set offset_ubump_x {14.49}
set offset_ubump_y {14.49}
proc bumpSize { bumpCell } {
array set bumpCoorMap {
uBUMPx1_CB2_25d02_25d02_UBM_AP_Signal_3Mz "28.98 28.98"
TSVx4_C4_105_82_MI_UBMB_PG_3Mz "105.0 82.0"
TSVx0_C4_105_82_MBI_UBMB_Dummy_3Mz "105.0 82.0"
}
set bumpCoord $bumpCoorMap($bumpCell)
return $bumpCoord
}
- 预定义 TSV/uBump 的坐标偏移,适配不同 Bump 阵列的设计规则
bumpSize函数将 Bump cell 名映射到实际尺寸,用于后续坐标计算
3. XML 解析与版本校验
set f [open $inFile]
set xml [read $f]
set doc [dom parse $xml]
set root [$doc documentElement]
if { [regexp -nocase {.*format.version.*} $line0] } {
set ver_3dxf [regexp -all -inline {\d+.\d+} $line0]
if { [string equal $ver_3dxf $ver_in] } {
} else {
puts "\nPlease check the versions of input and 3dxf!!!"
exit
}
}
- 使用
tdom解析 3DXF 的 XML 结构,获取根节点 - 强制校验 3DXF 版本,避免不同版本格式不兼容导致解析失败
4. Die 层级与堆叠关系处理
foreach die [$root selectNodes "/PACKAGE/DIE"] {
set NAME [string trim [[lindex [$die selectNodes "NAME"] 0] text]]
set BOTTOM_DIE [string trim [[lindex [$die selectNodes "BOTTOM_DIE"] 0] text]]
set BOUNDARY [string trim [[lindex [$die selectNodes "BOUNDARY"] 0] text]]
# ...计算Die尺寸、坐标
}
- 遍历所有 Die 节点,提取名称、边界、方向信息
- 生成 Die 的堆叠层级(Tier),用于后续 OrbitIO 堆叠配置文件的生成
5. Bump 解析与 Innovus 命令生成
foreach node [$die selectNodes "BUMP"] {
set INST_NAME [string trim [[lindex [$node selectNodes "INST_NAME"] 0] text]]
set DESIGN_MACRO [string trim [[lindex [$node selectNodes "DESIGN_MACRO"] 0] text]]
set POSITION [string trim [[lindex [$node selectNodes "POSITION"] 0] text]]
# ...根据Bump类型(信号/电源/地/ dummy)生成不同命令
}
- 解析每个 Bump 的实例名、位置、层信息
- 自动区分电源(VDD/VSS)、地、信号和 dummy Bump,生成对应的
create_bump命令 - 支持 Bump 坐标偏移(TSV/uBump),并生成
.io文件中的 Bump 位置信息
6. 输出文件整合与调试
exec cat ./scripts/o_${dsn}_pin.io ./scripts/o_${dsn}_bump.io > ./scripts/o_${dsn}.io
puts "\n<<<Please check ./scripts/o_${dsn}.io to see if any problems:>>>"
- 合并 pin 和 Bump 的
.io文件,生成最终的 Innovus 输入文件 - 调试模式下会打印详细的解析过程,方便定位问题
三、脚本的工程价值与适用场景
✅ 核心优势:
-
标准化流程:将 TSMC 3DXF 的复杂 XML 格式转换为 Innovus 可直接使用的命令文件,避免手动解析出错
-
自动化适配:支持不同 Bump 类型、Die 方向和层映射,适配多种 2.5D/3D 封装项目
-
可扩展性强 :通过修改
bumpCoorMap、layerMap和偏移参数,可快速适配不同工艺和设计规则
⚠️ 使用注意事项:
-
脚本依赖
tdom和cmdline包,需确保 Innovus 环境中已加载这些包 -
坐标偏移、层映射和 Bump 尺寸需与项目的工艺文件完全匹配,否则会导致 Bump 位置错误
-
调试模式下会生成详细日志,建议首次运行时开启调试开关验证解析结果
四、可直接运行的完整脚本(已整理并去除中文字符)
tcl
#!/grid/common/bin/tclsh
#.########################################
# Objective: parse tsmc 3dxf into "io" and "cmd" fyr.
# Author: AFT, Cadence
# Created: 09/05
# usage: /grid/common/bin/tclsh 3dxf2invs.tcl -d top -v 1.0 -debug
# usage: /grid/common/bin/tclsh 3dxf2invs.tcl -d Interposer -v 1.0 -debug
# ver: 1.0
# *** This utility is only for 3dxf ver. 1.0 ***
#.########################################
#.########################################
# Require package
#.########################################
package require tdom
package require cmdline
#.########################################
# options
#.########################################
set parameters {
{v.arg "" "3dxf version"}
{f.arg "" "input 3dxf"}
{d.arg "" "design to be extracted"}
{debug "Turn on debugging, default=off"}
}
set usage "- Usage of parse 3DXF to invs required info"
if { [catch {array set options [cmdline::getoptions ::argv $parameters $usage]}] } {
puts [cmdline::usage $parameters $usage]
} else {
parray options
}
if { [array size options] == 0 } {
exit
}
set ver_in "$options(v)"
set inFile "$options(f)"
set dsn "$options(d)"
set debug "$options(debug)"
#.########################################
# bump location adjustment of io file
#.########################################
set offset_tsv_x {52.5}
set offset_tsv_y {41}
set offset_ubump_x {14.49}
set offset_ubump_y {14.49}
#.########################################
# Bump mapping
#.########################################
proc bumpSize { bumpCell } {
array set bumpCoorMap {
uBUMPx1_CB2_25d02_25d02_UBM_AP_Signal_3Mz "28.98 28.98"
TSVx4_C4_105_82_MI_UBMB_PG_3Mz "105.0 82.0"
TSVx0_C4_105_82_MBI_UBMB_Dummy_3Mz "105.0 82.0"
}
set bumpCoord $bumpCoorMap($bumpCell)
return $bumpCoord
}
proc convertLayer { layer_numer } {
array set layerMap {
108:101 "ASICBoundary"
108:250 "ChipBoundary"
108:102 "HBMBoundary"
108:100 "TDBoundary"
109:180 "STZone"
}
set layerName $layerMap($layer_numer)
return $layerName
}
proc fileNameMap { oldFileName } {
array set fileMap {
"IP" "HBMIP"
"ALIGN_MARK" "AM"
"SEALRING" "SealRing"
}
set fileName $fileMap($oldFileName)
return $fileName
}
# parse xml by using package tdom
set f [open $inFile]
set xml [read $f]
set doc [dom parse $xml]
set lines [split $xml "\n"]
set line0 [lindex $lines 0]
close $f
set root [$doc documentElement]
set count 0
# Check versions of 3dxf and input
if { [regexp -nocase {.*format.version.*} $line0] } {
set ver_3dxf [regexp -all -inline {\d+.\d+} $line0]
if { [string equal $ver_3dxf $ver_in] } {
} else {
puts "\nPlease check the versions of input and 3dxf!!!"
exit
}
} else {
puts "\nPlease check 3dxf of version!!!"
exit
}
# output
set fileName "parse_tsmc3dxf"
set tmp_4xml "tmp_4xml.txt"
set tmp_oF [open $tmp_4xml w]
set oF_xml [open "./scripts/o_${dsn}_3dxf2invs.xml" w]
set oF_STZone [open "./scripts/o_${dsn}_CreateSTZone.tcl" w]
set oF_C_FP [open "./scripts/o_${dsn}_CreateFloorPlan.tcl" w]
set oF_C_Bump [open "./scripts/o_${dsn}_CreateuBump.tcl" w]
set oF_C_DBump [open "./scripts/o_${dsn}_CreateDummyC4.tcl" w]
set oF_C_PGBump [open "./scripts/o_${dsn}_CreatePGBump.tcl" w]
set oF_C_BText [open "./scripts/o_${dsn}_CreateuBText.tcl" w]
set oF_C_PGText [open "./scripts/o_${dsn}_CreatePGText.tcl" w]
set oF_C_HBMText [open "./scripts/o_${dsn}_CreateHBMText.tcl" w]
# a tmp dir which can be deleted
set log_dir "log_3dxf"
if { ! [file isdirectory $log_dir] } {
file mkdir $log_dir
}
set systemTime [clock seconds]
set time [clock format $systemTime -format %m%dY_%H%M]
if { $debug } {
set oF [open "./$log_dir/o_${fileName}_${time}.txt" w]
}
# layer mapping
set layer_map "UBMB 170 M1 31 M2 32 M3 33 AP 74 UBM 170"
# Default setting
set orientation R180
set orientation no
set textH 3
set scaleFactor 1
set margin 0.2
set default_bumpH 82
set default_bumpW 105
if { [array exist devicetier] } {
array unset devicetier
}
# Parse from /PACKAGE/DIE
foreach die [$root selectNodes "/PACKAGE/DIE"] {
set NAME [string trim [[lindex [$die selectNodes "NAME"] 0] text]]
set BOTTOM_DIE [string trim [[lindex [$die selectNodes "BOTTOM_DIE"] 0] text]]
set BOUNDARY [string trim [[lindex [$die selectNodes "BOUNDARY"] 0] text]]
set BOUNDARY_CAD_LAYER [[lindex [$die selectNodes "BOUNDARY_CAD_LAYER"] 0] text]
set llx [lindex $BOUNDARY 0]
set lly [lindex $BOUNDARY 1]
set llx2 [lindex $BOUNDARY 2]
set lly2 [lindex $BOUNDARY 3]
set sizeX [expr $llx2 - $llx]
set sizeY [expr $lly2 - $lly]
if { [string equal $NAME $dsn] } {
puts $oF "<Chip name=\"$NAME\" orientation=\"R0\" faceUp=\"yes\" llx=$llx lly=$lly sizeX=$sizeX sizeY=$sizeY scaleFactor=$scaleFactor />"
} else {
puts $oF "<Chip name=\"$NAME\" orientation=\"R180\" faceUp=\"no\" llx=$llx lly=$lly sizeX=$sizeX sizeY=$sizeY scaleFactor=$scaleFactor />"
}
if { $debug } {
puts $oF "\nDIE"
puts $oF "NAME: $NAME"
puts $oF "BOTTOM_DIE: $BOTTOM_DIE"
puts $oF "BOUNDARY: $BOUNDARY"
puts $oF "BOUNDARY CAD LAYER: $BOUNDARY_CAD_LAYER"
}
array set devicetier {}
set null_tier 0
if { [string equal $BOTTOM_DIE NULL] } {
incr null_tier
append devicetier($NAME) $null_tier
} else {
foreach a_key [array name devicetier] {
if { [string equal $a_key $BOTTOM_DIE] } {
set assigned_tier $devicetier($a_key)
incr assigned_tier
append devicetier($NAME) $assigned_tier
}
}
}
foreach a_boundary_layer $BOUNDARY_CAD_LAYER {
set cmd "set init_top_cell {$NAME}"
set cmd [string map { " " " " } "add_shape -rect $BOUNDARY -layer M1 -shape BLOCKAGEWIRE -status FIXED"]
set cmd [string map { " " " " } "add_gui_shape -layer [convertLayer $a_boundary_layer] -rect {$BOUNDARY}"]
puts $oF_C_FP "$cmd"
}
if { [string equal $dsn $NAME] } {
set oF_o_pin_io [open "./scripts/o_${dsn}_pin.io" w]
set oF_o_bump_io [open "./scripts/o_${dsn}_bump.io" w]
puts $oF_o_pin_io "\nglobals\nversion = 3\nio_order = default\n\nio\npin (up"
}
# cmd - floor plan
if { [string equal $dsn $NAME] } {
set oF_fp [open "./scripts/o_${dsn}_InitFloorPlan.tcl" w]
set x2y2 [lrange $BOUNDARY end-1 end]
set cmd [string map { " " " " } "floorPlan -coreMarginsBy die -site core -s $x2y2 2.508 4.56 2.508 4.56"]
set cmd [string map { " " " " } "floorPlan -site core -d $x2y2 0.0 0.0 0.0 0.0 -noSnapToGrid"]
puts $oF_fp "$cmd"
close $oF_fp
} else {
continue
}
# Parse STZone from /PACKAGE/DIE
foreach node_STZone [$die selectNodes "STZone"] {
set BOUNDARY_STZone [string trim [[lindex [$node_STZone selectNodes "BOUNDARY"] 0] text]]
set BOUNDARY_CAD_LAYER_STZone [string trim [[lindex [$node_STZone selectNodes "BOUNDARY_CAD_LAYER"] 0] text]]
foreach a_boundary_layer $BOUNDARY_CAD_LAYER_STZone {
set cmd "add_gui_shape -layer [convertLayer $BOUNDARY_CAD_LAYER_STZone] -rect $BOUNDARY_STZone"
puts $oF_STZone "$cmd"
}
}
close $oF_STZone
# Parse (HBM)IP from /PACKAGE/DIE
foreach a_dienode {list IP ALIGN_MARK SEALRING} {
set mappedFileName [fileNameMap $a_dienode]
set a_of [open "./scripts/o_${dsn}_Create$mappedFileName.tcl" w]
foreach a_subdienode [$die selectNodes $a_dienode] {
set INST_NAME [string trim [[lindex $a_subdienode selectNodes "INST_NAME"] 0] text]
set DESIGN_MACRO [string trim [[lindex $a_subdienode selectNodes "DESIGN_MACRO"] 0] text]
set POSITION [string trim [[lindex $a_subdienode selectNodes "POSITION"] 0] text]
set ROTATION [string trim [[lindex $a_subdienode selectNodes "ROTATION"] 0] text]
if { $debug } {
puts "\n$a_dienode"
puts $a_of "\n$a_dienode"
puts $a_of "INST_NAME: $INST_NAME"
puts $a_of "DESIGN_MACRO: $DESIGN_MACRO"
puts $a_of "POSITION: $POSITION"
puts $a_of "ROTATION: $ROTATION"
}
set cmd [string map { " " " " } "addInst -cell $DESIGN_MACRO -inst $INST_NAME -loc $POSITION -ori $ROTATION -status fixed"]
puts $a_of "$cmd"
}
close $a_of
}
# Parse BUMP from /PACKAGE/DIE
foreach node [$die selectNodes "BUMP"] {
if { $debug } {
incr count
puts "\n$count"
}
set node_count [llength [$node childNodes]]
if { $node_count == 8 } {
set INST_NAME [string trim [[lindex [$node selectNodes "INST_NAME"] 0] text]]
set DESIGN_MACRO [string trim [[lindex $node selectNodes "DESIGN_MACRO"] 0] text]
set POSITION [string trim [[lindex $node selectNodes "POSITION"] 0] text]
set PORT_NAME [string trim [[lindex $node selectNodes "PORT_NAME"] 0] text]
set PORT_TEXT_LAYER [string trim [[lindex $node selectNodes "PORT_TEXT_LAYER"] 0] text]
set ROTATION [string trim [[lindex $node selectNodes "ROTATION"] 0] text]
set PIN_LAYER [string trim [[lindex $node selectNodes "PIN_LAYER"] 0] text]
set pin_layer_num [dict get $layer_map $PIN_LAYER]
set PIN_NAME [string trim [[lindex $node selectNodes "PIN_NAME"] 0] text]
if { $debug } {
puts "BUMP"
puts $oF "\nBUMP"
puts $oF "INST_NAME: $INST_NAME"
puts $oF "DESIGN_MACRO: $DESIGN_MACRO"
puts $oF "POSITION: $POSITION"
puts $oF "PIN_LAYER: $pin_layer_num"
puts $oF "PIN_NAME: $PIN_NAME"
puts $oF "PORT_NAME: $PORT_NAME"
puts $oF "PORT_TEXT_LAYER: $PORT_TEXT_LAYER"
puts $oF "ROTATION: $ROTATION"
}
set xcoord [lindex $POSITION 0]
set ycoord [lindex $POSITION 1]
set bumpW [lindex [bumpSize $DESIGN_MACRO] 0]
set bumpH [lindex [bumpSize $DESIGN_MACRO] 1]
set xloc_io [format "%.3f" [expr $xcoord + $bumpW/2]]
set yloc_io [format "%.3f" [expr $ycoord + $bumpH/2]]
set xloc1 [format "%.3f" [expr $xcoord - $bumpW/2 - $margin]]
set yloc1 [format "%.3f" [expr $ycoord - $bumpH/2 - $margin]]
set xloc2 [format "%.3f" [expr $xcoord + $bumpW/2 + $margin]]
set yloc2 [format "%.3f" [expr $ycoord + $bumpH/2 + $margin]]
set cmd [string map { " " " " } "create_bump -name_format $INST_NAME -cell $DESIGN_MACRO -allow_overlap_control keep_all -loc_type geometry_center -loc $POSITION -orientation $ROTATION deselectAll; windowSelect $xloc1 $yloc1 $xloc2 $yloc2 assignSigToBump -top_pin $PORT_NAME -selected; deselectAll"]
if { [regexp VDD $INST_NAME] || [regexp VSS $INST_NAME] } {
puts $oF_C_PGBump "$cmd"
} else {
puts $oF_C_Bump "$cmd"
}
set cmd "assignSigToBump -top_pin $PIN_NAME -selected ; deselectAll"
set cmd [string map { " " " " } "add_text -label $PORT_NAME -layer $PORT_TEXT_LAYER -pt $POSITION -height $textH"]
if { [string equal UBMB $PORT_TEXT_LAYER] } {
puts $oF_C_PGText "$cmd"
} elseif { [string equal UBM $PORT_TEXT_LAYER] } {
puts $oF_C_BText "$cmd"
} else {
puts "\nPlease check the layerName of BUMP!!"
exit
}
set xcoord [lindex $POSITION 0]
set ycoord [lindex $POSITION 1]
set cmd [string map { " " " " } "(pin name=\"$PORT_NAME\" x=$xcoord y=$ycoord layer=$pin_layer_num)"]
puts $oF_o_pin_io "$cmd"
if { [regexp "TSV" $DESIGN_MACRO] } {
set adj_xcoord [expr $xcoord - $offset_tsv_x]
set adj_ycoord [expr $ycoord - $offset_tsv_y]
} else {
set adj_xcoord [expr $xcoord - $offset_ubump_x]
set adj_ycoord [expr $ycoord - $offset_ubump_y]
}
if { [regexp -nocase {.*vdd.*} $PORT_NAME] } {
set cmd [string map { " " " " } "(bump name=\"$INST_NAME\" cell=\"$DESIGN_MACRO\" x=$adj_xcoord y=$adj_ycoord signal=\"$PORT_NAME\" type=power place_status=fixed )"]
} elseif { [regexp -nocase {.*vss.*} $PORT_NAME] || [expr { [regexp -nocase {.*gnd.*} $PORT_NAME] }] } {
set cmd [string map { " " " " } "(bump name=\"$INST_NAME\" cell=\"$DESIGN_MACRO\" x=$adj_xcoord y=$adj_ycoord signal=\"$PORT_NAME\" type=ground place_status=fixed )"]
} else {
set cmd [string map { " " " " } "(bump name=\"$INST_NAME\" cell=\"$DESIGN_MACRO\" x=$adj_xcoord y=$adj_ycoord signal=\"$PORT_NAME\" place_status=fixed )"]
}
puts $oF_o_bump_io "$cmd"
} elseif { $node_count == 4 } {
set INST_NAME [string trim [[lindex $node selectNodes "INST_NAME"] 0] text]
set DESIGN_MACRO [string trim [[lindex $node selectNodes "DESIGN_MACRO"] 0] text]
set POSITION [string trim [[lindex $node selectNodes "POSITION"] 0] text]
set ROTATION [string trim [[lindex $node selectNodes "ROTATION"] 0] text]
if { $debug } {
puts "BUMP"
puts $oF "\nBUMP"
puts $oF "INST_NAME: $INST_NAME"
puts $oF "DESIGN_MACRO: $DESIGN_MACRO"
puts $oF "POSITION: $POSITION"
puts $oF "ROTATION: $ROTATION"
}
set cmd [string map { " " " " } "create_bump -cell $DESIGN_MACRO -allow_overlap_control keep_all -loc_type geometry_center -loc $POSITION -orientation $ROTATION"]
puts $oF_C_DBump "$cmd"
set xcoord [lindex $POSITION 0]
set ycoord [lindex $POSITION 1]
if { [regexp "TSV" $DESIGN_MACRO] } {
set adj_xcoord [expr $xcoord - $offset_tsv_x]
set adj_ycoord [expr $ycoord - $offset_tsv_y]
} else {
set adj_xcoord [expr $xcoord - $offset_ubump_x]
set adj_ycoord [expr $ycoord - $offset_ubump_y]
}
set cmd [string map { " " " " } "(bump name=\"$INST_NAME\" cell=\"$DESIGN_MACRO\" x=$adj_xcoord y=$adj_ycoord place_status=fixed )"]
puts $oF_o_bump_io "$cmd"
} elseif { $node_count == 3 } {
set POSITION [lindex [$node selectNodes "POSITION"] 0] text
set PORT_NAME [lindex [$node selectNodes "PORT_NAME"] 0] text
set PORT_TEXT_LAYER [string trim [[lindex [$node selectNodes "PORT_TEXT_LAYER"] 0] text]]
set cmd [string map { " " " " } "add_text -label $PORT_NAME -layer $PORT_TEXT_LAYER -pt $POSITION -height $textH"]
puts $oF_C_HBMText "$cmd"
if { $debug } {
puts "BUMP"
puts $oF "\nBUMP"
puts $oF "POSITION: $POSITION"
puts $oF "PORT_NAME: $PORT_NAME"
puts $oF "PORT_TEXT_LAYER: $PORT_TEXT_LAYER"
}
} else {
puts "\n==please check bump-count==\n"
}
}
close $oF_C_HBMText
if { [string equal $dsn $NAME] } {
puts $oF_o_pin_io ")\n"
puts $oF_o_bump_io ")\n"
close $oF_o_pin_io
close $oF_o_bump_io
exec cat ./scripts/o_${dsn}_pin.io ./scripts/o_${dsn}_bump.io > ./scripts/o_${dsn}.io
exec rm ./scripts/o_${dsn}_pin.io ./scripts/o_${dsn}_bump.io
}
}
close $tmp_oF
close $oF_C_FP
close $oF_C_Bump
close $oF_C_DBump
close $oF_C_PGBump
close $oF_C_BText
close $oF_C_PGText
# to generate stacking configuration xml
if { $debug } {
puts "\n"
parray devicetier
}
set sorted_keys_array [lsort -integer -stride 2 -index 1 [array get devicetier]]
set tmp_oF [open $tmp_4xml r]
set file_data [read $tmp_oF]
close $tmp_oF
file delete $tmp_4xml
set line_data [split $file_data "\n"]
puts $oF_xml "<StackChip>"
puts $oF_xml " <TopDesign name=\"$dsn\"/>"
puts $oF_xml " <Package offsetX=0.0 offsetY=0.0 orientation=\"R0\"/>"
set tiernum 0
foreach {device tier} $sorted_keys_array {
foreach a_line $line_data {
if { [regexp $device $a_line] } {
if { $tier == $tiernum } {
} elseif { $tier > $tiernum && $tiernum == 0 } {
incr tiernum
puts $oF_xml " <Tier number=$tier>"
} elseif { $tier > $tiernum } {
incr tiernum
puts $oF_xml " <Tier number=$tier>"
}
puts $oF_xml " $a_line"
} else {
puts "Something wrong, please check devicetier!!"
}
}
}
puts $oF_xml " </Tier>"
puts $oF_xml "</StackChip>"
close $oF_xml
if { $debug } {
close $oF
}
puts "\n<<<Please check ./scripts/o_${dsn}.io to see if any problems:>>>"
五、常见问题与调试建议
-
XML 解析失败 :检查 3DXF 文件路径是否正确、格式是否为标准 XML,确保
/PACKAGE/DIE等节点存在 -
Bump 位置错误 :核对
offset_tsv_x/offset_ubump_x和bumpCoorMap中的尺寸是否与工艺文件一致 -
层映射不匹配 :检查
layerMap中的层号是否与项目的层定义一致,避免convertLayer返回空值 -
堆叠层级错误 :确保
BOTTOM_DIE字段正确,脚本通过该字段生成 Die 的堆叠关系,错误会导致 OrbitIO 配置异常