UPF2.1下Hard Macro与Top-Level Port电源意图建模实践与避坑指南

📅 发布时间:2026/9/29 5:07:10
UPF2.1下Hard Macro与Top-Level Port电源意图建模实践与避坑指南
几年前在做一颗SoC集成时我把一个第三方DDR PHY硬核的UPF文件写得“看起来正确”supply set 建了、power domain 也划了、隔离命令也放了综合和后端全部顺利通过。结果到了power-aware仿真阶段问题像挤牙膏一样冒出来——PHY输出送到常开域的信号漏了level shifter其中一组端口的隔离电源选错了域直接导致关断状态下下游逻辑被浮空输入反复触发翻转。那次排查整整花掉两天最后根因并不复杂我对这个Hard Macro的电源意图描述本质上只是“写了网表需要的电压值”而不是“约束了工具该理解的行为状态”。这类问题在UPF2.1项目里非常典型尤其是当你需要在顶层把Hard Macro和Top-Level Port的电源意图统一管理起来的时候。与其说UPF是在“描述”电源网络不如说它是在“约束”工具对模块行为边界的所有假设。面向做过基础UPF、想在集成层面把电源意图做得更成体系的工程师这篇内容会围绕Hard Macro怎么建模、顶层端口状态怎么定义、PST怎么闭合、隔离和电平转换怎么落位以及我实际踩过的哪些坑展开。1. 一个容易被低估的事实Hard Macro的电源意图连工具都只能“猜”1.1 Hard Macro凭什么特殊Hard Macro通常指那些预先完成版图设计、内部物理结构和时序都固定了的模块SRAM compiler生成的memory、SerDes PHY、DDR PHY、模拟前端、高速接口IP都属于这一类。你拿到的交付物往往是一堆.lib、.lef、.gds以及一个可能写得很含糊的UPF文件甚至干脆没有UPF。这给电源意图建模带来一个根本矛盾UPF这套语言的设计初衷原本是让我们“从RTL阶段描述功耗意图然后由工具在逻辑综合和物理实现时自动推导出电源网络”。但Hard Macro已经是物理实现完的内部哪条电源轨道连接哪个晶体管、哪个寄存器在什么条件下被保电你既改不了也看不见。工具唯一能掌握的信息来自库文件和你在顶层给它画出的外部接口约束。所以处理Hard Macro电源意图的第一原则是你写的不是它的内部电路而是它的“外部行为契约”。工具只有看到一份明确的契约才知道这个宏的输出端口在某个电源状态下是否有效以及它的输入端口需要什么电平驱动。否则它只能靠猜——猜的结果就是前端仿真过了、后端物理验证却暴雷。1.2 用supply set做“外部可见性”抽象UPF2.1相对早期版本的核心进步之一是让supply set成为一等公民。你可以把一组供电关系抽象成一个集合例如{power VDD, ground VSS}就是一个supply set。Hard Macro不需要暴露内部几十条供电轨它只需要在端口边界上声明好“我这个宏的外部供电接口对应哪几个supply set”工具就会拿着这套抽象去和库单元里的power pin、ground pin做映射。这里有个很关键的理解UPF的约束对象是“信号端口的行为”而不是“金属连线”。你写了create_supply_set SS_UHS0并不代表芯片上真的产生了这条电源网络它只是给了工具一个逻辑手柄让工具知道哪些端口和这条电源状态相关。真正落到物理上的供电网络是后端根据supply net的关联关系生成的。对Hard Macro而言外部可见的就是它暴露出来的那些端口和供电引脚所以用supply set去抽象它的外部行为刚好是闭环的。2. 黑盒还是灰盒Hard Macro供电建模的两条典型路径2.1 纯黑盒只声明外部可见的供电关系对于模拟IP、射频前端这类内部没有数字电源管理逻辑的Hard Macro通常采用纯黑盒建模。你不需要关心它内部有几个电源域只需要在外部为它建立对应的supply port和supply set然后与该宏的物理引脚关联。一份典型的黑盒约束看起来是这样的create_power_domain PD_PLL -include_scope create_supply_port TOP/VDD_PLL create_supply_port TOP/VSS create_supply_net TOP/VDD_PLL_NET -port TOP/VDD_PLL create_supply_net TOP/VSS_NET -port TOP/VSS create_supply_set SS_PLL_CORE -function {power TOP/VDD_PLL_NET} -function {ground TOP/VSS_NET} set_domain_supply_net PD_PLL -primary_power_net TOP/VDD_PLL_NET -primary_ground_net TOP/VSS_NET关键一步是把这些外部supply set与宏的端口关联起来。通常工具会通过库文件中power pin的名称映射或者通过associate_supply_set来建立关系associate_supply_set SS_PLL_CORE -handle {inst_pll/VDDPLL}这一步走完之后工具就“默认”这个宏的所有内部逻辑都工作在这个supply set下。黑盒的问题是如果Hard Macro内部实际上有多个供电引脚比如VDD、VDDIO、VDD_A而你在外部只定义一个supply set后端的connect_upf_domain就会把多个物理pin都挂到同一根net上仿真和IR drop分析都会失真。所以黑盒不意味着简单它要求你把宏的全部物理电源引脚都摸清楚再决定如何映射到少数几个逻辑supply set。2.2 灰盒继承内部电源域时的scope处理第二种情况是Hard Macro内部本身已经做过低功耗设计。类似DDR PHY这类复杂的数字硬化IPvendor交付时通常附带一份内部UPF文件里面定义了内部power domain、supply set、isolation和retention逻辑。这种情况下如果在顶层又重新写一套supply net去覆盖它几乎必然产生scope冲突或状态残留问题。正确做法是把vendor UPF作为子模块约束继承进来。UPF标准本身没有统一的include命令但主流工具实际都支持通过Tcl的source方式加载或者由集成脚本先把设计scope切入到该宏实例下再执行vendor UPFcurrent_design TOP set_scope inst_ddr_phy source $IP_DIR/ddr_phy_power.tcl set_scope TOP继承之后顶层就可以把宏内部定义的supply set通过关联命令“上提”到当前层使用。这里最容易犯的错是重复创建同名supply port。vendor UPF里已经create_supply_port VDD_PHY你在顶层又create_supply_port VDD_PHY后端的UPF解析器会直接把两个object当成不同实体net连接关系全乱。对于memory IP灰盒建模有一个典型场景SRAM一般至少包含两个外部供电脚核心电源VDD和IO电源VDDIO有些带深度睡眠模式的还会多一个VDDCS。如果你只用一个supply set去套那么低功耗验证工具无法理解为什么VDD掉电后VDDIO还能维持输出也无法正确判断retention状态下哪些cell还活着。正确做法是为每类供电引脚建立独立supply set并在PST中分别定义状态。2.3 怎么选三条判断标准我自己的选择标准很简单第一vendor给不给内部UPF。给优先走灰盒不给只能黑盒。第二这个宏是否存在“掉电后仍有部分电路需工作”的场景。只要存在就必须拆成多个supply set不能黑盒一把抓。第三验证团队对power-aware仿真的要求级别。如果项目只做RTL级UPF检查黑盒可能够用如果要做动态仿真和静态检查全链路建议直接上灰盒宁可前期多花时间也不要等signoff阶段才被工具逼着重写。维度纯黑盒灰盒内部细节了解程度不需要需要vendor文档/UPF多电源场景支持弱需要手工拆多个supply set强内部状态表可由vendor维护Scope冲突风险低高source时要小心后端net连接准确性由外部映射决定更接近宏内部真实结构适用对象模拟IP、简单数字宏带低功耗设计的PHY、memory3. Top-Level Port的电源意图声明仅仅是开始3.1 顶层端口要分三类理解Top-Level Port是芯片和外部世界交互的边界但“顶层端口”四个字在UPF语境下很容易模糊。实际工程中至少能分成三类芯片物理pad端口直接和封装管脚相连通常挂在VDDIO供电域。core逻辑端口存在于芯片内部逻辑和IO之间通常挂在core电压域。某些必须跨电压域传递的控制信号端口比如深睡模式下仍需要接收的外部唤醒信号。UPF2.1处理这些端口时本质上要求你为每类端口指明它属于哪个supply set并且定义它在这个supply set不同状态下的端口行为。很多工程师只做前一半声明端口属于某个power domain但没做后一半——端口状态定义。这会导致工具在推断isolation、level shifter插入位置时缺少行为依据。3.2 create_supply_port到add_port_state的完整链路一个相对完整的顶层端口电源意图描述应该从电源端口声明一路走到端口状态定义。下面是一个简单例子create_supply_port VDDIO create_supply_port VDD create_supply_port VSS create_supply_net VDDIO_NET -port VDDIO create_supply_net VDD_NET -port VDD create_supply_net VSS_NET -port VSS create_supply_set SS_IO -function {power VDDIO_NET} -function {ground VSS_NET} create_supply_set SS_CORE -function {power VDD_NET} -function {ground VSS_NET} create_power_domain PD_TOP -include_scope set_domain_supply_net PD_TOP -primary_power_net VDD_NET -primary_ground_net VSS_NET add_port_state TOP/rst_n \ -state {on -supply SS_CORE} \ -state {off -supply SS_CORE}注意这里add_port_state的语义它声明rst_n这个端口在SS_CORE处于on状态时是有效的高电平复位在off状态下则属于无驱动状态。这样写完之后工具才能推导出如果外部把rst_n拉到高电平而core域已经断电这个端口会不会变成非法驱动。从工具视角看端口状态和PST是对照阅读的。PST里写SS_CORE.off那么所有与SS_CORE关联的端口要么被隔离要么被保持要么明确标注为dont care。没有端口状态约束信号的端口会被形式化工具视为“未知行为”在power-aware仿真里表现出来就是X态传播排查起来极其痛苦。3.3 PST闭合性是排查问题的关键抓手Power State Table也就是PST是UPF2.1里约束电源状态组合合法性的核心。很多项目在写的阶段就埋了雷原因不是语法错误而是状态组合没有闭合。闭合的意思是凡是系统可能出现的、实际硬件上能成立的供电状态组合PST里必须显式出现凡是硬件上不允许的组合PST里不能出现否则一旦出现工具就会挂。举例来说一颗芯片有core域、IO域和一个PLL供电域正常工作时三个都on深度睡眠时core和PLL都off但IO保持on。PST至少要有两个状态create_pst PST_TOP -supplies {SS_CORE SS_IO SS_PLL} add_pst_state S0_NORMAL -pst PST_TOP \ -state {SS_CORE.on SS_IO.on SS_PLL.on} add_pst_state S1_DEEPSLEEP -pst PST_TOP \ -state {SS_CORE.off SS_IO.on SS_PLL.off}问题往往出在第三个状态core on、PLL off的这种“中间态”在软件切电源的序列里很可能出现但PST里没写。那么当UPF检查工具或者power-aware仿真跑到这个组合时要么报状态非法要么就把PLL相关的端口行为当成未知产生伪X态。更隐蔽的问题是端口状态和PST的组合合法性。比如PST里SS_PLL.off但某个顶层端口却只定义了-state {on -supply SS_PLL}没有定义off行为静态检查直接报端口在非法状态下无约束。这种问题手动加时间找起来很慢最稳的做法是在创建PST前先列一张完整的状态组合表逐个打勾。状态名SS_CORESS_IOSS_PLL端口rst_nPT_OUTS0_NORMALononononvalidS1_DEEPSLEEPoffonoffoffclamp0非法态onoffon--4. 边界策略落地隔离、保持与level shifter怎么放4.1 隔离策略在Hard Macro边界的特殊性隔离和保持策略是电源意图落到逻辑边界的具体动作。无论你是用set_isolation自动插入还是后端手工例化都需要在UPF里把策略描述清楚。Hard Macro边界和普通模块边界有一个本质区别Hard Macro内部可能没有可替换的隔离cell。也就是当宏掉电时你不能指望宏内部自己把自己的输出钳住必须在宏外部插隔离单元。隔离单元的供电必须来自常开电源否则宏掉了电隔离单元跟着掉等于没隔离。这里给一个典型写法set_isolation PLL_ISO_OUT \ -domain PD_PLL \ -isolation_supply_set SS_IO \ -clamp_value 0 \ -applies_to outputSS_IO就是那个常开电源。曾经有同事把-isolation_supply_set写成了SS_CORE综合工具也接受了直到IR drop分析时才暴露问题core域在深睡模式下根本没电隔离单元当成摆设。工具其实不会主动纠正这种逻辑错误它只检查supply set在PST里是否常开所以你在选择隔离电源时自己心里必须有一张“谁常开、谁可关”的表。在memory的retention场景里隔离策略要配合set_retention使用。UPF里需要指定save和restore条件更关键的是要指定-save_supply_set也就是进入保持状态后由哪个供电集合维持寄存器内容set_retention RET_SRAM_REG \ -domain PD_MEM \ -retention_condition {save_en} \ -save_supply_set SS_VDDCS4.2 电平转换不是“插入一个cell”那么简单Level shifter的插入条件是简单清晰的信号从一个电压域进入另一个电压域且两侧电压值不同就必须插。但Hard Macro场景里有个麻烦——工具看不到宏内部的电压域边界它只能看到宏端口所关联的supply set。所以当信号从Hard Macro的输出端口出来进入core域时如果宏端口关联的是SS_PLL_CORE而core域是SS_CORE两者电压不一致工具会自动尝试在端口边界插level shifter。如果库里的level shifter cell的上限电压、下限电压覆盖不到这两个域的电压组合工具在优化阶段会悄悄放弃插入然后只给你一条warning。这条warning经常被淹没在几千条日志里。常见的解法是显式声明level shifter策略甚至直接指定使用哪一种cellset_level_shifter LS_PLL_OUT \ -domain PD_PLL \ -applies_to output \ -location self \ -threshold 0.8 \ -cells {LVLSHD_1V0_0V8_S}-location self表示让工具把level shifter放在宏边界内部一侧这在某些物理约束下更有利。从电源意图的角度看重点是让工具明确知道这个点是跨域点并且有足够约束完成替换而不是留给后端去猜。4.3 常开路径的UPF表达方式总有些信号即使在主电源关断时也必须“活着”比如远程唤醒信号、紧急中断。UPF里表达常开路径核心不是靠一条命令而是靠supply set在PST中的状态组合来体现。如果某条路径的信号始终由SS_IO供电那么只要SS_IO在PST所有状态下都是on这条路径就是常开的。换句话说常开路径不是专门标出来的而是通过“该路径所有逻辑单元都挂在常开supply set上”来保证的。Hard Macro场景中常见的错误是一个宏的某根中断输出确实连到了常开域但宏本身的供电域已经在PST里被定义成off。这种情况下即使互连net挂在常开supply set信号源也已经没了路径“看似常开”实际失效。UPF工具检查always-on路径时会把这种场景报成supply set冲突但前提是你把宏内部该中断端口的driving逻辑明确关联到了常开电源。如果macro vendor的UPF里没有这么做检查也发现不了只能出物理仿真问题时才炸。5. 我踩过、也看别人踩过的四个深坑5.1 坑一source内部UPF时scope重复声明用灰盒方式集成vendor UPF时我最常遇到的报错是“supply port already exists”。现象vendor UPF里声明了create_power_domain PD_INNER顶层集成脚本里也定义了同名域或者顶层为了连net直接也create_supply_net VDD_PHY结果和vendor里名字撞了。排查思路很简单但容易被忽略source vendor UPF之前先查一下当前设计scope里有没有已经存在的同名object。建议项目脚本里统一封装一个source_upf的过程在source前自动做一个已存在对象的检查。另外vendor UPF通常在自己模块内部要求create_power_domain ... -scope_definition或者依赖于宏的current_design直接放在顶层source往往会有中间状态最好在隔离的synthesis session里逐步验证。5.2 坑二端口状态和PST状态对不上曾经有一个项目PST定义了SS_CORE.off但某条从core域经隔离单元到IO域的接口信号其输入端口只声明了on状态没有声明off状态。静态检查工具在检查时序弧合法性时不断报“port is unconstrained in state”。看起来是某个端口的约束漏了实际暴露的是接口协议本身的问题——这条信号在core掉电后到底是隔离成常低还是保持之前的值设计团队一直没定义清楚。UPF在这里充当了不撒谎的镜子你没写清楚工具就把问题亮出来。修法不是简单补一条add_port_state而是先和架构团队确认掉电策略再决定加isolation还是加pull-down。5.3 坑三PST少写了非法状态这属于最隐蔽的一类问题。PST漏掉某个中间供电组合仿真工具不会在UPF解析时报错只在动态仿真跑到那个组合时出现大量未知态。更麻烦的是这类X态问题可能只出现在某一条特定路径上复现率极低。一次在调试DDR PHY的低功耗状态切换时我们花了一周才发现问题来自PST缺一个状态PLL域和PHY core域的掉电顺序不是原子的中间存在几百ns的“PLL off、PHY core on”状态。PST没定义工具把这段窗口的所有信号都标成X正好落到数据总线仿真直接没法收敛。把中间态补进PST后问题消失。这个经历让我养成了习惯任何新增的power domain或supply set都要在上线之前列出完整的二维组合表把所有合法与非法状态一次性标清楚。5.4 坑四retention模式的supply set函数没写全Hard Macro中带retention功能的寄存器在UPF里需要描述三样东西保存条件、恢复条件、保持阶段使用哪条电源。很多人写了set_retention但忘了指定-save_supply_set或者指定了但该supply set在PST里没有保持状态。后端工具倒不会报错但实际上拉出的网表里retention cell的电源连接是断的等价性检查过不了。一个相对完整的retention约束应该覆盖save和restore两侧set_retention RET_MACRO_REG \ -domain PD_MACRO \ -retention_condition {save_en} \ -restore_condition {restore_en} \ -save_supply_set SS_MACRO_RET其中SS_MACRO_RET必须在PST中有一个始终可用的状态。这往往意味着要把宏的retention电源轨单独抽象成一个supply set而不是直接复用主供电。6. 把电源意图变成可交付的Signoff结果6.1 静态检查到底在查什么UPF写得好不好最终要看能不能通过静态检查。主流程上有Cadence VCLP、Synopsys CLP以及EDA工具自带的UPF checker它们本质上都在做同一类事检查所有supply net、supply set是否有对应的物理端口没有就是悬空。检查每个power domain的primary supply net是否和该域内所有lib cell的power pin对齐。检查每条跨域信号路径上是否有isolation、level shifter没有则报错。检查PST状态组合和端口状态定义是否闭合不闭合则报warning。检查retention cell的save supply set是否在PST中可见。很多warning你看着像没事但都往后端堆积最后变成一堆“waiver”。我的建议是任何warning都要写理由写不出理由就必须在源头清零。Hard Macro和Top-Level Port的问题越往后面越难改因为涉及物理连接和约束死角。6.2 从UPF到后端网表的衔接细节UPF在前端验证里只是逻辑约束到了后端工具会基于它插入真实的isolation cell、level shifter、power switch。这里有一个容易忽略的事实后端工具默认只对它“看得见”的边界做这些自动化处理。Hard Macro内部已经被固定工具不会也不可能在里面替你插任何cell。所以Hard Macro周边的隔离、电平转换、常开电源连接基本都有两种落地方式一种是在UPF里用-location self要求工具把cell放在宏边界上另一种是直接在网表里手工例化这些cell然后把UPF里的策略设为already_placed或not apply。我个人倾向于后者因为Hard Macro通常有特定的物理约束和时序预算手工例化更可控。但无论哪种UPF里的状态定义和PST约束都必须保持完整因为后续的功耗分析、形式化验证、低功耗仿真都会重新解释这套行为。6.3 一份值得收藏的检查清单下面这张表是我每次处理Hard Macro和Top-Level Port时都会过一遍的检查项你可以直接拿去做模板检查项检查动作通过标准Supply port完整性对照数据手册列出宏全部供电引脚每个供电引脚都能映射到唯一supply setSupply set命名一致性检查lib、LEF、UPF三处名字三处无同名不同义、同义不同名端口状态定义每个Top-Level Port关联的supply set所有状态都有端口行为无unconstrained端口PST状态组合列出所有合法/非法供电组合并逐一核对合法组全部在PST中非法组全部不存在隔离策略电源检查isolation cell的supply set在PST中是否恒on无隔离电源随主域掉电Level shifter覆盖范围检查所有跨域路径是否已插入或声明0条遗漏跨域路径Retention完整性检查save/restore条件和save supply setPST中保留状态可见最后再分享一个个人经验低功耗设计的复杂度在当今SoC里早就不是靠某个“高手画几笔UPF”能兜住的了。Hard Macro和Top-Level Port的电源意图处理本质上是一个工程管理问题——把每一条电源状态、每一个边界端口、每一段跨域路径都变成可检查、可追责的条目才能避免把风险带到芯片回来之后。我自己现在接手新项目第一件事不是写UPF而是先把所有Hard Macro的手册、所有顶层IO的电源要求整理成一张表再让UPF去表达这张表。工具是诚实的你偷懒的地方它早晚会在某一环节加倍还回来。