核心概念与实施框架指南)
做数字后端这些年hierarchical flow层次化设计实现流程是绕不开的话题。只要芯片规模上去、时钟频率逼近极限纯flat铺到底的路子基本走不通。我最早接触hierarchical flow是因为一块接近两千万门的SoC平台工具跑place到一半内存爆炸之后同样的事情又因为congestion、因为时序收敛反复找上门。从那时候起我就意识到这套方法论不是可选项而是大芯片后端实现的基本功。这篇是这个系列的第一篇先不堆命令细节而是把hierarchical flow的来龙去脉、核心概念、适用判断和整体实现框架讲透。你手上如果有正在纠结要不要切hierarchy的项目或者入行不久想补上这块知识拼图这篇应该能帮你把思路理顺。1. 为什么纯扁平化实现在大芯片面前越来越吃力1.1 Flat flow的天花板容量、运行时间、收敛质量先回忆一下纯flat flow的流程把整个设计所有逻辑、所有memory、所有模拟模块一次性读入物理实现工具做floorplan摆单元时钟树综合布线然后签核。这套流程在小规模设计中非常直接有效规模在百万门上下时工具的运行时间、内存占用量、迭代效率都还在可接受范围内。可一旦设计规模突破某个阈值问题就接踵而至。首先是内存和运行时间。工具的物理优化过程涉及大量图算法——单元摆放、拥塞评估、时序分析、布线资源分配这些都是典型的计算密集任务。一两千万门的设计跑一轮place挂24小时甚至48小时是常态如果有比较严重的congestion或者setup violation一轮优化跑完还得human-in-loop介入再来一轮、两轮项目时间全部烧在等待上。其次是拥塞与布线资源问题。大芯片的面积并不完全跟逻辑规模成线性增长因为布线资源routing resource的密度会随面积利用率和宏单元分布急剧恶化。尤其是memory密集的设计macro之间的窄通道channel地方cell密度过高、局部congestion爆表工具在flat视角下很难做全局优化——它需要把整个芯片同时纳入考虑结果往往是局部问题反复修不动。最后是收敛质量。flat流程的时序分析天然包含所有路径理论上最完整但问题是设计中关键的跨时钟域路径、离得极远的block-to-block路径、穿过大片macro区域的路径工具需要大量迭代才能接近理想状态。实际项目中更常见的情形是修好了一个角的congestion另一个角又崩了为了让一条长路径勉强收敛牺牲了周围一票short path的cell sizing优化。这种低效的全局负优化是flat flow在大设计上的本质弱点——工具试图一次照顾所有东西最后处处平庸。1.2 项目节奏逼出来的路径选择除了技术层面的压力项目节奏也在逼着后端团队换思路。一个大规模芯片从RTL freeze到tapeout留给物理实现的时间窗口往往只有几个月。flat flow下一次完整实现迭代的成本太高——floorplan改一轮要重跑好几天ECO修一轮也要大半天任何上游的微小改动都会造成时间瀑布。而hierarchical flow天然支持模块级独立迭代某个block的RTL改动只影响该block内部top层面的重新实现成本远低于整体重跑。这种解耦带来的灵活性在多项目并行、tapout节奏紧张的现实里几乎直接决定了团队能不能按期交片。所以我的判断标准很简单设计规模超过一定量级、或floorplan复杂度到了一定程度、或迭代频率高到flat flow已经拖不住进度就应当主动上hierarchical flow。它不只是一个技术选型更是一个项目管理的策略。2. hierarchical flow到底在切什么逻辑层次、物理边界、数据接口三位一体2.1 同一个层次概念的三层含义很多人一听到hierarchical就以为是把RTL按module拆开分别实现但实际物理实现意义上的hierarchy远比这个复杂。它至少同时包含三个层面而且这三层必须配套运作整个flow才跑得通。第一层是逻辑层次logic hierarchy也就是RTL里module之间的嵌套关系。逻辑层次是天然存在的即便你不做physical hierarchy综合工具也会按它组织网表。但hierarchical flow的关键在于选择一个逻辑层次作为物理划分边界通常是把某个submodule指定为partition分区/物理块这个partition在物理上会被工具当作一个独立对象处理。第二层是物理边界physical boundary。这个才是切hierarchy之后真正多出来的东西。每个partition在芯片上占有明确的一片矩形区域有自己的电源环、自己的pin位置、自己的布局与布线约束。物理边界是后续所有block级实现、顶层集成、时序预算的基础它的形状、大小、外接pin分布直接决定这个partition甚至整个芯片的布局布线质量。第三层是数据接口也就是block与block之间、block与top之间怎么交换信息。这层最容易在项目初期被轻视但它往往决定了整个流程能不能收敛。物理实现工具在hierarchical flow中要处理的接口数据至少包括逻辑网表、物理约束floorplan、电源域、时序约束SDC、时序抽象模型ETM/ILM、物理抽象模型LEF/FRAM、功耗信息CPF/UPF。在实际项目中这些接口数据像契约一样框定了每个block的外部视图一旦某个抽象模型的精度不足到了top集成阶段问题会被放大到难以收拾。2.2 逻辑层次与物理层次并不需要严格一致实际工程里有一种很常见的误会以为选了某个逻辑module作为partition就必须把它下面的所有submodule都一股脑并到partition里。其实不然。在做physical hierarchy时你可以把一个逻辑module指定为partition同时规定它的若干子模块浮在top上即不上收、不合并甚至可以把逻辑上属于不同层次、物理位置上紧邻的模块合并成一个partition。块与块之间也不要求一一对应RTL文件工具允许你通过group的方式重新组织物理层次。我举个具体例子。一个设计的memory controller和它的DDR PHY逻辑上分属两个module物理上却必然贴在一起、由同一条总线连接。如果把这两个module各自独立成partitionpin之间的连接必须绕道top层布线资源浪费且时序极难收敛。实际项目中更聪明的做法是逻辑不动、物理上把两者划进同一个partition——在后端工具里这通常通过partition属性配合物理boundary设置实现。换句话说hierarchical flow的物理层次本质上是对逻辑层次的一次按物理需求重新分块。2.3 与flat flow的本质差异边界被显式化Flat flow里模块边界是隐形的——工具眼里只有一堆cell、一堆net边界只存在于注释里。而hierarchical flow一旦启用模块边界就成了工具必须尊重的一等公民pin要固定位置时序要按边界切分布线要经过特定通路buffer要摆在本块内部。这意味着优化空间在边界处被显式收窄换来的是各模块可以并行做局部优化的巨大自由。这种显式化边界并行局部优化的组合有点像软件工程里模块化——单模块的代码可能为了接口干净而多写一点封装逻辑但整个系统因此获得了分模块编译、独立团队开发的能力。芯片后端也一样你付出一定的面积和时序预算作为接口税但换来了整个项目可以把十几个工程师分散到不同block上同时干活换来了每个block只花两天就能跑完一轮place的迭代速度。这套取舍在小设计里不划算在大设计里没得选。3. 两种主流实现路线bottom-up的务实与top-down的全局观3.1 Bottom-up先把每个block做到极致Bottom-up hierarchical flow的思路很直接先选择partition边界让每个partition独立完成从floorplan到晚签核或者至少到post-route signoff的物理实现产生各自的物理实现数据和抽象模型然后在顶层把这些抽象模型拼起来只对top层剩余的logic顶层逻辑、IP、IO pad做实现最后做整芯片的signoff。这套路线是业界用得最广的原因是它足够务实并行度高。每个partition的工程师自扫门前雪不需要频繁等待彼此结果。局部收敛质量好。因为每个partition只在自身规模和边界内做优化工具计算压力小能细抠局部congestion、功耗、时序。迭代隔离性好。一个partition出了问题不会像多米诺骨牌一样牵连全局。面积与布局可控。Macro密集的block自己完整布局不会被外部环境过度扰动。但它也有明显代价。最突出的是top层pin assignment与budget问题——partition的边界时序预算怎么定直接决定这个block能不能收敛。预算给宽了top层到处是长路径、top修不动预算给窄了block内部为了满足假时序要求疯狂加buffers面积爆炸、功耗上涨。此外partition的边界pin位置是提前定的如果top层实际绕线后发现有更好的pin位置可能已经来不及让block重新实现只能通过ECO硬顶。这个流程的典型适用场景是设计规模很大、block之间时序路径相对干净、团队组织天然按模块划分。3.2 Top-down从全局蓝图倒推每个blockTop-down的思路则相反在物理实现早期就做全局floorplan甚至做一个低精度的全芯片预布局trial place跑全局拥塞和大致时序分析再基于这些全局信息给每个partition切pin位置、定面积、做时序预算后续再要求每个partition依据这份预算书独立实现。这种做法更理想主义因为预算不是猜出来的而是靠一个全局参考做出来的所以整体收敛性更好。但top-down对工具能力要求很高需要工具能高效处理设计中一部分是模棱两可的abstract模型一部分是真实逻辑这种混合场景。ICC2和Innovus这些主流工具在近几代版本里对top-down流程的支持已经越来越成熟但也意味着流程复杂度更高、脚本体系更重、出问题的概率更大。Top-down适合的场景是设计规模巨大block之间交互密切pin和budget如果靠人工经验拍板top集成阶段必然翻车。3.3 实际项目中的混合姿态现实没有教科书那么干净。我做过的大多数项目严格说都不是纯bottom-up或纯top-down而是两者混搭。例如某些memory密集的block因为布局太敏感会先用bottom-up的思路手工定floorplan、手工做pin assignment另一些逻辑密集、交互频繁的block则走top-down的预算流程由工具先给参考方案再人工微调。又比如项目早期用top-down做全芯片可行性评估哪些block做出来大概多大、pin大概怎么布确定大方向后再转入bottom-up并行实现。这种战略上用top-down定全局战术上用bottom-up管落地的混合姿态往往才是真正解决工程问题的方式。4. 第一次切partition前必须想清楚的五件事4.1 划分的粒度切太粗等于没切切太细等于自找麻烦Partition粒度的选择是第一件要慎重的事。常见的判断维度有三个cell总数、面积和运行时序路径密度。从工具能力角度单个partition的规模最好控制在工具能轻松处理的范围内——通常单partition在几百万门以内时place和CTS的速度都还能保证超过一千万门单个block的迭代优势就不明显了。另一个经验是看时序路径密度如果某块区域内的路径大量跨过假设的逻辑边界那切分的意义就会大打折扣因为budget估算误差会在这种区域被迅速放大。还需要考虑项目团队的组织形式。如果你们本来就是按功能模块分工——一个工程师负责DDR子系统另一个负责USB子系统——那物理partition就应当尽量贴合团队分工避免同一个物理块被多个team同时修改不然版本管理和interface数据同步会变成灾难。4.2 Pin assignment最容易被低估的一环在hierarchical flow里pin assignmentPin规划/IO pin分配是一个看起来简单、实际上极影响成败的设计决策。pin的位置会影响block内部的拥塞分布、影响top层绕线长度、影响时序收敛而且它是在流程早期就要定的、后期几乎没有return机会的参数。我踩过的一个大坑是一个DDR controller的partition当时为了迁就顶层的floorplan把两百多个数据pin全部压缩在block的一侧结果block内部为了把这些pin送到对应memory位置上白白多出了大量绕线局部congestion爆炸setup也收敛得很痛苦。后来重新规划pin让数据pin跟着各自对应memory的物理位置散开分布block内部的绕线负担立刻降了下来。所以做pin assignment时我的建议是follow这些原则pin分布要与外部连接点的物理位置对齐减少top层绕线同一总线的pin尽量分组且靠近方便顶层布线和避让高切换活动率的pin例如时钟、高频总线要避开拥挤区域和IR-drop敏感区域并尽量避免把大量pin压在partition的短边上因为短边上的pin密度一旦过高block内部布线资源会被瞬间耗尽。4.3 时序预算与接口约束预算不是拍脑袋Timing budgeting本质上是在回答一个问题一条穿过三个partition的路径A块、B块、C块各应分摊多少延迟裕量预算方法直接决定block内部优化方向和收敛难度。工程中比较实用的做法是先用top-down的全局预布局跑一遍虚拟实现拿到完整路径的实际延迟分布再把这些延迟归一到各partition的接口约束上。这个过程通常由工具的半自动budget流程完成但人工也要参与。我通常还会把预算稍微加重在比较好修的block上——逻辑密度低、布线宽松的block多做几个buffer不心疼相反congestion严重的block预算要尽量给足因为你的cells在那边光是走路都困难遑论加裕量。另外必须强调接口约束的自动化检查。很多top集成阶段的时序violation根源是block交付的接口模型与预算假设不一致。所以每个block在交付抽象模型时必须同步输出预算和实际实现后的violation报告由top team逐条比对、归类不能等集成后发现问题才追责——那时候代价已经是几天工时。4.4 面积预估给floorplan留出活着的空间每个partition需要的面积必须在切分之前有相对准确的估算。估算方法有三种一是基于综合后的cell面积加一定percentage的inflation二是直接跑一个快速place实验得到真实利用率三是参考上一代产品的同规模模块数据。三种方法各有优劣但我的经验是综合后的cell面积打底、再用place实验校核最稳妥。留空间这件事也很讲究。以前我总想省钱把partition面积压得很紧结果block内部的利用率看着合理实际布完线后发现channel区域宽窄不一CTS的buffer塞不进去整体晚收敛。后来我养成了面积宁多勿少的习惯——floorplan初期就统一规划5%到10%的routing margin专给clock tree的buffers、post-route ECO、迟来的时序修复用。这些margin看似浪费die area但它们能换来整条hierarchical flow的稳定收敛这笔账是划算的。4.5 电源与时钟贯穿partition内外的两条大动脉切partition时电源/地网络和时钟网络往往容易被当成top层的事而忽略但它们恰恰是hierarchical flow中跨层协作最频繁的部分。电源方面ICC2/Innovus的hierarchical flow都要求先做gird规划每个partition需要有明确的电源ring、straps连接点、以及电压域信息。这里的关键是block内部的power grid要到什么密度层级密度太低block内部IR-drop不过签核密度太高又挤占了布线资源。通常做法是top层先把ring和主要straps打好block内部再补到满足自身电流需求的密度最后由top层做IR-drop signoff时统一看整体效果。时钟方面更考验功底。Partition的时钟树如何平衡有些flow选择在每个partition内部独立做CTS局部树再在top做跨partition的平衡与uncertainty补偿也有流程用global clock trunk local leaf tree的方式先把主干时钟从时钟源送进每个partition的入口寄存器再接内部分支。两种方式各有适应场景核心是必须定义清楚跨block的clock skew怎么算、uncertainty怎么留。一旦切换partition边界时时钟树的逻辑层级定义不清整个design的时序窗口全乱返工成本极高。5. 切完之后才是真正的战场block实现、抽象模型与top集成5.1 Block级实现收敛标准必须高于top需求Block级实现是hierarchical flow中工作量最大也最可控的阶段。既然我们已经把物理边界、pin、budget都定好了剩下的就是在有限边界内把设计做到尽可能好。这个阶段的设计收敛标准不能只是满足预算而是要做得比预算更好——因为block交付多出来的裕量最后会成为top层应对unmodeled效果和ECO的缓冲。我的习惯是给每类block内部额外加上50到100ps的时序收敛裕量。这个增加值听着不多但它消化了abstract模型与实际signoff工具之间的差异尤其是ETM片上时序模型分析时的建模误差。实践经验告诉我凡是block交付时刚好满足预算的项目top集成的头两周必定是在救火凡是每个block都主动多留一点裕量的项目top走的相对顺。5.2 抽象模型不能只当作仿真玩具Block完成物理实现后需要为top层生成抽象模型。这部分是hierarchical flow中最容易被技术债反噬的环节。当下主流的抽象模型包括ILMInterface Logic Model接口逻辑模型保留block输入/输出端的接口寄存器以及端口到寄存器之间的逻辑与路径同时把内部单元吞吐掉。ILM的精度比传统ETM高因为在接口附近的分析仍然是基于真实单元的。ETMExtracted Timing Model完全黑盒化的时序视图分析速度快、保密性强适合第三方IP或者跨团队交付但建模误差更大需要更保守的uncertainty假设。FRAM/LEF这类物理抽象则是top层place route时识别block物理位置和pin坐标的依据必须与block最终版图完全一致否则top工具直接崩或者连线对不上。这里最容易被忽视的一个知识点是抽象模型不是一劳永逸的。Block内部做了ECO、改了pin位置、调整了电源ring都必须同步重新生成抽象模型并确保top层同步更新否则后边所有基于旧模型的分析都在沙滩上盖楼。我经历过一次block已经ECO定稿但top还在用半个月前的ILM走布线的乌龙等发现时已经白跑了两天flow。从那以后我就在项目里强制加了一道流程每个block每次交付都必须附带模型版本号和生成时间戳top team收到后第一件事是跑LEC/formal校验逻辑一致性确认无误再进流程。5.3 Top集成越到最后越考验silkscreen功夫Top集成的过程看起来只是把已有block摆好、把顶层逻辑布完线实际上它是整个hierarchical flow技巧最密集的环节。Top层往往还有IO cells、顶层逻辑、千奇百怪的assemble宏单元以及大量需要在顶层绕线的连接。这些顶层绕线通常会主动绕开block内部因为block内部区域已经不是top工具直接可编辑的空间了。我遇到过最典型的top集成问题是preCTS阶段看起来一切正常结果CTS一跑跨block的时钟路径skew大得离谱。原因是在top层做时钟树综合时工具无法自动穿越block内部的布局障碍导致它必须绕远路送时钟而每个block内部的树结构又各不一样skew自然爆炸。解法是在block实现阶段就管好clock entry point的设计——提前规划好每个block的时钟入口寄存器和树根位置甚至在一些critical clock上做latency balancing而不是把问题全部抛给top CTS。另外top集成阶段还需要防着DRC/LVS在边界附近的反攻。Partition边界是hierarchical flow里最容易出现bulk DRC、断pin、绕线间距违规的地方——因为两个block分别实现时谁也不会替对方考虑边界内的金属密度。所以我的习惯是top floorplan出来后尽早跑一次跨block boundary的DRC预检即使是early stage的draft数据也能提前暴露冲突区域避免最后关头才在LVS攻关期被拖住。5.4 ECO流程也要跟着分而治之功能ECO、金属ECO在hierarchical flow里同样要拆解。一个ECO如果落在单个block内部那基本由该block的工程师独立完成、独立验证后重新生成抽象模型如果ECO涉及多个block外加top逻辑那就需要前后顺序合理分配——通常先做block内部ECO再做top集成最后统一跑整芯片drc/lvs/timing signoff。这里的经验法则是ECO的影响半径决定工作流程绝不图省事直接在top上对所有相关模块一起跑ECO那样会把hierarchical flow省下的时间全部还回去。6. 什么情况下你会后悔用了hierarchical flow6.1 过度划分的代价我必须说hierarchical flow不是灵丹妙药。如果设计规模其实不大、全芯片一阵就能跑完强行划成十几个partition只会徒增大量接口管理、模型生成和跨团队对齐工作。我见过有人为了展示方法论跟上潮流把一个本来flat flow两个晚上就能收敛的设计硬切成八个partition结果光是pin协调和budget对齐就比flat多花了三周时间——纯粹是自找麻烦。6.2 模型与真实逻辑不匹配时top集成会变成无底洞另一个会让人崩溃的情况是抽象模型精度与真实逻辑不匹配尤其是在top CST、STA与block实现时使用不同toolsets的跨工具流程中。工具A的ETM给top用工具B做block最终signoff两者对path delay的计算天然有差距。这种差距在早期可能只有几十ps积累到top上就可能变成几百ps的signoff violations而且定位极难——你不知道问题是出在模型上、budget上、还是某个block内部的实现上。所以我坚持一个原则流程搭建阶段先跑一个bottom-up signoff对比实验——把全部block最终版图与真实逻辑放到一起做一次完整全芯片STA和单纯用抽象模型搭建的top STA结果对比量化模型误差再决定顶层分析的保守度。6.3 组织协作的反噬最后一点容易被很多人忽略hierarchical flow改变的不仅是技术流程更是团队的协作方式。一旦启用多block并行流程每个block的交付物、版本、模型、约束必须高度一致。任何team A改了自己的SDC没同步给team B的微小失误最后都会演变成top层的重大时序问题任何block的floorplan改了没通知top team的心思都会让top集成阶段的floorplan billboard和connectivity check变得千疮百孔。越是大型设计越是需要依赖版本管理、自动化回归和验收清单而不是人的同步意识。从评估是否启用hierarchical flow到真正让这套流程跑稳是一个需要不断浇灌耐心和实践的过程。这篇先把我认为的基础知识地图画清楚为什么需要它、它切的是什么、有哪两条路线、切之前要解决哪些问题、切完之后日常是什么样。下一篇文章我打算拆一个真实的partition案例从floorplan初版到pin assignment再到budget发布把具体操作的每一步、每一条命令、每一个排查过程摊开讲。到时候你会发现纸上谈兵和实际处置之间的距离往往只隔着一堆让你捶胸顿足的细节。