从ITF/IRCX自动生成EMX工艺文件的脚本思路与实操 有些人可能觉得这类话题太“后端”不如版图、仿真那么直观但真正跑过 tapeout 的人都知道PDK 里一份烂工艺文件能把人折腾到怀疑人生。尤其是 EMX 这种以电磁仿真见长的工具喂给它的工艺文件如果层级、厚度、方块电阻这些基础参数有偏差后仿结果跟你实际流片回来的测试数据能差出一大截。而这个文件恰恰又经常是从代工厂给的 ITF 或 IRCX 里转过来的手动转你真的会用记事本打开 ITF然后一行一行对着映射表去写 EMX 的 tech file 吗我试过几百层金属和过孔堆叠在一起光是理清层间关系就能耗掉一个下午更别提里面那些“同一个 layer 在两个文件里命名完全不一样”的坑。这篇文章我就用自己的实际经历说一下怎么从 ITF/IRCX 自动生成 EMX 工艺文件。不是讲现成工具下载因为我都是自己写脚本解决的Candence 系的东西写个 Skill 或者 Perl 脚本都很常见我这里主要用脚本思路来拆解。适合正在为 PDK 迁移、工艺库适配、后仿精度问题头疼的模拟/射频工程师也适合刚接触 EMX 但不想在这个环节浪费时间的新人。1. 内容整体设计与思路拆解1.1 ITF/IRCX 和 EMX 工艺文件到底是啥关系先说基础不然后面说原理你会懵。ITFInterconnect Technology File是代工厂提供的一种文本格式描述金属层、过孔层、介质层的物理参数比如层名、厚度、方块电阻、介电常数、最小间距等。IRCX 是另一种类似的东西常见于某些 PDK 里本质上是同一类信息的不同组织方式字段风格和 ITF 略有差异但核心信息一致像是给互连工艺做“体检报告”。EMX 是 Integrand 家的电磁仿真器主要做无源器件建模比如电感、变压器、传输线。它仿真时读入的工艺文件结构上非常有序一层层的导体、介质、过孔按堆叠顺序排列好每层有自己的厚度、电导率、介电常数、损耗角正切等。EMX 不吃 ITF 原始格式它有自己的 tech file 格式通常是 .emx 后缀或者通过 EMX 提供的工具转换。问题就在这代工厂给的是 ITF/IRCXEMX 要的是自有的 tech file。这两个东西不是简单改个后缀就能互相认的中间隔着一层“数据转述”的过程。1.2 为什么选择脚本而不是手动转换我知道有人会说“就几层金属手动填一下 EMX 的界面不行吗”顶层金属六层、厚铝两层、过孔十二种、钝化层若干……一旦工艺节点到了 28nm 以下互连层数轻松奔着 10 去每个金属层还分厚度、方块电阻、密度规则。你手动在 GUI 里一层一层敲敲完第一遍心里就没底第 7 层金属到底有没有敲错厚度过孔 8 到底连的是金属 7 和金属 8 还是金属 6 和金属 7更关键的是EMX 里有一个概念叫“return path”也就是回流路径。这个回流路径跟顶层金属的厚度、电导率直接相关如果你手动录入时把顶层金属填错一点电感仿真出来的 Q 值能偏 5% 以上。这种影响很隐蔽不是一眼能看出来的。所以自动化就三个好处速度快、可重复、可审计。速度快指的是几百层内容秒级生成可重复指的是你换了工艺库、改了某个参数脚本重跑一遍就完事可审计指的是 ITF 里的原始参数和 EMX 里的最终值能逐条对上评审、答疑、回归测试都有据可查。1.3 整体方案的思路框架我最终落地的方案是这样的输入代工厂提供的 ITF 或 IRCX 文件。解析用脚本提取分层信息、物理参数、导电属性、过孔连接关系。映射把代工厂层名比如 M1、M2、V1、V2……映射成 EMX tech file 需要的层编号和类型。输出直接生成 .emx 格式的工艺文件。校验再写一段反向解析代码把生成的 .emx 读回来跟原始 ITF 的关键参数做一致性对比。这套流程里最难的不是“提取”也不是“输出”而是“映射规则”。因为 ITF 里的层描述跟 EMX 的层描述不是一一对应的中间存在一个“物理含义到仿真语义”的转换。比如 ITF 里 M6 的 thickness 可能写的是物理厚度但 EMX 里某些层需要写的是“集肤深度等效厚度”这是要根据电导率和频率自己换算的。这个思路定型以后后面的事情就顺畅多了。2. 核心细节解析与实操要点2.1 逐段拆解ITF 文件的结构与解析要点你要写脚本解析 ITF第一件事不是写代码而是打开 ITF 文件把它的结构摸清楚。不同代工厂的 ITF 尽管大体相似但细节字段差异很大。通常你能看到这样的段落metal { name M1 thickness 0.18u resistivity 0.12 ohm/sq min_width 0.1u min_space 0.1u } via { name V1 thickness 0.4u resistance 5 ohm array_x 2 array_y 2 } dielectric { name D1 thickness 0.4u epsilon 4.1 tan_delta 0.02 }这只是个高度简化的示例真实 ITF 字段要更复杂命名也是五花八门有的用thick有的用thickness有的用t有的用rho表示电阻率有的用rs表示方块电阻。解析的关键点是建立一个“容错”机制不能把字段名写死。我的做法是先用正则表达式按块分割然后用一个哈希表去匹配不同代工厂可能用的同义字段。比如thick_patterns [r\bthickness\b, r\bthick\b, r\bT\b] rho_patterns [r\bresistivity\b, r\brho\b, r\bRs\b]这个过程看起来不起眼但实际上是最花时间的一步。我一开始偷懒默认所有 ITF 都用thickness字段结果换了个 PDK 就挂了。后来规规矩矩写了字段别名表稳了。2.2 EMX 工艺文件的格式与“坑”EMX 的 tech file 我拿到的版本长这样简化版substrate { eps 11.9 thickness 250u rho 10 ohm-cm } metal { name M1 type conductor thickness 0.45u conductivity 5.8e7 metal_layers M1_EMX } via { name V1 type via thickness 0.3u conductivity 5.8e7 connects M1_EMX M2_EMX }看着和 ITF 差不多但里面有三个隐蔽的坑第一个坑ITF 通常给的是方块电阻ohm/sq而 EMX 某些版本要的是体电导率S/m。这两者的换算关系是电导率 1 / (方块电阻 × 厚度)。这里厚度要用米不能用微米很多人在这儿栽跟头。第二个坑EMX 里每一层导体不仅要有物理名比如 M1还要有一个 “EMX 内部图层名”比如 M1_EMX这个内部名用于 EMX 识别上下连接关系。很多生成的脚本把物理名和内部名混用结果 EMX 直接报错“layer mismatch”。第三个坑过孔的阵列信息。ITF 里写的是array_x和array_y表示过孔阵列的个数但 EMX 需要的是过孔本身的尺寸和间距或者需要知道每个过孔的电阻。你要么把阵列信息转换成单个过孔的电阻值要么找 EMX 文档里对应的字段。如果你不处理生成的工艺文件在仿真时虽然报错不严重但仿真结果里的过孔电阻会差得很离谱。2.3 脚本的核心处理流程在我实际写的脚本里核心流程分四步。第一步读取 ITF/IRCX 文件按段落解析出所有层。这一步要注意“三个层级”金属层、过孔层、介质层以及可能的顶层钝化层。不同层有不同的关键字要根据段落内的name字段和关键字共同判断。第二步建立“物理层到 EMX 层”的映射表。这一步不是写死在代码里而是用一份配置文件YAML、JSON 都可以维护。原因很简单代工厂层名和 EMX 层名天然不一样比如 GF 的 ITF 里叫M4EMX 里叫M4_EMX但也有叫M4_C或者Metal4的。配置文件里维护一个physical_name - emx_name的键值对脚本只负责读取配置并执行。第三步参数换算。把 ITF/IRCX 里的厚度、方块电阻、介电常数等转换成 EMX 需要的电导率、等效厚度、介电常数、损耗角正切等。换算公式我在后面的实操部分会详细给出来。第四步生成 .emx 文件。生成的文件要组织成清晰的段落结构并附上注释标明每一行参数来自 ITF/IRCX 中的哪个原始字段。这一步对后续审计非常重要不然过三个月你自己都忘了那些数字是怎么来的。3. 实操过程与核心环节实现3.1 准备工作与环境搭建我用的环境是 Linux 工作站脚本用 Python 3 写没有用太重的外部依赖只用了正则表达式和标准库。这样做的原因是 PDK 文件往往在无网的服务器上你装不了pip install一大堆东西标准库的方案最保险。你需要准备的输入文件有三样ITF 文件或 IRCX 文件、EMX tech file 模板或者你从 EMX 安装目录里扒一个例子出来、代工厂的连接关系文档一般在 PDK 的 Readme 里或者你从版图工具里导出的 layer map 文件里获取。如果你手头连一个 EMX 模板都没有也别慌。EMX 集成在 Virtuoso 里的时候会在某个目录下生成一份emxTechExample找到它就能做骨架。3.2 Python 解析 ITF/IRCX 的代码骨架我直接给出一个能用的解析骨架然后解释关键点。import re import json def parse_itf(filename): layers [] with open(filename, r) as f: content f.read() # 按 metal / via / dielectric 分块 for m in re.finditer(r(metal|via|dielectric)\s*\{(.*?)\}, content, re.S): block_type m.group(1) block_body m.group(2) data parse_block(block_type, block_body) layers.append(data) return layers def parse_block(block_type, body): data {type: block_type} name_match re.search(rname\s*\s*(\S), body) data[name] name_match.group(1) if name_match else None # 提取物理参数 thick_m re.search(r(?:thickness|thick|T)\s*\s*([0-9.])\s*(u|n|m)?, body) if thick_m: val float(thick_m.group(1)) unit thick_m.group(2) or u data[thickness] convert_to_meters(val, unit) rs_m re.search(r(?:resistivity|rho|Rs)\s*\s*([0-9.]), body) if rs_m: data[sheet_res_ohm_sq] float(rs_m.group(1)) eps_m re.search(repsilon\s*\s*([0-9.]), body) if eps_m: data[epsilon] float(eps_m.group(1)) return data def convert_to_meters(val, unit): if unit u: return val * 1e-6 elif unit n: return val * 1e-9 elif unit m: return val * 1e-3 return val这个骨架解决的问题是把 ITF 里不同风格的字段名都归一到一致的数据结构里。运行完parse_itf之后你会得到一个列表里面每个元素是一个字典包含type、name、thickness、sheet_res_ohm_sq、epsilon这些键。注意一个细节解析文件时用re.S标志这样.能匹配换行符。我一开始漏了这个导致块匹配失败解析出来的层数少了一半排查了半天。3.3 参数换算与 EMX 生成逻辑这一步是整个流程的核心我把换算公式写清楚金属层电导率siemens/m 1 / (方块电阻 ohm/sq × 厚度 m)介质的介电常数直接用 ITF 里的epsilon但要注意 EMX 里有些层需要epsilon_r和tan_delta分开给如果 ITF 里面只给了一个loss_tangent那就直接映射过去。过孔电阻ITF 里如果给了resistance那很好直接填入。如果给的是array_x 2、array_y 2而单孔电阻是5 ohm那最终每根过杆的等效电阻就是 5 / (2 × 2) 1.25 ohm。这个不做后续仿真结果偏得离谱。有了这几个公式生成代码就顺理成章了def generate_emx_tech(itf_data, layer_map, output_path): with open(output_path, w) as f: f.write(// Generated from ITF via script\n) f.write(// Timestamp: 2024-05-20\n\n) for item in itf_data: if item[type] metal: thickness item[thickness] rs item[sheet_res_ohm_sq] conductivity 1.0 / (rs * thickness) if rs and thickness else 5.8e7 emx_name layer_map.get(item[name], item[name] _EMX) f.write(fmetal {{\n) f.write(f name {emx_name}\n) f.write(f thickness {thickness * 1e6:.4f}u\n) f.write(f conductivity {conductivity:.4e}\n) f.write(}\n\n) elif item[type] via: # 处理过孔 pass elif item[type] dielectric: # 处理介质 pass实际跑的时候你会遇到一些“非标准”的 ITF。比如某家代工厂的 IRCX 里金属层的方块电阻给的不是ohm/sq而是ohm/mil我第二次做自动化转换时就踩了这个坑。这种单位换算如果不注意生成的 EMX 文件里的电导率会差一万倍因为在 EMX 看来金属层变成了绝缘体。所以我会建议在脚本里加入单位检查如果发现resistivity的值大于 1多半是ohm/mil就要做单位换算。3.4 加入层间连接关系的自动推导ITF/IRCX 里通常会直接给出每层过孔的两端金属层名比如 V1 连接 M1 和 M2。但是有些工艺库的 ITF 里只写了过孔的厚度没有写连接关系。这时候就要靠“堆叠顺序”来推导。思路把 ITF 里的层按厚度排列逻辑排序介质层夹在金属层之间过孔层联通上下金属如果 Vx 的厚度等于 Dx第 x 层介质的厚度那么 Vx 大概率连接 Mx 和 M(x1)。这个规则不是 100% 可靠但 90% 的情况下能猜对剩下 10% 靠连接关系文档来校验。我建议你在脚本里输出一份“层间连接关系表”到日志文件里人眼过一遍比直接信脚本要靠谱。我就在项目里干过“太自信、没仔细检查连接表”的事结果把 V12 和 V13 搞混了后仿电感值差了几 nH排查了半天最后发现是连接关系搞错了。3.5 生成结果验证生成的 .emx 文件怎么验证对不对我一般做三道检查。第一道EMX 自带的 parser 跑一遍看能否通过基本语法检查。这一步能抓出括号不匹配、字段缺失这类低级错误。第二道反向提取。写一个小的读取脚本把生成的 .emx 文件重新解析成数据字典然后跟原始 ITF 解析出的数据字典做 diff。厚度差超过 1%电导率差超过 5%就要报警。这相当于你的转换过程是“可逆”的。第三道跑一个简单的仿真测试。用一个已知的 inductor 版图分别用官方 PDK 转换好的工艺文件和你的脚本生成的工艺文件仿真对比感值、Q 值、自谐振频率。如果在 2% 以内基本可以说转换成功了。如果差得多别急着怀疑 EMX大概率是你的电导率或者过孔电阻算错了。4. 常见问题与排查技巧实录4.1 解析后厚度单位错了我遇到过一次ITF 里厚度写了0.45u但我的脚本默认单位是米转换的时候把 0.45 当成了 0.45 米乘以 1e-6结果 EMX 里厚度变成了 450 微米离谱到没法看。排查方法很简单在生成 .emx 之后先打印出每一层的厚度跟 ITF 原文件逐项对比。不要用眼睛扫写一个自动对比的代码任何一层的差值超过阈值就直接报错。4.2 方块电阻和电导率换算错了这是最容易犯的错误。EMX 需要电导率单位是 S/m而 ITF 里给的是方块电阻单位是 ohm/sq。你必须用厚度去换算。但这里还有一层坑厚度是有单位的换算的时候要特别注意。如果你在 ITF 解析时把厚度转成了微米1e-6 的因子就很容易丢。我最后的解决方案是在所有换算之前统一转成“米”然后在输出 .emx 的时候再转回“微米”。中间不做任何其他单位运算减少出错可能。4.3 过孔数太多导致 EMX 仿真特别慢有的工艺库里有十几层过孔每一层过孔又分了很多种比如 V1、V1a、V1b直接全部转成 EMX 里的过孔层会让仿真网格变得极其密集速度慢到怀疑人生。我的处理办法是转换时增加一个“简化开关”。对于非关键路径上的过孔层可以合并成一个等效过孔或者把某些过孔阵列替换成一个大的导电块前提是电磁特性等效。这个开关在高频段可能不适用但对于低频段的快速验证效果极佳。4.4 EMX 报 layer mismatch 错误这个报错我见过太多次了几乎都是因为过孔连接关系写错了或者某个金属层在 EMX 里的内部名没有被正确映射。我的排查套路是先在 EMX 的原始例子里查一下它使用的内部层名比如 M1_EMX再看我的脚本生成的层名是否完全一致。只要把映射表里的名字对齐问题马上消失。4.5 IRCX 文件和 ITF 文件格式差异大吗差异是有的主要体现在块结构上。IRCX 里往往多了interconnect段落而且某些参数名用缩写比如MTT表示金属厚度RSH表示薄层电阻。我的应对策略是在预处理阶段把 IRCX 先标准化成 ITF 的结构然后再走同一套解析流程。这样只写一个转换器而不是两套解析器。这里给你一个参考表格快速对比两个文件格式里的常见字段映射ITF 常见字段IRCX 常见字段含义thicknessMTT厚度resistivityRSH方块电阻epsilonDK介电常数loss_tangentDF损耗角正切array_xNX过孔阵列横向个数array_yNY过孔阵列纵向个数nameLayerName层名表格里的映射不是绝对固定的但你可以按这个思路去适配。做自动化转换时宁可多做几层防御也别指望代工厂能给你一份完全标准的文件。5. 工具选型与脚本语言的选择思路5.1 Python 还是 Perl哪个更合适很多老工程师写这类工具会用 Perl因为历史原因PDK 社区里 Perl 脚本确实多。但我自己选 Python原因有三。第一Python 的字典、列表、正则表达式用起来非常直观尤其是处理嵌套的块结构时写代码和维护都省心。第二Python 的调试工具更好用pdb、ipdb 能让你一行行看解析过程Perl 就相对粗糙。第三后续如果你想把这个脚本扩展成带界面的小工具Python 可以直接挂个 tkinter 或 PyQtPerl 就麻烦得多。不过 Perl 也不是没优点。Perl 的正则是图灵完备的处理非常“脏”的古老 PDK 文件反而顺手。我见过某些老代工厂的 IRCX 里夹杂着大量历史注释Python 正则写出开可能要几十行Perl 可能几行就搞定了。5.2 有没有更省事的现成方案有但我不推荐一上来就找现成工具。EMX 官方其实提供了一些转换脚本通常是 Python 或 Skill但它们的匹配逻辑是基于特定代工厂的 PDK 写的换个工艺库就得改。更坑的是官方脚本有时是为了某个特定项目的流片任务定制的未必能覆盖你手头的所有层。我的观点是第一版一定要自己写哪怕只是几十行的“小破脚本”。这个过程能让你彻底搞懂 ITF/IRCX 里每一层参数的物理含义和 EMX 工具内部的层组织方式。以后遇到问题你会比任何现成工具都更从容。当然如果你时间特别紧也可以先用官方转换工具产一份结果然后写脚本对比你的自动化输出两边互相验证。我项目里跑过一个 28nm 工艺库手动转换 官方工具花了两天我的脚本自动对比只花了一个下午加上调试脚本的时间一共一天这个 ROI 还是很值得的。5.3 配置文件设计让脚本适应多种工艺节点我在第三节提到过映射表配置这里展开讲一下怎么设计。我用的 YAML 配置大概是这个风格layers: M1: emx_name: M1_EMX type: metal V1: emx_name: V1_EMX type: via connects: [M1_EMX, M2_EMX] D1: emx_name: D1_EMX type: dielectric connects: [M1_EMX, M2_EMX]脚本读取这份配置后解析 ITF 得到的层数据会和配置里的emx_name做关联。这样你换工艺库时只需要改配置文件不需要改 Python 代码。这个设计让我在多个项目里复用同一套脚本。只要 ITF 文档结构没有大改配置文件一换十分钟就能出新的 EMX 工艺文件。6. 实操心得与扩展思考最后再分享几个实际干活时才体会到的点。第一不要只想着“转换成功”要想着“可逆审计”。我每次生成 .emx 文件后都会保留一份 ITF 原始参数和 EMX 生成参数的对照表输出成 CSV 或者 Markdown跟着项目文档走。这样做的好处是可以随时回溯一个异常仿真结果对应的工艺文件是哪一行参数有问题。没有这个审计文件你面对一个神秘的后仿偏差时会无从下手。第二同一套脚本可以扩展到其他工具。我讲的是从 ITF/IRCX 转 EMX但实际这套“配置文件解析器生成器”的三段式架构完全可以套用到转 HFSS、转 ADS、转 Sonnet 等工具的工艺文件上。你只需要改改 EMX 生成器那块代码换成对应的输出模板剩下的解析和映射逻辑几乎不用动。第三PDK 文件里的版本信息一定要看。有一段时间我发现自己更新工艺库后脚本突然多出了好几个解析失败排查半天才发现代工厂的新版本 ITF 里把字段名从array_x改成了num_x。所以提醒一下每次拿到新 PDK第一步不是跑脚本而是 diff 一下 ITF 文件跟上一版有什么区别。哪怕你已经写好了容错机制提前知道变化总比事后排查强。第四也是最重要的生成工艺文件后的“物理合理性”检查绝不能被省掉。脚本帮你把数字填进去了但数字合不合理要靠工程师的判断。比如介质层的介电常数一般是 3.9 到 4.5 之间如果脚本输出的数值是 1.5那显然不对要么是解析错误要么是原文件用了其他单位可能是epsilon_r而不是epsilon。这种异常值如果是人工录入大概率会发现但脚本一旦跑起来人往往就松懈了。我最后在脚本里加了“范围检查”逻辑超出合理范围的参数直接高亮警告这救过我好几回。写这份自动化脚本的时间成本不算低但它带来的收益是持续的。每次拿到新工艺库、新 PDK 版本我都能在一天之内跑出一份可靠的 EMX 工艺文件。再过几个月如果机台、工艺线宽或温度系数发生变化我也能快速重新生成并对比差异。这个过程让我彻底摆脱了手动逐行填写工艺文件的噩梦也让我对每个参数的来源、转换、影响都有了更清晰的认识。建议你也动手写一个不用多漂亮能跑通就行。