ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Innovus的四核A7 SoC时钟树及低功耗设计实践

基于Innovus的四核A7 SoC时钟树及低功耗设计实践 前阵子刚把一颗四核Cortex-A7 SoC的后端设计推进到signoff阶段趁记忆还热乎把这轮在Innovus里折腾时钟树和低功耗设计的思路、脚本、踩过的坑完整整理出来。A7这个IP大家都不陌生ARM家主打能效比的应用处理器核拿它做四核SoC的方案非常多从工业控制到边缘计算都有覆盖。但A7虽然本身功耗友好一旦凑成四核加上周边一堆外设和总线时钟树和低功耗这两块就成了后端能不能顺利signoff的关键。这篇文章不打算讲教科书式的流程纯粹按真实项目中“怎么想、怎么配、怎么写脚本、怎么排错”的路子来。核心围绕两件事一是如何在Innovus里把四核A7的时钟树做得又稳又省功耗二是怎么通过UPF和多电压域等手段把低功耗设计落到实处。文末会附上可直接改用的Tcl脚本框架大家根据自己的工艺库和约束调参就能跑起来。1. 项目拆解与设计思路1.1 四核A7 SoC后端设计的三个硬骨头这颗芯片不算复杂但“小芯片”恰恰是后端设计最难抠的地方。四核A7意味着主时钟要送到四个核簇每个核内部还有自己的分频和门控逻辑。你在设计上遇到的第一个问题就是四个核的时钟到达时间clock latency必须尽量一致否则跨核通信的时候时序收敛会非常痛苦。第二个硬骨头是功耗。28nm工艺下漏电功耗不再是完全不用管的级别更重要的是动态功耗——时钟网络从来都是功耗大户尤其是处理器簇这种高翻转率的模块。为了控制功耗你需要做多电压域核心域和IO域分开供电空闲时还能把某个核的电源彻底断掉。这些在RTL阶段定好的架构物理实现阶段要真刀真枪地落到版图上。第三个问题是面积。四核A7加上L2、GIC、DMA、各类总线桥芯片面积并不宽裕。时钟树综合的时候如果skew约束设得太紧工具会疯狂插buffer功耗和面积一起爆掉。这个度怎么拿捏就是后端经验的核心了。1.2 时钟树优化和低功耗为什么必须一起讨论很多人习惯把CTS和低功耗当成两个独立阶段来做先调好时钟树再考虑省功耗。但在实际项目中这两个目标严重耦合。先说时钟树的功耗占比。一颗芯片中时钟网络消耗的动态功耗通常能占到全局的30%到40%处理器核这种高频模块甚至更高。每多插一级buffer就多了一堆时钟沿翻转功耗就多一份。所以在设定CTS目标时skew、latency、max transition这些指标并不是越小越好而要在满足时序的前提下尽量少插buffer、少耗电。再说低功耗设计对时钟树的影响。一个典型场景是时钟门控clock gating。为了省电大部分模块都会在空闲时把时钟关掉这是通过ICGIntegrated Clock Gating单元实现的。问题是ICG插入之后门控时钟网络和常开时钟网络的负载差异会很大CTS做平衡的时候处理不当就会产生严重skew。另一个场景是多电压域。不同电源域的信号穿越需要加level shifter这些额外单元加进时钟路径或者数据路径都会影响时钟树的规划。所以我的思路很明确在设计阶段先把时钟架构和低功耗架构一起定下来再在Innovus里用脚本和约束把这两个维度的需求同时表达给工具。接下来两节分别展开。2. 时钟树设计从约束设定到树形收敛2.1 时钟树核心指标怎么定CTS开始之前先把目标指标定清楚。这不是拍脑袋而是基于时序余量和功耗预算反复权衡的结果。我的设计指标供大家参考指标目标值说明全局skew≤ 80ps四核之间时钟到达时间差核内skew≤ 50ps单个A7核内的时钟偏差Max transition≤ 0.3ns过长transition会导致电池竞争和时序恶化Max fanout≤ 16可工具自动调整对A7这种中等负载的单元合适时钟树级数控制在25级以内级数过多会造成latency过大且功耗飙升这里有个经验不要一上来就把全局skew设成30ps或者更小。工具为了满足这个目标会插大量buffer时钟网络面积和功耗立刻暴涨而且后期ECO想动都难。80ps的skew对1GHz左右的A7设计已经足够时序收敛完全够用。如果时序分析后发现某些路径实在紧张再用useful skew针对性调整不要全局拉紧。2.2 CTS前的预检查清单时钟树综合的前提是布局基本合理我习惯按这个顺序做预检查检查SDC完整度。所有生成时钟generated clock都要定义清楚尤其是PLL分频出来的各档时钟以及门控之后的功能时钟。漏定义任何一条CTS的结果都会很离谱。检查placement density。密度超过70%时CTS的绕线压力会很大建议提前调整布局或者把std cell的利用率控制在合理区间。检查clock net上的DRC。CTS之前如果clock path上有短路或者天线问题先清掉再跑综合。检查macro placement。内存宏的位置直接影响时钟树的布线走向我习惯先把大的SRAM、PLL手动摆好再让工具做placement。# Innovus中快速生成并检查时钟树约束 create_clock_tree_spec -file ./outputs/cts_spec.tcl edit_clock_tree -spec_file ./outputs/cts_spec.tcl check_clock_tree -verbose ./outputs/cts_presolve.rptcheck_clock_tree会在真正跑CTS之前把所有潜在问题列出来比如时钟端口定义缺失、跨域路径、ICG单元未识别等等。这一步非常关键宁可多花一小时做预检查也不要等到CTS跑完再回头看log。2.3 时钟树综合的完整流程和参数选择我在Innovus里跑CTS的标准流程分四步。第一步是生成并编辑spec文件第二步是设置CCopt的优化参数第三步运行综合第四步做结果分析和确认。# CTS阶段脚本四核A7时钟树综合与平衡 set_db init_lib_search_path /path/to/liberty/lib set_db init_hdl_search_path /path/to/rtl/ # 读取网表和约束 read_mmmc inputs/mmmc.tcl read_physical inputs/pin_assign.tcl read_netlist outputs/placement.sdc # 放置基本配置 set_db place_detail_legalization_inst_gap 3 set_db place_detail_legalization_max_displacement 100 # ----- 时钟树目标设置 ----- # 全局skew目标:四核之间控制在80ps以内 set_db cts_target_max_transition_time 0.300ns set_db cts_target_skew 80ps # 指定时钟树使用inverter优先功耗更低 set_db cts_buffer_cell_list {BUFX2 BUFX4 BUFX8 BUFX12 INVX2 INVX4 INVX8} set_db cts_inverter_cell_list {INVX2 INVX4 INVX8} # 开启时钟门控的时序修复 set_db cts_clock_gating_aware_optimization true # 生成CTS spec文件 create_clock_tree_spec -file ./outputs/cts_spec.tcl # 修改spec中的关键参数可选按需微调 edit_clock_tree -spec_file ./outputs/cts_spec.tcl # 运行时钟树综合 clock_opt_design这里的几个参数值得多说两句。cts_buffer_cell_list指定了时钟树可用的缓冲器单元我优先选了功耗和驱动都平衡的BUFX4和BUFX8INV系列则用来做反相器级。很多先进工艺下inverter对时钟边沿的恢复能力比buffer好而且面积小所以把INV加进去是常规操作。cts_clock_gating_aware_optimization是处理门控时钟的关键。它会自动识别ICG单元的输出在门控分支上做平衡避免门控后时钟路径和常开路径的skew过大。这个选项对A7这种大量使用时钟门控的IP非常重要我强烈建议打开。2.4 四核时钟平衡的实战处理四核A7的时钟平衡是这次设计最棘手的地方之一。四个核在物理上分开放置到PLL的走线距离天然不同如果直接交给工具默认跑CTS出来四个核的latency往往差出100ps以上。我的处理方式是先做“粗平衡”再靠工具做“细平衡”。具体来说在placement之后、CTS之前手动给四个核加上对称的skew group约束让工具在建立时钟树时就把“四个核要同时到达”作为一个显式目标。# 手动定义四核时钟树组实现跨核平衡 set_db cts_enable_skew_grouping true create_clock_tree_group -name group_CPU0 -sinks {u_CPU0/reg_clk/*} create_clock_tree_group -name group_CPU1 -sinks {u_CPU1/reg_clk/*} create_clock_tree_group -name group_CPU2 -sinks {u_CPU2/reg_clk/*} create_clock_tree_group -name group_CPU3 -sinks {u_CPU3/reg_clk/*} # 这四个组之间要求平衡 set_clock_tree_group_constraints -group_list {group_CPU0 group_CPU1 group_CPU2 group_CPU3} -max_skew 80ps跑完之后我用下面的命令做结果确认# 时钟树结果报告 report_clock_tree -detail ./outputs/cts_tree.rpt report_clock_timing -type skew ./outputs/cts_skew.rpt最终四核间skew稳定在65ps左右满足80ps的目标。ts_skew.rpt这个报告必须认真看里面会列出每条时钟路径的起点、终点、latency和transition任何一条路径endpoint的transition超标都要回溯到对应的clock tree spec去调。3. 低功耗设计多电压域与时钟功耗优化3.1 从UPF到Innovus的电源意图落地A7四核SoC的低功耗设计我在前端RTL阶段就写好了UPFUnified Power Format到Innovus这一步的核心任务是把电源意图实际落地成物理实现。这包括电源域的划分、电平转换单元的插入、隔离策略的执行、保持寄存器的处理以及关键的always-on buffer网络。这颗芯片的电源域划分大致是处理器四核各占一个独立域可以单独断电以支持动态休眠总线互连和L2做一个域常开IO和模拟接口单独隔离。# 低功耗设计脚本UPF读取与电源域落地 # 读取UPF文件提交电源设计意图 read_power_intent -1801 upf -file inputs/low_power.upf commit_power_intent # 检查电源域状态 report_power_domain ./outputs/power_domain.rpt report_supply_net ./outputs/supply_net.rpt第二步非常关键。UPF文件中会写清楚每个域的供电电压、电平转换策略、隔离策略但工具能不能正确落地靠的是commit_power_intent这个命令。提交之后Innovus会把每个power domain的pg pin连接信息更新到物理数据库中。此时检查report_power_domain确认每个域的Supply Set和对应的Power Switch Cell都正确连接否则后面跑power analysis会一片混乱。3.2 Level Shifter与Isolation Cell的插入策略跨电压域的信号必须经过电平转换否则高电压域的信号驱动低电压域的逻辑时会漏电。A7核域通常是0.9V甚至更低而IO域和常开域一般在1.2V或者1.8V。这些跨域路径数量不少位置还分散。我的策略是“域边界集中放置”而不是“路径各处乱插”。在UPF里指定level shifter放置在目标域侧这样物理上前后单元的位置更集中绕线更可控。Innovus里用insert_level_shifter_pin来指定策略或者靠UPF的set_level_shifter完成。# Level Shifter和Isolation策略设定 # 建议在commit_power_intent之前通过UPF文件设置 # 如果在Innovus内调整可用如下命令 set_level_shifter_strategy -rule low_to_high -location from set_level_shifter_strategy -rule high_to_low -location to # 插入隔离单元在模块断电时钳住输出 set_isolation_strategy -domain PD_CPU0 -isolation_net ISO_CPU0 -clamp_value 0实际操作中我遇到过一个坑level shifter的设置如果两边驱动能力不匹配会出现transition time违规。比如低电压域驱动一个高电压域的level shifter推不动后级全是transition violation。这种情况下需要在level shifter前手动加buffer增强驱动。隔离单元的插入同样要小心。如果隔离单元插在离输出端口太远的地方断电瞬间的毛刺会直接窜到常开域导致总线误操作。我的经验是把isolation cell放在靠近输出端口的位置并且用always-on的供电这个在UPF里要显式声明。3.3 时钟门控、操作数隔离与动态功耗优化时钟门控是低功耗设计里收益最明显的手段。A7这种处理器在跑workload时很多子模块其实并不工作RTL设计里已经有大量ICG在起作用。但从后端角度我需要做的事情有两个一是确保ICG单元被正确识别和布局二是让CTS对门控时钟和常开时钟做平衡。在Innovus中ICG单元需要被标记为clock gating cell这样CTS才能正确处理。一般工艺库会自带ICG单元但后端需要确认lib中的cell是否被工具识别为clock gate。可以用下面的命令检查。# 检查并设置ICG单元识别 report_clock_gating ./outputs/clock_gating.rpt # 如果没有正确识别手动指定 set_db lib_cell CLKORCGX4 -clock_gating trueICG单元的摆放位置也很讲究。离时钟树的源端太近后面挂过去的寄存器扇出会非常多影响transition离得太远门控分支的延迟和常开分支差太多CTS平衡困难。比较理想的位置是在时钟树的中后段同时保证该模块内的寄存器都能在短距离内接到时钟。操作数隔离是动态功耗优化的另一招。在A7内部很多算数逻辑单元在空闲时输入端仍然翻转白白消耗动态功耗。操作数隔离的原理是在空闲时把数据输入端钳位让内部节点不翻转。这个优化通常在前端RTL做但后端如果发现功耗评估时某些模块动态功耗过高也可以通过ECO插隔离逻辑来解决不过成本比较高尽量前端处理。3.4 低功耗物理实现中的两个常见坑低功耗设计在后端实现中我认为最值得说的坑有两个。第一个坑是always-on buffer。断电域的某些信号在芯片休眠时还需要维持比如唤醒信号、隔离使能等。这些信号必须走always-on的供电网络驱动它们的buffer也必须是常开单元。问题在于默认的布局绕线阶段工具并不知道哪些信号是always-on的于是会把它们的driver放在关断域内导致芯片唤醒功能彻底失灵。常规做法是在UPF中定义好always-on信号CTS之后做一个专门的检查确认这些buffer都在常开电源域内。# 检查always-on buffer是否落在常开域 check_power_switches -verbose ./outputs/power_switch_check.rpt check_pg_pin_connectivity -verbose ./outputs/pg_conn_check.rpt第二个坑是电源开关单元的压降。Power switch cell在关断域中负责控制供电通断插入数量不足会在大电流时产生明显IR drop。尤其在处理器跑到高频高负载时压降会导致时序劣化甚至功能错误。我的做法是在布局后用EM/IR工具做一次动态压降分析如果压降超标就在热点区域补插power switch。4. 后端脚本复用与ECO操作4.1 从CTS到Clock ECO的完整脚本后端设计不是一锤子买卖。时钟树第一次跑完几乎必然有不满足的路径要么是setup violation要么是hold violation要么是skew超预算。在Innovus里我习惯用一套结构化的脚本来做时钟树ECO而不是每次都手动改。# 时钟树ECO通用脚本定位问题 - 调整spec - 重跑CTS - 验证 proc eco_clock_tree {} { # 第一步报告所有时钟树违规 report_constraints -violators -verbose ./outputs/cts_violators.rpt # 第二步针对setup violation检查是否时钟latency失衡 # 针对hold violation检查是否data path被过度优化 # 针对skew问题临时收紧或放宽特定group的约束 # 第三步只对修改的时钟域做增量综合避免全量重跑 clock_opt_design -only_sub_clock_trees -sub_clock_trees {group_CPU2} # 第四步验证结果 report_clock_timing -type skew ./outputs/cts_skew_eco.rpt report_constraints -all ./outputs/cts_constraints_eco.rpt }增量重跑CTS是提高效率的关键。比如发现只有group_CPU2内部的skew超标就没必要全芯片重新balance用-only_sub_clock_trees选项只重做那个group时间可以省下一大半。4.2 ECO Buffer Tree的插入实践时钟树后期还会遇到一类特殊需求不是修时序而是修功能。比如某个信号需要额外驱动能力或者某个模块增加了一个需要时钟同步的寄存器组这个时候需要手工插buffer tree。Innovus里有一个命令专门干这个eco_buffer_tree。它可以按照用户指定的拓扑结构在指定位置插入多级buffer并自动完成布线。我用它处理过一次A7核外部的中断控制器时钟分配问题效果相当不错。# 手工ECO buffer tree示例 # 需求在GIC时钟入口增加一级buffer提高驱动能力并匹配延迟 create_eco_buffer_tree \ -name eco_buf_gic_clk \ -source u_gic/clk_in \ -sinks {u_gic/u_intc/reg_ack/clk u_gic/u_intc/reg_mask/clk} \ -cell_list {BUFX4 BUFX8} \ -max_fanout 8执行之后要看两个地方一是新增buffer的物理位置是否合理二是插入后的时钟延迟变化是否在可接受范围。ECO buffer tree操作完毕务必再做一次完整的时序回归因为手工插入buffer可能影响共享路径上的其它模块时序。4.3 脚本之间的数据传递与自动化跑复杂项目的后端流程脚本自身的可维护性也很重要。我习惯把脚本拆成三部分公共配置脚本、流程控制脚本、ECO/修签脚本。公共配置脚本定义所有工艺相关的路径和标准单元列表流程控制脚本调用公共脚本并按顺序执行place、CTS、route、signoff检查ECO脚本单独存放只在需要时手动调用。项目里有个很实用的技巧用环境变量控制流程节点。通过一个shell变量指定当前跑到哪一步脚本内部做节点判断这样CI自动化或者晚上挂机跑批处理都方便很多。例如#!/bin/bash # 流程控制示例nightly run export STAGEcts innovus -stylus -files ./scripts/run_innovus.tcl -log ./logs/cts_run.log脚本里的参数尽量统一走变量不要硬编码路径。同一个设计今天在服务器上跑明天挪到本地跑只要环境变量改一下就行脚本本身不用动。5. 常见问题与排查技巧实录5.1 时钟树综合阶段常见问题速查这一份速查表是我在实际项目中反复用到的排错清单覆盖了CTS阶段出现频率最高的问题、原因定位和解决办法。问题现象可能原因排查命令解决思路CTS后skew严重超标未识别ICG单元report_clock_gating手动设置clock_gating属性并重跑CTS时钟树transition违规buffer扇出过大或位置集中report_clock_timing -type transition在spec中降低max fanout或加buffer阵列时钟延迟过大缓冲器级数过多report_clock_tree -detail调整spec限制级数优先用驱动大的buffer四核之间skew不平衡没有跨核group约束report_clock_timing -type skew手动建立skew group并设置group间强制平衡门控时钟路径大量hold violationICG位置太靠近叶节点report_clock_timing -type hold将ICG往时钟源方向移动或对门控分支加延迟CTS之后如果发现大量hold violation先别急着插delay buffer。很多情况下这是时钟门控带来的结构性问题检查ICG的物理位置是不是太靠后了——ICG离叶节点越近门控时钟路径越短数据路径上的hold修复就越困难。5.2 低功耗流程中容易忽视的检查低功耗设计的验证比普通时序收敛更讲究尤其是多电压域和电源关断相关的检查漏一项后面流片回来可能就是废片。我记得做这颗A7项目时第一次跑完voltage aware STA报告显示一条level shifter漏插。那条路径跨了0.9V域到1.2V域工具没自动识别到功耗分析显示多出了近20毫瓦的动态功耗泄漏。排查下来发现是UPF中这条跨域路径的约束没配对后来专门写了个脚本把跨域路径全部拉出来审核一遍才彻底解决。# 低功耗完整检查命令 # 1. 检查所有跨域路径是否有level shifter # 2. 检查断电源域的隔离单元是否都已插入 # 3. 检查电压域边界的pg pin连接 report_voltage_areas -verbose ./outputs/voltage_areas.rpt report_level_shifter ./outputs/level_shifter.rpt report_isolation ./outputs/isolation.rpt report_pg_pin_connectivity ./outputs/pg_pin_conn.rpt # Voltage-Aware STA # 需要读取CPF/UPF并为每个电压域指定对应的liberty库 read_power_intent -1801 upf -file inputs/low_power.upf commit_power_intent create_delay_corner -name func_0p9v -lib {lib_cpu_0p9v.lib} create_delay_corner -name io_1p2v -lib {lib_io_1p2v.lib}做完这些检查后功耗分析的结果才可靠。漏了一条level shifter功耗和时序数据全都是错的后面所有优化方向都会被带偏。5.3 关于Innovus命令日志的几个判断技巧最后分享几个实用的小技巧主要围绕log文件怎么看、怎么快速定位问题。CTS运行过程中关注log里有没有类似“CTS-500”这样的警告。CTS-500经常表示某条时钟路径找不到合理的平衡点如果出现这个警告建议先检查时钟源端到ICG的路径有没有阻塞通常是某条placement blockage挡住了绕线通道。另外report_clock_tree的输出里重点关注Leaf Cell Count这一列。如果一个group下面的leaf cell少得离谱那大概率是约束定义漏掉了某些寄存器对应的时钟根本没有被纳入这棵树。这种问题在A7四核设计中非常容易出现因为寄存器数量庞大SDC里一个create_generated_clock写错就会漏掉几百个寄存器。还有一个经验clock_opt_design跑完之后不要急着进入布线。把此时生成的def文件单独保存一份命名成post_cts.def。这个习惯能救命——后面如果布线阶段出现严重crowding或者时序大幅变差可以退回到CTS后的状态重新做优化完全不需要重新跑一遍placement和CTS一天的时间就这么省下来的。写在最后的几点体会做这个四核A7 SoC之前我其实觉得时钟树和低功耗就是流程里的两个步骤按部就班跑完就是了。但这一轮做下来感受完全不同——时钟树不是“跑完就完事”低功耗也不只是“挂了UPF就生效”两者在设计意图阶段就要协同考虑。skew定多少门控怎么处理关断域在物理上怎么摆放这些决策最终都体现在时序、功耗、面积的三角平衡里。个人体会最深的一点是后端的功夫很大程度上花在“目标设定”上而不是“工具操作”上。参数定得准、spec写得清楚Innovus跑起来又快又稳参数拍脑袋定工具再强也救不回来。脚本本身其实不复杂复杂的是脚本背后的思考和踩坑记录。大家把文中的框架拿回去结合自己的工艺库、标准单元、PPA目标调整参数多跑几个版本对比很快就能形成属于自己的后端设计套路。希望这篇实战记录对正在做类似处理器SoC后端的朋友有参考价值。
返回列表