Multi-bit触发器MBFF后端设计实战:从综合到布局布线的全面指南
1. 为什么非要折腾MBFF功耗、面积与时序的三重账第一次接触MBFF是在一颗28nm的MCU上。综合跑完面积比预期多出3%功耗更是超标不少。那时候我做后端没多久第一反应是调整floorplan完全没有想到问题竟然出在寄存器本身的物理结构上。后来被资深工程师点了一句“你把设计里的单bit触发器统计一下看看有多少能合并成multi-bit。”我才真正开始系统接触Multi-bit触发器MBFF这条优化路径。1.1 单bit触发器重复造轮子的浪费绝大多数RTL工程师在写代码时习惯用always (posedge clk)描述一组寄存器一个bit对应一个触发器。这个习惯本身没有问题但落到标准单元库里每个单bit触发器背后都有一套完整的“配套设备”独立的时钟输入端、独立的复位端、独立的内部反相器、独立的驱动管。这些配套设备在每个单bit触发器里都是重复的。做个生活化类比一个办公室有20个人如果每个人都在自己的工位放一台饮水机那需要20台饮水机、20套水路、20份电力维护。更好的做法是集中放两三台大饮水机大家排队接水就行。MBFF做的事情就是“共享饮水机”——把多个bit的时钟反相器、复位逻辑、功耗开关等共用起来只保留每个bit自己的数据存储部分。这颗28nm MCU里的寄存器数量大概在50万bit左右其中大量是按字节组织的控制寄存器、状态寄存器、FIFO指针天然具备合并条件。如果全部换成2bit或4bit的MBFF单元共享逻辑的比例相当可观面积和功耗都能省下一大块。1.2 MBFF在物理上到底长什么样标准单元库里的MBFF常见的有DFQD1BWP这类2bit触发器、DFQD2BWP这类4bit触发器命名规则各家略有差异但思路一致一个单元内部包含2个或4个相同的触发器bit它们共享时钟、共享复位、共享电源和地内部还有一个共用的时钟反相器网络。与单bit触发器相比MBFF的高度通常更高占用的row数更多。比如单bit触发器可能是9轨高度一个4bit MBFF可能是12轨或者更高。这意味着在布局阶段MBFF无法和普通单bit单元挤在同一行里它需要跨越多个标准单元的row这对后续legalization和congestion都有直接影响。正因为物理尺寸更大MBFF在布局阶段不像单bit触发器那样“见缝插针”。它更像一块小号的宏单元摆下去之后周边一段区域内的布线通道都会受影响尤其是M1、M2层的资源。很多项目在MBFF比例超过30%后congestion会突然恶化原因就在这里。1.3 什么时候该用、什么时候该收手MBFF不是万能的。我在一个项目里踩过这样的坑为了追求面积把大量单个bit互相无关的触发器强行合并成4bit MBFF结果布局阶段发现congestion爆表布线绕线严重时序反而变差了最后不得不回头拆掉一部分合并。合并的前提是这些bit在逻辑上密切相关最好满足以下任一条件是同一个寄存器的连续bit位比如reg[3:0] data是同一个控制信号的多个状态位比如一组使能信号数据更新条件相同拥有相同的时钟门控或使能条件。如果两个bit的数据路径完全不相关、更新时机不同、使能条件不同强行合并之后只会让综合工具在MBFF内部增加额外的数据选择逻辑得不偿失。另外DFT可测试性设计也要提前沟通很多MBFF单元对scan chain的连接方式有限制如果RTL里已经规划好了scan chain顺序合并策略会牵一发动全身。2. 综合阶段的MBFF合并不是靠RTL写出来的MBFF的合并发生在综合阶段RTL本身不需要任何改动。综合工具会读入标准单元库中的MBFF单元根据RTL中寄存器的逻辑关系、库单元的面积和时序信息自动决定哪些单bit触发器可以合并成一个多bit触发器。2.1 工具怎么判断“可以合并”在Synopsys Design Compiler里做MBFF合并的核心逻辑是看两个条件逻辑兼容性和物理可行性。逻辑兼容性指的是两个或多个寄存器的时钟端、复位端、置位端必须完全一致而且数据输入不能相互依赖。比如reg[3:0] a和reg[3:0] b如果它们的赋值条件都是同一个enable信号那就可以考虑合并成一个8bit的MBFF或者合并成两个4bit MBFF。物理可行性指的是目标库里必须有对应bit数的MBFF单元而且综合出来的面积、时序不能比合并前更差。工具会遍历所有可用的MBFF单元计算合并前后的面积对比、时序余量变化然后决定是否执行合并。我自己用DC做28nm项目时常用的流程是这样的# 开启寄存器合并 set_register_merging_options -allow_retime true -max_bit_width 4 compile_ultra -timing其中-max_bit_width 4限制最大合并宽度为4bit。这个参数很关键无脑合并成8bit甚至16bit在物理上未必划算因为MBFF宽度越大单元内部的布线拥塞越严重而且一旦需要ECO工程变更单修改其中一个bit整个大单元都要跟着动。Cadence Genus流程中类似的做法是set_db design:work .merge_registers true syn_generic syn_map syn_opt两边工具的选项名字不一样但要表达的核心意思是一致的让工具在综合阶段自动寻找可合并的寄存器组用MBFF单元替换。2.2 库文件里的学问not used列表就是“封杀令”综合阶段有个容易被忽略的细节库文件里的dont_use属性。如果lib文件里MBFF单元没有标注dont_use工具默认会尝试使用如果标了dont_use工具会完全忽略这些单元即使它们在面积和功耗上更有优势。我在一个项目里遇到过很奇怪的现象同样的设计同样的约束两版综合结果面积差了8%。排查到最后发现是第二版SDC里多了一句针对MBFF单元的set_dont_use命令而写这句命令的工程师是想“暂时禁用”某个有问题的4bit单元结果整类单元都被封杀了。所以库文件检查是MBFF综合的第一步。我的习惯是# 查看当前设计里MBFF单元的使用情况 report_cell_usage | grep DF.*Q.*D.*BWP确认哪些MBFF单元是允许使用的、哪些被dont_use封杀了。如果发现某个bit宽度的MBFF时序有问题不要整个disable而是单独disable那一个有问题的cell保留其他可用的。2.3 合并后网表的检查formal verify不能省MBFF合并会改变网表中的寄存器分组关系但逻辑功能不应该改变。综合完成后一定要跑形式验证确认合并前后的网表逻辑等价。这里有一个容易踩的坑set_register_merging_options -allow_retime true里的-allow_retime会让工具在合并的同时进行寄存器重定时优化也就是允许寄存器在不同逻辑层级之间移动。这种移动虽然能改善时序但会给形式验证增加不少难度。如果项目对formal verification要求特别严格建议先关掉-allow_retime只做纯粹的寄存器合并。另外合并后的网表里寄存器名称会发生变化。原来RTL里的信号名data_reg[0]、data_reg[1]可能被合并成一个名为multibit_reg_inst的单元内部bit。在做ECO或查询信号时需要适应这套新的命名规则否则会出现找不到信号的情况。2.4 综合阶段最令人困惑的面积反弹MBFF明明是省面积的但有时候综合报告里面积反而变大了这是怎么回事我第一次遇到时也懵了。后来仔细分析才发现问题出在“面积反弹”上合并后的MBFF单元虽然本身比多个单bit加起来的面积小但MBFF的物理尺寸更大工具在综合阶段无法准确预估布局布线时的绕线资源消耗于是会插入更多的缓冲器和反相器来保证时序收敛这些额外插入的cell往往抵消甚至超过MBFF本身省下的面积。解决办法是综合阶段把时钟约束适当收紧一点给布局布线预留更多余量同时关注congestion报告。如果综合报告里的MBFF合并率已经很高但面积没有明显下降需要回头检查是不是因为大量MBFF周围的时序缓冲器太多了。3. 布局阶段的地盘之争density、congestion与legalizationMBFF从综合进入布局后真正的技术活才刚刚开始。综合阶段看到的只是面积和功耗的数字变化布局阶段看到的是物理空间上的冲突。3.1 MBFF在版图上的物理形态大块头的碰撞以我们用的28nm标准单元库为例普通单bit触发器的尺寸大概是1.4μm宽、2.0μm高而一个4bit MBFF的尺寸达到了2.1μm宽、3.2μm高几乎是单bit触发器的两倍多。这个尺寸变化意味着原本可以在同一行里连续摆放多个单bit触发器的区域现在只能放一个MBFF旁边还会留下不规则的空隙。这些空隙如果太小其他标准单元塞不进去就会造成更多的空隙碎片降低整体的cell利用率。所以MBFF比例高的设计布局阶段的Density利用率目标不能照搬单bit设计通常要降低5%到8%。3.2 place阶段的第一个坑density与congestion互相拉扯我在一个40nm项目里遇到过这样的场景综合后MBFF比例大约25%我按老经验把Density目标设成0.75结果place之后congestion报告一片红特别是几个大的MBFF集群附近M2、M3层的overflow高得吓人。分析之后找到原因MBFF内部的两个bit之间是有物理隔离的它们之间的布线通道不能走任何互联线相当于在版图上放置了一个“内部阻塞区”。如果多个MBFF挤在一起这些阻塞区叠加起来就会把周边的布线通道全部堵死。这就像晚高峰的地铁站站台人多还可以走动但如果有人在站台中间堆了几面墙人流就直接卡死了。处理办法是在place阶段就把MBFF的摆放打散不要让他们形成大面积的连续堆叠。具体操作有两种思路一种是在floorplan阶段预先规划placement blockage把一些区域设置为MBFF禁止摆放区强制工具把MBFF分散到芯片各处另一种是在place时提高congestion优化力度place_opt -congestion -effort high -max_density 0.68实测下来把Density从0.75降到0.68congestion问题基本缓解时序余量反而因为布线通畅而变好了。3.3 用“防爆区”的思路处理MBFF摆放密度沿用刚才“地铁站墙”的比喻我在后来的项目里摸索出一套比较实用的做法给MBFF集群设置虚拟的“防爆区”。具体做法是在floorplan阶段先把设计中MBFF数量比较密集的区域圈出来用工具命令在这个区域周围设置一圈placement halo让其他普通单元不能紧贴着这一圈摆放给MBFF集群留出足够的布线空间。# Innovus中给选中的MBFF实例添加halo selectInst [get_cells *multibit*] add_placement_halo -halo 2.0 2.0 place_opt这里的2.0表示在MBFF实例周围留出2微米的摆放禁布禁止区域。虽然这会让局部密度看起来更高但实际布线资源反而更宽裕了。这是因为MBFF本身占用的M1/M2资源很少真正的布线压力来自周边信号线的绕线需求留出这些区域后绕线拥堵得到了有效缓解。3.4 legalization与pre-placement检查清单MBFF因为高度比常规单元高legalization阶段需要额外注意确认MBFF是否落在正确的row上如果有旋转或翻转到非标准row位DRC会报错检查MBFF与邻近单元之间的间距是否满足最小间距要求特别是同高度但不同row对齐方式下确认电源/地轨连接是否正确部分MBFF单元因为高度更高需要的电源轨连接方式与单bit不同。我一般在place完成后会跑一个快速检查# Innovus中检查放置合法性 check_placement如果MBFF翻转方向不对、row对齐错误这个命令会直接报出来。这里补充一个经验不要等到CTS阶段再检查placement那时候改起来成本高得多。4. CTS与布线阶段容易被忽视的坑很多文章讲MBFF都到place就结束了但实际上CTS和布线阶段才是问题集中爆发的时候。MBFF对时钟树的影响被严重低估了。4.1 时钟树上MBFF的“共享时钟pin”MBFF的时钟端在物理上是共享的一个MBFF单元内部所有bit共用一个时钟pin、一条内部时钟网络。这对CTS来说有好处也有坏处。好处是时钟树上的负载点数量大大减少。一个4bit MBFF只贡献1个clock pin load而4个单bit触发器贡献4个。这样一来时钟树的级数可以减少latency和skew都有改善。坏处是MBFF内部的共享时钟网络会把“时钟到达时间差异”进一步放大。因为MBFF内部从时钟pin到每个bit的时钟端距离不同会导致同一个MBFF内部不同bit的clock latency有微小差异。虽然这个差异通常在皮秒级别但在高速设计中它直接影响hold timing的分析精度。我在一个7nm项目里就因为这个内部skew吃过亏。一个16bit的MBFF内部从时钟pin到最远bit的距离比到最近bit的距离多了将近12ps。在时钟频率1.2GHz的设计里12ps的偏差足以让一组本来看起来没问题的hold路径出现数百条违例。解决办法是在CTS阶段对MBFF的使用情况做特殊处理不要让工具把MBFF当成普通单元自动优化。我通常会先手动约束MBFF的时钟树延迟再运行CCopt或CTS工具# CCopt中为MBFF时钟pin设置目标延迟 set_ccopt_property -pin_filter [get_pins *multibit*/CK] target_skew 20ps ccopt_design这样可以让工具针对MBFF的时钟端做额外的skew优化降低内部偏差。4.2 hold fix与incremental的边界问题MBFF在hold修复阶段有个很麻烦的特性如果原设计里两个bit之间的路径存在hold问题合并成MBFF后这两个bit的物理距离从“可能很远”变成了“非常近”这对setup修复是好消息因为路径变短了但对hold修复来说反而是坏消息因为时钟到达时间差异可能变大hold余量变得更紧。更麻烦的是如果MBFF内部的某个bit需要插入hold buffer这个buffer必须放在MBFF单元外部因为MBFF内部是无法插入任何单元的。这会导致本来很简单的一次hold修复变成在MBFF附近找地方插buffer如果附近空间紧张还得先移动其他cell腾位置。我遇到过一次这样的场景一个4bit MBFF的bit0和bit1之间的hold违例有150ps需要在bit0的数据路径上插入一个delay buffer。原本以为很简单结果发现MBFF周围全是高密度的标准单元连一个能放buffer的空位都没有。最后只能通过挪动周边几个不相关的cell才把buffer插进去前后花了两个小时。这类问题的最佳解决思路是前置防御在综合阶段就把MBFF的比例控制在一个合理范围内不要在数据路径敏感的场景里大量使用高位宽MBFF。4.3 布线后的EM/IR风险MBFF还有一个不容易察觉的风险IR drop。因为多个bit共享电源和地一个MBFF在工作时如果多个bit同时翻转瞬间电流会很大在这个单元的电源网络上造成明显的电压降。我在一个项目里跑IR drop分析时发现某个4bit MBFF在极端工作条件下的IR drop超过了5%的阈值被redhawk标红。原因就是那个MBFF的4个bit在同一个时钟沿全部翻转瞬间抽走了大量电流而周边的电源网格密度不足以支撑这么大的瞬态电流。处理方案有两种一种是在floorplan阶段提前在MBFF密集区域加强电源网格密度增加额外的M4或M5电源走线另一种是在布局阶段把这些高翻转率bit的MBFF分散到不同区域避免多个高功耗MBFF挤在同一片电源网格下。我个人更推荐第二种做法因为电源网格的修改会影响整个芯片的供电规划牵涉面广而MBFF摆放位置的调整相对容易。前提是要在前端提供的数据里拿到每个寄存器的翻转率信息提前把高翻转率bit标记出来。5. 实测收益一组可以当参考的数据说了这么多理论到底实际效果如何我把自己最近几个项目的MBFF数据整理了一下分享给大家做参考。5.1 面积、功耗、时序的对比指标不使用MBFF使用30% MBFF使用60% MBFF面积100%91%86%总功耗100%92%85%时钟树级数1297CTS后时钟skew65ps48ps39ps布线后congestion中等中等偏高总布线长度100%95%93%这组数据来自一颗28nm Cortex-M级别芯片工作频率800MHz逻辑规模约200万门。可以看到面积和功耗的下降是明确的时钟树质量也有改善但congestion在60% MBFF比例时已经开始出现压力。5.2 数据背后的逻辑为什么绕线资源节省更多仔细观察表格会发现面积节省只有14%但总布线长度节省了7%。这个现象很有意思。原因是MBFF不仅合并了寄存器件本身还合并了连接到这些寄存器的局部网络。比如4个bit如果共用同一个时钟门控那这个时钟门控就只需要驱动1个MBFF的时钟pin而不是4个单bit触发器的时钟pin。时钟树上的走线少了一大截总布线长度自然下降了。另外数据通路上也有类似效果。如果一个数据位宽的寄存器原本要从ALU输出一路接到寄存器输入合并成MBFF后这些数据线会归拢到同一个MBFF周边绕线长度更集中布线资源利用效率更高。5.3 和FPGA布局布线的区别这部分也回应一个经常被问到的问题“FPGA里不是本来就有很多触发器成组排列吗为什么搞ASIC还要专门讲MBFF”FPGA里的触发器布局确实天生是“成组”的一个CLB可配置逻辑块内部包含多个FF这些FF共享时钟资源物理位置紧邻。但FPGA的FF数量是固定的不存在“合并”的概念——反正每个LUT里已经预置了固定数量的FF用不用都占着资源。而ASIC的MBFF是库层面的物理优化需要综合工具在逻辑和物理之间做全局权衡。它更灵活可以根据设计需求选择不同的bit宽度但也把物理约束问题提前带到了综合阶段增加了全流程的复杂度。这也是为什么MBFF优化不能只看综合报告必须从综合一路盯到布局布线全流程联动。6. 我踩过的坑和一套可复用的实战清单文章最后分享一个我在MBFF项目里印象最深的踩坑经历以及我现在每次做MBFF项目都会检查一遍的实战清单。6.1 一个让我通宵排查的时钟问题那是一个16nm的项目芯片已经跑通了CTS时序报告看起来一切正常但前仿真验证时发现部分寄存器数据在指定时钟沿之后出现了异常翻转。花了整整一个晚上排查最后才发现问题出在一个8bit MBFF上——这个MBFF的时钟pin到内部第7个bit的时钟端距离太远导致该bit的时钟延迟比其它bit大了将近30ps。在做时钟树综合时工具默认认为MBFF是一个“单个负载”没有对内部bit的延迟差异做精细修正最终导致了这个bit的建立时间不足。后来我养成了一个习惯CTS之后专门抽查MBFF的内部时钟延迟确认工具报告的CK pin到内部各bit的延迟差异是否在可接受范围内。如果差异过大说明这个MBFF要么需要降位宽要么需要手工调整时钟树约束。6.2 每次做MBFF项目都会过的检查清单下面这个清单是我做完几个MBFF项目后总结出来的权当一份“抄作业”材料RTL阶段确认确认待合并的寄存器组是否满足逻辑兼容性数据路径无互依赖使能条件一致。综合阶段确认检查库文件中MBFF单元的dont_use设置确认允许使用的MBFF位宽范围跑完综合后用report_cell_usage确认MBFF的实际使用比例。形式验证确认合并后的网表和原始RTL跑formal verify确保逻辑等价。floorplan阶段确认对MBFF密集区域设置halo分散布局避免大块MBFF堆叠形成布线空白区。place阶段确认关注Density与congestion的平衡必要时降低Density目标place后跑check_placement确认MBFF的row对齐和翻转方向。CTS阶段确认检查MBFF内部时钟pin到各bit的时钟延迟差异确认在可接受范围内。hold fix阶段确认如果需要在MBFF外部插入hold buffer提前检查周边空间是否足够避免边插边搬cell。IR drop确认对高翻转率bit聚集的区域做IR drop分析有问题及时调整摆放。布线完成后确认跑DRC和LVS特别关注MBFF单元的电源地连接和DRC检查因为高位宽MBFF的物理尺寸异常容易在某些角落产生间距违例。6.3 关于工具版本和库版本的配合最后提醒一点MBFF的使用效果和工具版本、库版本高度相关。同一个设计用DC 2016版和DC 2020版综合出来的MBFF比例可能差很多因为新版本工具的合并算法更激进对congestion的感知也更强。同样的不同工艺节点下的MBFF单元设计差异也很大。28nm时代的MBFF单元相对简单内部bit数量通常只有2到4个到了7nm、5nm出现了一些8bit甚至16bit的MBFF单元内部的共享时钟网络设计更复杂但也带来了更大的物理风险。所以拿到一个新的工艺库不要急着直接上高位宽MBFF先做一个小规模测试模块分别用不用MBFF、用不同的MBFF位宽策略跑一遍看看面积、功耗、时序、congestion四类指标的变化趋势再决定正式设计里怎么定MBFF策略。这个“先试水再铺开”的思路帮我避免了好几次大改方案。