资讯动态

GDS版图合并/组装:calibredrv Tcl脚本与冲突自检

发布时间:2026/10/29 15:31:42 来源:尧图企业网站定制
1. 先把需求分清楚你要的是 merge 还是 assemble1.1 merge 与 assemble 的根本区别做版图的人一提“合 GDS”十有八九没说清楚自己到底要哪一种。这两种需求的差别很大选择错工具往往白忙半天。merge 是“合并库”。你手上有 A.gds、B.gds、C.gds 三个文件每个文件里有一套 cell彼此没有引用关系。merge 做的事是把这三套 cell 塞进同一个 layout 数据库里cell 的名字、层次、几何全部原样保留只是现在它们属于同一个文件了。合并完之后输出文件的 top cell 是哪一个是需要你自己指定的——因为这些 block 之间本来就没有父子关系工具不会凭空帮你选一个顶。assemble 是“组装顶层”。你手上有若干 block每个 block 都已经是一块完整、可以被引用的硬核或者子模块你要做的是新建一个顶 cell把这些 block 当成 instance 摆进去按坐标对好位置再连上电源环、pad ring、seal ring 这些东西。assemble 之后因为顶 cell 存在实例引用关系所以整个文件是连通的层次结构。区别在于merge 之后你得到的是“一堆并列的 cell”assemble 之后你得到的是“一棵有根的树”。很多人跑完 merge 发现输出文件的 top 不对、或者打开后一片空白原因就是工具默认选了一个空 cell 当 top而真正的顶层数据还在库里于是看上去“合成了个寂寞”。提示如果你的目标文件要送去做全芯片 DRC/LVS那你需要的通常是 assemble不是 merge。只有“把散落的库合成一个库再由别的流程去引用”才用 merge。1.2 三个典型场景与对应做法我把这几年遇到的合图需求归成三类每类的做法不一样。第一类是标准单元、IO、memory 的库合并。比如标准单元库一个文件、SRAM 一个文件、PAD 一个文件你做全芯片验证的时候工具只吃一个 LAYOUT PATH那就得先合起来。这类需求是纯 merge各库互不引用合完设不设 top 都无所谓因为你后续是由顶层去引用它们的。做法最简单一次性脚本跑完就行。第二类是多来源 IP 拼顶层。A 团队给一个模拟模块B 团队给一个数字模块你负责拼顶。这类既要做 merge把两边的库都装进来又要做 assemble建顶、摆位置。很多人只做了前半段然后拿一个“空顶”去跑验证跑了半天发现面积是 0。第三类是数据分片交付。交付方把一个版图按区域切成好几个 GDS 分开发你收到之后要还原成一个整体。这类是纯 merge但有个硬性约束——各分片之间的坐标必须是绝对坐标不能各自把左下角拉到原点。如果对方切图时重新归了零你合完就得手工平移回去这是最容易出问题的地方。1.3 合图前必须确认的四件事在敲命令之前我一般会先花五分钟把这四件事确认掉能省掉后面两小时的排查。确认项为什么重要怎么快速确认每个文件的 top cell 叫什么merge 后要指定 top名字错了就白合打开文件看层次树或用脚本列出 cell 列表dbu数据库单位是否一致不一致会引入取整误差甚至几何变形看文件头 UNITS或在工具里看 layout 属性层号/层名体系是否同源不同来源的层号含义可能完全不同抽查两三个关键层看图形对不对得上是否有同名但内容不同的 cell会静默覆盖最难查合并前后比对 cell 数量这四条里第四条是最阴的。工具不会因为“两个 cell 同名但内容不一样”就报错它只会按某种规则留下一个另一个直接被丢掉。你后面跑 DRC 全 clean、LVS 也过结果流片回来发现某个 cell 的某层少了——因为合并的时候被另一个同名 cell 覆盖了。这种问题在版图上几乎不可能靠肉眼发现。2. 方案选型为什么我把 calibredrv 当主力2.1 三种可选路径对比能合 GDS 的路子不止一条我前后试过三种各自的适用面差别挺大。方案上手成本批处理能力层次处理适用场景calibredrv Tcl 脚本中需要写几行 Tcl强可循环、可判断、可记日志完整保留工程化、常态化、文件多calibredrv 图形界面低点几下就行弱一次一个完整保留临时验证、排查问题自写解析脚本高要懂 GDS 二进制结构看自己写得多好很容易出问题不建议除非有特殊定制第三种我特别说一下。GDS 是二进制流格式结构不算复杂record 加数据类型理论上自己写个解析器把两个文件拼起来并不难。但“能拼”和“拼对”是两回事cell 引用索引要重建、层号要查表、属性记录要处理、path 和 boundary 的边界情况要处理、SREF 和 AREF 的重复引用要展开或保留……任何一处细节没对上出来的文件在别的工具里就可能打不开或者打开了但几何偏了一个 dbu。这种“看起来对、实际上差一点”的结果比直接报错危险得多。2.2 Tcl 批处理为什么更适合工程化选 calibredrv 的 Tcl 接口最大的理由不是它“能合”而是它“能重复地、可验证地合”。假设你手上有 40 个文件要合。手工点 40 次第一次做完了下周数据更新了你还得再点 40 次而且中间任何一次点错漏一个、顺序错一个都不会有人发现。换成脚本一个文件列表加一个循环跑完出一份日志日志里每个文件的处理结果、每个文件的 cell 数量、最终输出的 cell 总数都记着。下次数据更新重跑一遍对比两份日志差异一目了然。这就是工程化和手工操作的本质区别不是能不能做出来而是能不能证明做对了。2.3 GUI 也不是没用它的两个不可替代场景我并不是完全不用图形界面有两个场景我一定是开着界面做的。第一个是排查可疑数据。脚本跑完发现 cell 数量不对你需要知道是哪个 cell 被覆盖了。这时候打开界面看层次树两个版本的 cell 摆在一起比 bbox、比层数比在命令行里 println 快得多。第二个是确认视觉正确性。数字上的检查bbox、cell 数、层统计只能证明“结构对”不能证明“长得对”。尤其是 assemble 场景摆完位置一定要打开看一眼看各 block 的相对位置对不对、有没有镜像翻转、有没有整体偏移一个 die 的距离。这种东西数字检查很难覆盖但眼睛扫一遍三秒钟就能发现。3. 最小可用一个能跑起来的合图脚本3.1 脚本本体与逐行解释下面是我最常用的最小版本够用也好改。保存成merge_gds.tcl用calibredrv -a merge_gds.tcl跑。# merge_gds.tcl # 用法: calibredrv -a merge_gds.tcl # 建一个空的 layout 容器 set L [layout create] # 第一个文件用 read 装进去 layout read $L ./in/block_a.gds # 后续文件用 merge 追加 layout merge $L ./in/block_b.gds layout merge $L ./ip/macro_c.gds layout merge $L ./lib/pad_ring.gds # 指定最终输出的顶层 cell layout topcell $L top_chip # 写出去 layout write $L ./out/top_chip_merged.gds # 释放避免下一个任务内存叠加 layout delete $L几处值得说明的地方。layout create建的是一个空的数据库容器此时里面没有任何 cell。很多新手以为它建了一个“新 cell”其实不是它建的是“库”。真正的 cell 是后面 read/merge 进来的时候才产生的。第一个文件用layout read而不是layout merge是因为容器是空的需要一个“初始化”动作。如果你对空容器直接 merge某些版本会报错或者行为不一致。实测下来先 read 一个再 merge 其余是最稳的写法。layout topcell这一步千万别省。不指定的话工具会自己挑一个当顶层挑的规则在不同版本、不同数据下不一定一致。我遇到过最典型的情况merge 进来一个只有几个 text 的空 cell工具把它当成了 top输出文件的顶层就是那个空 cell真正的内容全在它下面的兄弟节点里看起来就是“合完什么都没有”。layout delete在单次任务里看着多余但如果你一个脚本要处理多批数据不释放的话内存会一直往上堆跑到第三批就被系统杀掉。3.2 参数细节text、property、case、units上面那个脚本是“默认参数”版本默认参数在简单数据上够用但稍微复杂一点的数据就会丢东西。下面这几个是我每次都要显式确认的。text文本标签。版图里的 text 是 LVS 做 net 命名的依据之一尤其是模拟模块内部那些手工打的 label。有些工具的默认读取行为不带 text或者只保留部分 text。写法上一般是在读取的时候挂一个保留标志例如-preserveText这类选项。注意不同版本的选项名可能不一样动手前建议在界面里的 Tcl 命令行敲一下帮助或者用一个带 text 的小测试文件先验证一遍——这个动作只要三十秒但能省掉后面“LVS 报了几百个未命名网络”的一天。property属性。GDS 里的 PROPATTR/PROPVALUE 记录经常被忽略但有些 IP 的属性里塞了真实信息比如器件的匹配要求、dummy 填充的标记、某些需要特殊处理的层次。属性丢了不会立刻报错往往要到后端某一步才出问题。处理方法同上读取时显式要求保留属性。case大小写。GDS 的 cell 名严格说是区分大小写的。但实际交付的数据里INV_X1和inv_x1同时存在的情况并不少见通常是不同团队命名习惯不同。如果不做统一你会在合并后得到两个内容一致、名字只差大小写的 cell白白多一份面积更糟的情况是某些下游工具按不区分大小写处理直接给你合并成一个那就变成静默覆盖了。稳妥的做法是合并前统一转成同一套命名规范别指望工具帮你兜底。units单位。这个下一节单独讲因为它牵扯的不是“丢不丢”而是“几何对不对”。3.3 批量合图的工程化写法真实场景往往是几十个文件而且数据会反复更新。这时候我会把文件清单外置脚本只负责读清单、跑循环、记日志。# merge_batch.tcl set L [layout create] set first 1 set fh [open ./filelist.txt r] set lines [split [read $fh] \n] close $fh set total 0 foreach line $lines { set f [string trim $line] # 支持 # 注释和空行 if {$f eq || [string index $f 0] eq #} { continue } if {![file exists $f]} { puts ERROR: file not found - $f continue } if {$first} { layout read $L $f set first 0 } else { layout merge $L $f } incr total # 每合一个就报一次当前 cell 数用于对比 puts AFTER $f : [llength [layout cells $L]] cells } puts TOTAL FILES MERGED: $total layout topcell $L top_chip layout write $L ./out/top_chip_merged.gds puts DONE, final cells: [llength [layout cells $L]] layout delete $L这段代码的价值全在日志上。AFTER xxx : N cells这行让你能一个个文件看清“合进来之后 cell 数涨了多少”。如果某个文件预期带 120 个 cell但合完只涨了 80说明有 40 个被同名覆盖了。这个信号非常重要比跑完之后发现 DRC 报错的代价小得多。filelist.txt就像下面这样# 基础库 ./in/std_cell.gds ./in/sram_macro.gds ./in/io_pad.gds # 自研模块 ./in/block_a.gds ./in/block_b.gds把注释和分组写进清单下次别人接手或者半年后你自己回头看都知道当时合了什么为什么这个顺序。注意如果一个文件里有多个顶层 cell比如一个库里既有标准单元又有 corner cellmerge 进来没问题但你要清楚layout topcell设的是最终那个“壳”不是库里的任何一个。4. 同名 cell 冲突最容易静默出错的地方4.1 冲突是怎么产生的同名 cell 冲突的成因说穿了就那么几种但每一种都很难在事后发现。最常见的是同一套库的不同版本混用。数字团队上个月给了一版标准单元库这个月修了两个 cell 更新了一版你手上同时有新旧两个文件合的时候忘了剔掉旧的。结果大部分 cell 一样个别 cell 内容不同哪一版生效取决于合并顺序和工具规则——这种“取决于顺序”的结果是最不应该出现在交付数据里的。第二种是不同来源用了同一套命名规范。比如模拟模块里有个 cell 叫inv数字模块里也有个 cell 叫inv两个团队各写各的名字撞了但内容完全无关。这种在合并之后的表现非常戏剧性某个模块的大部分图形突然“变成”了另一个模块的样子或者某个模块干脆空了一块。第三种是填充、dummy、decap 这类自动化生成的 cell。它们的命名往往是批量生成的FILL_1、FILL_2……不同工具生成的填充 cell 高度可能差几个纳米名字却完全一样。合图时被覆盖表现出来就是某片区域的填充高度不对密度检查过不了。4.2 三种处置策略与选择依据遇到冲突处理方式不外乎三种选哪种取决于冲突的规模和你对数据的掌控程度。策略一统一重命名加前缀。在合并之前先把每个来源的 cell 加一个来源前缀inv变成ANA_inv、DIG_inv。这样彻底不会有冲突。代价是下游的 LVS 规则、netlist 里的 cell 名可能全都要跟着改工作量取决于你的流程自动化程度。如果是一次性的项目这个代价可能比踩坑还大。策略二分开合并最后再拼顶。每个来源单独 merge 成一个中间文件各自跑一遍 DRC 确认没问题最后只把需要的 block 组装进顶层。这样冲突只发生在“组装”那一步而组装时你引用的是已经验证过的 block可控性高很多。我现在大部分项目走这条路。策略三接受覆盖但必须验证。如果有把握两个同名 cell 内容完全一致比如就是同一套库读了两遍那覆盖了也无所谓。但“有把握”不能靠感觉得靠比对抽查若干个同名 cell 的 bbox 和层数或者在合并前后各统计一次全库的图形总数数量一致才放过。4.3 一个可落地的冲突检测思路不用自己去解析 GDS用现成的接口做交叉比对就够了。思路是这样的合并前把每个文件单独读进一个容器记录它的 cell 名列表和各 cell 的 bbox合并后把结果再读一遍记录同样的信息。三份数据一对比被覆盖的 cell 就浮出来了。# 统计单个文件的 cell 与 bbox proc dump_cells {gdsfile outfile} { set L [layout create] layout read $L $gdsfile set fh [open $outfile w] foreach c [layout cells $L] { set h [layout cell $L $c] puts $fh $c\t[layout bbox $h] } close $fh layout delete $L }每个文件跑一遍得到一个cell名 - bbox的文本表。然后把这些表按 cell 名合并去重凡是同一个名字出现过两次以上的就是候选冲突。之后再对这些候选逐个比 bboxbbox 一致的可以先放过大概率内容相同bbox 不一致的就是必须处理的。这个方法不完美——bbox 一样不代表内容一样内部图形可能有差异。但它的成本极低能筛掉 90% 的情况剩下的靠 DRC 和 LVS 兜底。实际项目里我还没遇到过 bbox 完全一致但内容有实质差异、最后导致流片问题的情况。提示这套比对脚本建议做成常备工具。合图这个动作在项目里会重复几十次每次手工查冲突不现实做成一条命令能省很多事。5. 层号与单位跨来源数据合并的两个硬约束5.1 layer map 的写法与验证方法层号体系不一致是跨来源合图最常见的问题。同一个物理层A 家的库用 10/0B 家的库用 30/0直接合进去B 家的金属就跑到 A 家根本不存在的层号上去了。表现是文件合成了打开看B 家的模块整个“消失”了——其实是画在了你没打开的层上。解决办法是挂一个映射文件在读取的时候把层号转过来。常见写法是每行一条规则源层号、源 datatype、目标层号、目标 datatype用空白分隔# src_layer src_datatype dst_layer dst_datatype 10 0 30 0 11 0 31 0 12 0 32 0这里必须说明一点映射文件的字段顺序和格式在不同版本、不同工具形态下是有差异的有的是四列数字有的是“层号 datatype 层名 purpose”的组合写法。你拿到手的第一件事应该是做一个两三个层的极小测试文件跑一遍确认映射方向没搞反。搞反了不会报错只会把整张图翻个底朝天。验证方法很简单但很有效映射前后各统计一次层数量。假设源文件有 8 个层映射规则覆盖了全部 8 个或者是故意只映射其中几个那输出的层统计应该跟预期对得上。如果输出层的总数明显多于或少于预期说明有层没被规则命中被原样带过去了。5.2 dbu 不一致怎么办dbu 是版图数据库的精度单位。常见的有 1nm0.001um和 5nm0.005um两档老一点的工艺还有 10nm 的。两个不同精度来源的文件合并会发生什么合并工具通常会把所有几何换算到更细的那个精度上比如 5nm 数据库里的坐标 100 换算到 1nm 数据库里变成 500这个换算本身是精确的没问题。但反过来的情况就麻烦了1nm 数据库里的坐标 503要放进 5nm 数据库除不尽只能取整得到 500 或者 505。每一个坐标都可能有 1~2 个 dbu 的误差。这个误差单独看很小5nm 工艺里也就一根线宽的几十分之一。但如果你的设计里有等间距的阵列比如 memory 的位线、pad 的排布误差会累积。最典型的后果是merge 之后跑 DRC 突然多出几百个间距违例全部集中在某个模块内部间距值只差 1~3nm。我的处理原则是能不混就不混。如果必须混先把粗精度的文件升级到细精度在细精度下做完所有合并和编辑最后再整体降级。升级是精确的降级才丢精度把丢精度的动作压到最后一步至少中间所有的检查和修改都是干净的。判断当前 dbu 的方法在工具里看文件属性或者用一个只读的小脚本打印一下再或者直接看二进制文件头的 UNITS 记录。三种方法都能得到答案选最顺手的那个。6. 合完之后怎么验三层自检法6.1 结构层自检合完的文件不能直接往下走要先做结构检查。这一步只看“骨架对不对”不看内容。检查项判定标准不合格意味着top cell 名字跟你预期的完全一致topcell 设错或设漏总 cell 数各文件 cell 数之和减去已知重复有静默覆盖顶层 bbox覆盖所有 block 的 bbox 并集有数据丢失或位置偏移层总数与合并前的层并集一致层映射漏了text 数量与合并前之和接近text 没保留这五项里面最容易被跳过的是“层总数”。因为大部分人只关心图形在不在不太关注层列表。但层数量少了一两个往往意味着某个来源的某一层完全没进来那部分图形就这么没了而你的 DRC 照样 clean——因为 DRC 只检查它认识的层不认识的层不存在正好不报错。6.2 引用完整性自检第二层检查是看引用关系。版图里一个 cell 引用另一个 cell如果被引用的那个不存在有的工具会自动创建一个空的占位 cell 顶上去。结果是文件结构完全合法打开也不报错就是那个位置什么都没有。这种问题在 merge 场景下特别容易发生你合了 A 和 BB 引用了 C但 C 在第三个文件里你忘了合进来。工具不报错它默默给你造了个空 C。查的办法很直接把所有 cell 名拉一个集合遍历每个 cell 的引用列表看有没有不在集合里的。有就是缺。这个检查在脚本里几行就能写完但能挡住一类最难查的问题。# 收集所有被引用但未定义的 cell set all [layout cells $L] set defined [dict create] foreach c $all { dict set defined $c 1 } foreach c $all { set h [layout cell $L $c] foreach ref [layout refs $h] { if {![dict exists $defined $ref]} { puts MISSING REF: $c - $ref } } }不同版本的引用查询接口不一定叫这个名思路是通用的拿到“被谁引用”的名单跟“已定义”的名单求差集。差集非空就是数据不全。6.3 用一个小 DRC 检查重叠与错位第三层是物理检查也就是真的跑一次验证。最省事的做法是把合并后的文件和合并前的各个 block 分别跑同一个 DRC deck比较违例数量。如果合并后的违例数大致等于各 block 之和加上少量边界处的正常违例说明合并没有引入新问题如果合并后突然多出几百个八成是位置错位或者重叠。想更精准一点可以做按区域筛选。全芯片 DRC 跑完之后把违例按坐标范围分桶统计重点看落在 block 接缝处那些坐标上的违例。这些位置的违例才是合图引入的块内部的违例是设计本身的。这个动作看起来麻烦实际上就是把 DRC 结果数据库按坐标过滤一遍脚本化之后很快。还有一种粗但极其有效的检查算面积。把每个 block 的 bbox 面积加起来跟合并后的总 bbox 面积比。如果合并后的面积比各块之和还小说明有两个块被摆到了同一个位置重叠了。这个方法不需要任何工具支持拿几个数字一算就知道我在赶时间的时候经常先用这招扫一遍。注意面积法只能发现“整体重叠”发现不了“错位一微米”这种情况。它是初筛不是终检。7. 常见问题速查与踩坑记录7.1 速查表下面这张表是我这几年攒下来的遇到现象直接查能省掉从头想的时间。现象最可能的原因处理办法合并后打开一片空白top cell 指到了一个空 cell手动指定顶层或检查是否合进来一个只有 text 的 cellcell 数量比预期少同名覆盖用 dump_cells 比对冲突的加前缀某模块的图整个消失层号不一致画在了别的层上检查层映射统计层总数DRC 突然多出大量间距违例dbu 不一致导致的取整误差统一精度或在细精度下完成全部编辑LVS 报大量未命名网络text 未保留读取时显式保留 text重跑某区域填充高度异常填充 cell 同名覆盖比对填充 cell 的 bbox 和层数输出文件在别的工具打不开写文件时选项不兼容或数据本身有非法记录换一种格式输出或用原工具校验一遍脚本跑到一半被系统杀掉内存未释放多个容器叠加每个任务结束调用 delete或分批跑位置整体偏了一个 dieassemble 时坐标没减 bbox 左下角安置前先取 bbox用左下角做基准引用关系里有空 cell遗漏合入某个被引用的文件做引用完整性检查求差集7.2 几条只有踩过才知道的经验第一条先合小文件再合大文件。这不是必须的但在排查阶段很有用。小文件合并快出问题容易定位大文件放到最后前面结构对了再往里加出问题范围小。第二条中间结果一定要落盘。有些项目我要求每一个 block 单独 merge 出一个中间文件并保留下来。这样最后组装的顶出了问题可以直接拿中间文件去定位是哪个 block 的问题而不用重新合一遍全部数据。多占几十 G 磁盘换回来的是排查时间。第三条不要在同一步里既改层号又改单位又合文件。三个动作叠在一起出问题了你根本不知道是哪一步导致的。分开做每步验证一次虽然多敲几条命令但每次出问题只需要查一个变量。第四条别人的 GDS 到手先自己跑一遍读-写往返。把文件读进来什么都不改直接写出去然后用工具打开对比。如果往返一次之后 bbox 变了、层数变了、text 少了说明这个文件在读取环节就有损耗后面所有基于它的操作都不可信。这个动作只要几十秒我现在的习惯是收到任何外来数据的第一步就是做它。第五条日志比结果重要。合图这件事成功的输出是一个文件但真正让你敢把它交出去的是那份记录了“合了哪些、每个合完多少 cell、总共多少 cell、top 是谁”的日志。尤其在多人协作的项目里别人问你“你确定都合进去了吗”你能拿出日志比嘴上说“应该没问题”有用得多。我个人在这些年的实际使用中体会最深的一点是合图这个动作本身的技术难度很低几条命令的事难的是证明自己合对了。所有真正花时间的部分都在前面那五分钟的确认和后面那半小时的验证上。跳过这两步图照样能合出来也能过 DRC 能过 LVS然后问题会在最不该出现的时候出现。所以现在我的习惯是脚本可以五分钟写完验证环节一定要留够时间这部分投入从来不亏。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑