
先问一句多少人是在 ECO 阶段被后端工具悄悄改了 netlist然后在功耗报告里看到 standby leakage 高了一截才开始认真研究“iso 的 input 端为什么不能插 buffer”这个问题的我就是在 14nm 项目里踩过这个坑的人。表面上看这是一条再普通不过的物理设计规则但背后的因果链非常清晰隔离单元的 input 端一旦被插入 buffer电源关断时就会形成一条不受控的漏电路径而这条路径之所以会出现往往只是因为一条 dont_touch 没有设对。这里“iso”不是说 ISO 镜像文件而是 isolation cell隔离单元。在低功耗数字后端里它是电源关断场景下保护逻辑状态的关键结构。今天我就围绕“iso 的 input 端为什么不能插 buffer”这个话题把隔离单元的工作原理、dont_touch 的真实作用、漏电从哪来以及如何在 netlist 里排查这类问题完整过一遍。1. 先从隔离单元说起iso 在低功耗设计里的真实位置1.1 电源关断的基本诉求要做低功耗设计最直接的手段之一就是电源关断power gating。某个功能模块在待机状态下不需要工作就把它的电源断掉静态功耗自然归零。这个思路听上去简单落地时却有一个绕不开的问题模块断电后输出信号不再是稳定的 0 或 1而是悬空或者保持在上一个状态的残余电平业内一般叫 X 态。如果这个 X 态直接送到其他仍然带电的模块后果很严重。常开域里的组合逻辑可能因此出现不定态甚至形成从电源到地的直流通路让整个芯片的待机功耗反而变高。更麻烦的是状态机如果收到一个不确定的电平恢复供电后可能进入非法状态芯片死机是小事烧掉外部接口都有可能。所以必须在电源域边界放一个“守门员”在关断期间把输出钳到一个明确电平这个守门员就是 isolation cell。isolation cell 通常放在常开域always-on domain一侧接收来自可关断域的信号并根据控制信号决定是正常透传还是输出钳位值。它有三种常见类型钳到 0 的 isolation AND 型、钳到 1 的 isolation OR 型以及带锁存功能的 isolation latch 型。无论哪种类型工作逻辑都是类似的。1.2 隔离单元的两个端口地位完全不一样很多人第一次接触 isolation cell 时容易把它当成一个普通逻辑门输入端进来信号输出端出去信号无非多了一根 enable。但在后端物理设计眼里isolation cell 的两个端口地位完全不同。output 端连接的是常开域内部的负载这个端口只要驱动强度够插不插 buffer 影响不大。因为隔离单元输出的是经过钳位处理的稳定电平输出端即使再挂多级 buffer信号也不会因为电源关断而变成未知态。input 端就非常敏感了它直接面对的是来自可关断域的原始信号也可能面对的是关断域的 buffer 输出。电源一旦断开这个输入 net 的真实状态就脱离了逻辑控制任何插在它上面的常开域器件都会遭殃。所以在数字后端的实现流程里isolation cell 的 input 端几乎被默认为“禁区”。工具可以随意在 output 端优化负载但不允许在 input 端为了修 transition 或 max cap 去插 buffer。这背后不是风格偏好而是物理层面的约束。1.3 为什么偏偏是 input 端不能插 buffer用一句话概括核心原因isolation cell 的输入侧是电源域交界的敏感区域插入 buffer 等于在交界处埋了一个不受控的电流消耗点。展开说有三个层次。第一如果 buffer 被插在关断域那么电源关断后 buffer 本身没有供电它的输出自然悬空最终送到 isolation cell 输入的仍然是一个未知态。也就是说buffer 白插了而且它自己的漏电在关断期间反而成了额外负担。第二如果 buffer 被插在常开域那问题更明显buffer 的输入来自关断域电源关断后输入悬空buffer 内部反相器的 PMOS 和 NMOS 会同时导通形成短路电流。一个 buffer 可能只有几十微安但一条跨域边界往往有很多条路径叠加起来就是几毫安的额外漏电。第三buffer 会引入额外的延迟改变隔离信号和 enable 信号的相对时序关系可能导致隔离时机不对出现毛刺。这些都不是理论上的担忧我在后文会详细演示漏电路径的产生过程。现在先回答另一个关键问题为什么一条 dont_touch 能背这么大的锅。2. dont_touch 到底在保护什么一条属性的背后逻辑2.1 dont_touch 的常见误读数字后端里提到“dont_touch”很多人的第一反应是“防止工具改 cell 名字”或者“防止综合优化删掉这个模块”。其实 dont_touch 是一个覆盖面很广的约束属性既可以加在 cell 上也可以加在 net、pin、port 上而且作用强度和影响范围完全不一样。加在 cell 上保护的是这个 cell 不被删除、不被优化合并也不被替换成等效单元。加在 net 上保护的是这根线不被断开重连也不允许工具在中间插入 buffer。加在 pin 上保护的是这个端口对外连接的逻辑不变尤其是不允许为了满足 DRC 或时序而增加电路。关于“iso 的 input 端为什么不能插 buffer”这个具体场景真正起作用的是 pin 和 net 上的 dont_touch而不是 cell 上的 dont_touch。很多项目只在 UPF 里定义了 isolation cell然后习惯性地对 isolation cell 实例设了 dont_touch却忘了对 input pin 做同样处理。工具在优化阶段一看这个 cell 不能动但它的输入 net 没有任何保护为了修 transition就在 input 端一路插了两级 buffer。这就是我前面说的“dont_touch 没设对”的典型情况。2.2 在 input 端加 dont_touch 的真正目的如果你去问一个资深后端工程师为什么要在 isolation cell 的 input 端加 dont_touch他大概率会告诉你为了保证电源域边界结构不被工具改写。这确实是最直接的目的但背后还有一层逻辑值得展开。isolation cell 的 input 端连接的是一个跨电源域的信号它在 UPF 里的供电状态定义很特殊。工具在做时序优化时并不天然理解“这根 net 在某个时刻会悬空”这件事它只看到 transition 不满足、cap 负载太大于是按常规逻辑插入 buffer。一旦插了 buffer这根 net 就不再是简单的跨域信号而是被一个常开域器件夹在中间的结构。综合工具可能认为这是合理的驱动修复但从低功耗设计的角度看它破坏了隔离语义。所以对 input pin 设置 dont_touch 的真正目的是把“这块区域你不要碰”这个意图显式告诉工具。这是一种通过约束传递物理设计意图的方式。在命令层面通常这样写set_dont_touch [get_pins u_iso/I] true set_dont_touch [get_nets u_iso_input_net] true前者保护 pin后者保护 net。两条都设才比较稳妥因为不同工具对 pin 和 net 的敏感度不一样。我习惯在综合脚本里写一个专门的 isolation cell 约束文件把所有 iso 实例的 input pin 循环设一遍并检查是否有遗漏。2.3 如果没设 dont_touch工具会干什么没有 dont_touch 保护时逻辑综合阶段可能还看不出问题因为综合工具对 power domain 的抽象程度比较高而且 UPF 信息在综合网表里不一定完整保留。问题往往出现在物理实现阶段也就是 place 和 optDesign 环节。举一个我经历过的场景关断域里某个 flop 距离 isolation cell 有几百微米中间还经过一段高层金属线的负载比较大。工具在修 max transition 时发现这根 net 的 transition 超标按照默认规则最优解就是在中间插两个 buffer。而且工具很“机智”为了不让 buffer 占用关断域的面积把 buffer 放到了常开域一侧这正好踩中雷区。没有 dont_touch 时工具维护成本最低的做法就是插 buffer。它不会主动判断“这是 isolation cell 输入侧插 buffer 会引入漏电风险”。即使 UPF 里写了 boundary 信息工具对“关断后信号悬空”这件事的抽象也很难覆盖到每个 buffer 的电气行为。结果就是ECO 后的网表看起来功能逻辑没变但功耗和可靠性都出了问题。这个我在下一个部分详细拆解。3. 漏电到底从哪来三种典型的漏电路径3.1 插在关断域的 buffer白插了还添乱先看第一种情况buffer 被工具放在关断域内。这种情况相对少见因为后端工具通常会优先把 buffer 放在 net 的另一端附近也就是 closer to 常开域。但如果布局空间紧张或者工具对 power domain 边界识别有误buffer 确实可能落在关断域。问题在于关断域断电后这个 buffer 的电源和地都是断的输出 pin 就是一个高阻态。高阻态连接到 isolation cell 的 input 端结果和没接 buffer 没有任何区别isolation cell 看到的依然是未知电平。也就是说工具花了面积和功耗插入的 buffer在关断状态下完全不发挥作用反而因为 buffer 本身的源极、漏极和阱之间的反偏漏电额外贡献了一部分静态功耗。这还不是最糟糕的更糟糕的是恢复供电的瞬间。模块重新上电时buffer 的输入信号可能还处于未定义状态buffer 输出会先出一个中间电平然后被后续逻辑慢慢拉起来。这个过程如果恰好和 isolation enable 释放的时序重叠就可能产生毛刺导致常开域逻辑采样到错误值。3.2 插在常开域的 buffer半导通电流的根源再来看最典型的情况也就是我在 14nm 项目里真正踩到的那一种buffer 被放在常开域输入 net 从关断域过来。这种结构表面上看起来没什么问题buffer 有电输出至少能给出一个电平。但在电源关断后问题就藏在 buffer 的输入悬空上。一根金属线在电源关断后不会自动变成 0 或 1它会停留在断电瞬间的残余电压附近然后随着漏电缓慢漂移。这个电压很可能落在逻辑阈值附近比如 core 电压 0.8V 的时候漂移到 0.35V 到 0.45V 之间。buffer 输入在这个范围内时内部第一级反相器的 PMOS 和 NMOS 同时导通形成一条从 VDD 经过 PMOS、NMOS 到 GND 的直流通路。这种电流叫短路电流也叫穿通电流crowbar current大小取决于工艺和输入电压位置。我实测过同一颗芯片上标准阈值的 CLKBUFX12 在输入 0.4V 时短路电流能到 200uA 左右低阈值工艺的 buffer 更夸张超过 500uA 也不奇怪。如果一条跨域边界上有 20 条信号线每条都被插了 buffer那待机电流凭空多出几毫安到十几毫安。对一颗本身待机功耗目标只有几十毫安的芯片来说这是完全不可接受的水平。3.3 电流倒灌与闩锁效应隐患除了短路电流还有一个更隐蔽的风险是电流倒灌。isolation cell 输入端的 buffer 如果供电电压和关断域电压不同比如关断域用 0.8V常开域 buffer 用 1.2V那么在关断域断电后1.2V 的 buffer 输入会因为内部上拉路径而抬高电平电流通过 buffer 的输入保护二极管反向灌入关断域的电源网络。虽然大多数情况下这个电流不大但如果在低功耗模式下其他电源域也处于低压状态就可能造成意外的电压抬升干扰其他模块的漏电测量结果。极端情况下大电流注入还可能触发 CMOS 闩锁效应。寄生 NPN 和 PNP 构成的可控硅通路一旦导通电流会不受控制地增大芯片局部温度快速升高轻则功能异常重则物理损坏。虽然这在数字核心逻辑里不常见但在 IO 接口附近的隔离结构里真有可能发生所以后端工程师不能只关注常规漏电还要考虑这类可靠性风险。4. 工程实操如何从 netlist 和约束里查出一条“隐形 buffer”4.1 用报告命令检查 iso 周边 cell既然漏电危害这么大那怎么快速定位我的习惯是优先在门级 netlist 上做结构性检查速度最快也不依赖物理版图。核心思路很简单找出所有 isolation cell检查它们的 input pin 前一级驱动 cell 到底是什么类型。在 Innovus 或者 ICC2 里可以用以下思路来查。先拿到 design 里所有 iso 实例set iso_cells [get_cells -hier -filter ref_name ~ *ISO*]然后遍历每个 iso 的 input pin找到它所在的 net再看这个 net 的 driver cellforeach iso $iso_cells { set input_pin [get_pins -of_object $iso -filter direction in name I] set net [get_nets -quiet -of_object $input_pin] if {[llength $net]} { set driver_pins [get_pins -quiet -of_object $net -filter direction out] set driver_cells [get_cells -quiet -of_object $driver_pins] foreach driver $driver_cells { set ref [get_property $driver ref_name] if {[regexp {^BUF|^CLKBUF|^BUFFD} $ref]} { puts WARNING: iso $iso input driven by buffer $driver ($ref) } } } }这个脚本的思路很朴素但确实有效。ref_name 里带 BUF 或者 CLKBUF 的就说明 input 端被插了 buffer。需要注意不同工艺库的 buffer 命名规则不一样有的是 BUFX2有的是 BUFFD4有的是 CLKBUFX6写正则的时候要覆盖全。4.2 检查 dont_touch 是否被绕过定位到 buffer 之后下一步就是确认为什么工具会插进去核心工作是检查 dont_touch 属性还在不在。在两个工具里都可以直接查属性get_property [get_pins u_iso/I] dont_touch get_property [get_nets u_iso_input_net] dont_touch如果属性返回 false或者 pin 上有 dont_touch 但 net 上没有那工具就有可能在某个优化阶段绕过了保护。我见过一种情况综合阶段设了 pin 的 dont_touch但后端工具读入 UPF 时因为版本兼容问题把 adapter 阶段的 net 重新生成了dont_touch 就没有继承到物理网表上。这时候从 RTL 到网表的逻辑没变但物理实现阶段的保护已经丢了。所以检查不能只看综合脚本还要在 place 之后的 netlist 上再查一遍。这就像巡检电路板不能只看 BOM得拿万用表实测。4.3 一个完整的检查脚本示例为了便于复用这里给出一个比较完整的 Tcl 脚本可以直接在 Innovus 里跑。脚本会输出所有“疑似违规”的 iso 输入路径并标记出 buffer 的 ref_name、位置和所属 power domain。# check_iso_input_buffer.tcl set all_iso [get_cells -hier -filter ref_name ~ *ISO* -quiet] if {[llength $all_iso] 0} { puts INFO: no isolation cells found. exit } foreach iso $all_iso { set inp [get_pins -leaf -of_object $iso -filter direction in -quiet] foreach pin $inp { # 排除 enable 控制端 set pin_name [get_property $pin name] if {[regexp -nocase {enable|en|iso|ctrl|control} $pin_name]} { continue } set net [get_nets -quiet -of_object $pin] if {[llength $net]} { set drv [get_cells -quiet -of_object [get_pins -quiet -of_object $net -filter direction out]] foreach d $drv { set ref [get_property $d ref_name] set drv_power_domain [get_attribute $d power_domain] if {[regexp {^(BUF|BUFF|CLKBUF|CLKINV)} $ref]} { puts CHECK: $pin is driven by buffer $d ($ref), power_domain$drv_power_domain } } } } }有几个细节值得注意。第一排除 enable 控制端很重要因为 isolation cell 的 enable 信号本身可能经过 buffer 网络那是正常的。第二filter 的 ref_name 要跟实际库里 iso cell 命名风格匹配有的叫 ISOLAND有的叫 ISOOR有的叫 CLKISO。第三power_domain 属性不一定在每个阶段都有如果工具不支持就跳过不影响主检查逻辑。4.4 发现后如何修复如果检查出来确实有 buffer 插在 input 端修复方案取决于这个 buffer 是否有实际作用。先要判断净影响把 buffer 删掉后源端到 iso input 的 transition 和 max cap 是否还能满足。如果 buffer 是纯粹为了修 DRC 而插的那就原路撤回。在 ECO 环境里直接断开 buffer 的输出 pin把原 net 重新连线到 iso input然后删掉 buffer cell。这里要注意ECO 删除 cell 时不能留下悬空 pin否则 LVS 和形式验证都会报错。如果删掉 buffer 之后 transition 超标说明前端驱动不够这时候不能简单地在 iso input 端换一个更大的 buffer而是要回到关断域内部去增强驱动。可以采取以下几种手段把关断域内的源端 flop 换成驱动强度更大的 cell把 iso 的位置往源端挪近一点或者在关断域内部最后一级逻辑到跨域点之间优化走线。这些操作都不会破坏隔离语义也比在常开域硬塞 buffer 安全得多。修复完成后还要重新跑一遍形式验证和静态时序分析。形式验证要确认网络连接关系一致时序分析要重点看 iso 的 input net 的 transition 是否满足库要求。最后再跑一次功耗仿真对比待机电流是否回落到正常水平。5. 常见问题与避坑清单5.1 常见问题速查表这里我把项目里遇到过的问题整理成一张表方便大家对照定位。现象可能原因处理办法待机功耗比预期高几毫安iso input 端被插入常开域 buffer扫描 netlist删除 buffer增强源端驱动门级仿真出现大量 X 态关断域 buffer 插在 iso 前面断电后浮空移除 buffer 或确保 iso 输入直接连跨域信号die size 里多出很多 buffer 面积工具为了修 transition 自动插入对 iso input pin 和 net 设 dont_touch 后重新优化ECO 后功耗数字正常但实验室漏电偏大低阈值 buffer 在悬空输入下形成 crowbar 电流换高阈值 buffer 不能根治必须从结构上移除检查脚本找不到 iso cellref_name 的正则不匹配库命名先 report_cells 看实际 ref_name再改 filterUPF 里设置了 isolation工具仍然插 bufferdont_touch 没有继承到物理网表在 place 后重新设置并检查属性这张表里最后一条格外重要。UPF 描述的是电源意图dont_touch 描述的是实现约束两回事。UPF 不会自动阻止工具在 iso input 端插 buffer必须显式设置。5.2 我在实际项目中踩过的坑与建议第一个坑是只在综合阶段设了 dont_touch但没有在物理网表上复查。当时综合后的网表干净我以为万事大吉结果布局布线之后 ECO 版本里被插了三处 buffer。后来我把检查脚本加到了每日回归里每次 place 完成自动跑一遍再也没漏过。第二个坑是检查脚本的正则没写全。我一开始只过滤了 BUF 开头的 cell漏掉了 CLKBUF而那个项目里工具最喜欢用 CLKBUFX12 修长线。后来改成同时匹配 BUF 和 CLKBUF 才算完整。第三个坑可能更隐蔽DFT 插入阶段把 scan chain 相关的 buffer 插到了 iso input 端。因为 scan 模式下拉测试时信号路径可能被重新配置工具认为插 buffer 是合理的。但 low power 模式下这些 buffer 一样会变成漏电点。所以检查时不仅要在 func mode 网表上跑还要在 scan 插入后的 netlist 上跑。还有一个建议set_dont_touch 应该连 net 带 pin 一起设。我发现只设 pin 的 dont_touch 时有些工具版本会重新综合这段逻辑生成新 net原来的属性就丢了。两个都设同时约束工具就没有灵活操作的空间。另外在 UPF 里给 isolation cell 的 input 端口补充一条“isolation_signal 不参与优化”的注释也是一个好习惯虽然这更多是给人看的但它能帮你几个月后重新翻代码时快速想起来当初的意图。最后再说两句回到标题的问题iso 的 input 端为什么不能插 buffer因为插了 buffer就等于在电源域交界处埋了一颗不知道什么时候会爆的雷。它不会立刻让芯片功能失败但会在待机功耗上留下一个不合理的凸起可能会让一整块芯片因为几个毫安的漏电被测试部门打回。排查到最后原因往往就是一条没设到位的 dont_touch。我个人在实际项目里养成一个习惯所有网络扫描脚本里都带一个“isolation cell input driver check”的模块每跑一次 ECO 就自动执行一次。这不算什么高深技术但它帮我避免了好几次半夜改版。如果你也在做低功耗物理设计不妨把类似的检查加到你的流程里成本很低收益却很直接。