
直接说结论在纳米级工艺下时钟网络功耗能占到芯片总功耗的30%到50%而Multi-bit FF简称MBFF恰好是冲着这块最硬的骨头去的。我这两年先后在几个28nm和16nm项目里反复折腾DCT/DCG这套物理综合流程专门做多比特触发器的替换和优化期间踩过的坑比想象中多得多。这篇东西就把整个SPG流程完整拆开讲一遍从库检查、约束准备、命令实操到翻车现场你能想到的和想不到的都在里面。1. 开跑前先搞懂DCT/DCG、MBFF、SPG到底在干嘛1.1 MBFF是什么为什么它值得你花时间多比特触发器Multi-bit Flip-Flop说白了就是把多个功能独立的单比特触发器在物理实现上合并成一个共享阱区、共享内部驱动电路和时钟负载的单元。它不是一个新发明的逻辑功能而是对现有寄存器的物理重组。传统的单比特触发器阵列每个FF都有自己独立的时钟引脚、电源地连接和驱动电路放到版图上就是一堆重复的器件边界面积浪费很明显。MBFF把这些公共部分合并之后单个bit的有效面积会小不少而且时钟信号只需要驱动一个共享的内部时钟缓冲器时钟网络上的负载电容大幅下降动态功耗也就下来了。我在一个实际的28nm项目里统计过使用4bit MBFF替换等价单比特触发器之后寄存器区域面积大约减少了12%到18%时钟网络功耗下降了20%上下。这个收益对功耗敏感的IoT芯片或者移动芯片来说完全值得专门拉一条流程来做。但MBFF不是免费的午餐它的物理尺寸更大、排列更不灵活如果后端布局时周围空间紧张反而会造成摆放困难和布线拥塞。所以MBFF优化必须是个物理感知的过程不能闭着眼睛在逻辑网表里做等价替换这也是为什么我们今天要聊DCT/DCG而不是普通综合。1.2 DCT和DCG是同一个东西吗很多刚入行的朋友容易把DCT和DCG搞混其实它们的核心引擎是同一套都是Synopsys Design Compiler家族里带布局感知能力的物理综合环境。DCT全称是Design Compiler Topographical强调的是地形模式——它在综合阶段就会读入floorplan信息在内存里建立一个虚拟的布局环境来预估线长、拥塞和时序而不是像传统wireload model那样靠统计模型猜。DCG是Design Compiler Graphical它是DCT的图形化升级版本在Topographical模式的基础上增加了可视化的布局界面可以直观看到单元分布、拥塞热力图和关键路径走向。你完全可以把DCG理解成带眼睛的DCT跑优化的时候能边看边调。关键点在于MBFF优化必须在这种物理感知模式下做。原因很简单如果两个单比特FF物理位置相隔老远把它们强行合并成一个MBFF布局阶段就把放进一个单元里旁边必然空出一块区域网表里连接它们的信号线反而会被拉得更长时序和拥塞双双恶化。只有DCT/DCG这种能看到物理位置的工具才能判断哪些FF适合合并、哪些不能碰。1.3 你项目里说的SPG流程到底该怎么理解SPG这个缩写在不同团队的flow文档里有不同写法但在这套优化上下文里我倾向于把它理解为Single-bit Physical Grouping的流程缩写——也就是把传统逻辑综合完直接交网表给后端的模式升级成综合阶段做单比特FF的物理感知分组、替换成MBFF、再输出给物理实现的完整方法论。一个完整的SPG流程通常包含三个关键阶段第一阶段是分组候选识别工具遍历所有单比特FF分析它们的时钟域、使能信号、复位类型和逻辑连接关系找出可以合并的候选集合。第二阶段是物理距离判定综合引擎结合floorplan信息计算候选FF之间的实际距离超过阈值的一律不合并这一步是整个流程的灵魂。第三阶段是替换规则执行按bit数、驱动强度和功耗优先级选择合适的MBFF库单元完成替换同时更新网表和时序信息。后面所有实操细节本质都是在围绕这三个阶段展开。搞清楚这个逻辑你就不会被工具的一堆选项绕晕了。2. 库检查、约束准备和输入数据清单一个都不能少2.1 第一步先查LIB没有MBFF单元后面全是空谈我见过不少人兴致勃勃跑MBFF优化结果折腾半天report_register_merging出来是零最后发现foundry的库根目录里压根没有MBFF单元。工具再聪明也无法凭空把两个FF变成一个物理上更紧凑的复用单元它只能做逻辑合并但真正的面积和功耗收益必须依赖库里的物理MBFF单元。所以开工第一步先把库翻个底朝天。不同foundry对MBFF单元的命名规则很不一样常见的有DFFQN或AHFxN这类带bit数标识的单元名比如DFFQD4BWP、DFFQD2BWP这种。你可以用report_lib命令查看或者直接在lib文件里搜关键字report_lib -verbose $TARGET_LIB lib_report.txt grep -i DFFQ lib_report.txt | head -50重点检查这几项信息有没有2bit、4bit甚至更高bit数的MBFF单元bit数越多面积缩减越明显但摆放限制也越大。每个bit数下有没有多档驱动强度比如X1、X2、X4这直接影响替换时能否匹配原FF的驱动需求。单元带不带独立的SET/RESET引脚有没有扫描链输入输出。如果你设计里用了异步复位或者要做ATPG没有对应引脚的MBFF单元根本用不上。确认单元的area和power数值合理有些早期库的MBFF面积优势并不明显换完反而亏。2.2 SDC约束里藏着几个让后端抓狂的隐患常规的时钟约束、输入输出延迟、环境属性这些这里不展开我要强调三个在MBFF流程中容易被忽略的约束问题。第一个是约束对象写法。很多老项目的SDC里会用get_cells抓具体的寄存器实例名来设false path或case analysis。MBFF优化之后原来独立的FF实例名可能统一变成了新单元的内部bit名旧名字全部失效约束文件跑起来就是一堆Warning甚至直接报Error。建议从一开始约束就写成基于时钟组的模式匹配比如get_pins * -filter clock_pin这种方式避免在实例名上做死文章。第二个是扫描链相关约束。如果你的设计有DFTMBFF的扫描链连接方式会直接影响ATPG覆盖率。最好在综合之前就和DFT工程师对齐确认库里的MBFF单元在扫描模式下是每个bit独立scan in/out还是内部串联后再输出。这两种结构对scan chain长度和test timing的影响差很多。第三个是max_fanout和max_capacitance的设置。这两个参数会间接限制MBFF合并规模如果设得太激进工具可能因为害怕驱动不足而拒绝合并导致优化力度远低于预期。我建议初期先不设太严格跑完看结果再逐步收紧。2.3 DCT/DCG模式下的输入数据比普通综合多了哪些普通综合只需要RTL、SDC和工艺库就够了但DCT/DCG的Topographical模式必须额外吃进一批物理数据否则它只能开盲人模式优化质量大打折扣。我整理了一份常用输入清单floorplan或DEF文件里面至少要包含芯片尺寸、row高度、I/O pin位置和macro摆放位置这是工具做虚拟布局的基础。电源域信息多电压域设计必须提供否则工具不知道哪些FF在同一个电压域跨域合并就是事故。寄生参数文件比如tluplus或QRC tech file。没有它DCT只能靠粗略模型预估线延迟时序数据不够准等于白跑。之前版本综合保存的DDC文件如果你是从旧版本迭代上来的读DDC比重新读RTL省事得多而且能保留更多内部信息。没有floorplan的DCT不是不能用但它会把默认的die尺寸设成一个理想值虚拟布局的结果和真实后端实现偏差很大。我在一个项目里偷懒没喂DEF综合出来的MBFF数量和位置看着很漂亮结果后端一跑布局发现三分之一需要重新打散那个返工量想想都心疼。3. DCG实操Multi-bit FF优化从零到一的完整命令流3.1 启动DCG环境并完成基础数据加载先确认你的环境有Topographical模式的license启动方式如下dcnxt_shell -topographical_mode或者老版本的习惯dc_shell -topographical_mode启动之后先做环境自检看看license功能和版本号report_license dc_version接下来按部就班加载库和设计文件。我习惯把库设置在脚本最前面方便统一管理set TARGET_LIB /foundry/12t/tt_typ/lib/xxx_tt_1p0v_25c.db set MIN_LIB /foundry/12t/ff_typ/lib/xxx_ff_1p0v_0c.db set LINK_LIB * $TARGET_LIB $MIN_LIB set_app_options -name design_analysis.library_links -value $LINK_LIB read_ddc 你的设计.ddc link read_sdc 你的约束.sdc如果你的流程是从RTL开始那就read_verilog后再link。这里我强烈推荐有条件的话直接读DDC它能带上综合过程中的内部netlist和属性信息后续分析和ECO都方便。物理数据加载这一步别省read_floorplan 你的floorplan.fp # 或者如果是DEF格式 # read_def 你的floorplan.def读入之后务必做一次一致性检查确认floorplan里的模块边界和设计层次能对上不然后面虚拟布局的结果全是乱的。3.2 设置MBFF优化的关键开关每个选项都在控制什么这一步是整个流程的核心我至少踩过三次因为选项没设对导致MBFF数量为零的坑。DCT/DCG里的MBFF优化默认不一定会开启你需要主动把register merging相关的物理优化选项打开。不同版本选项名可能略有差异以你当前版本help为准。我常用的典型设置是set_app_options -name compile_top.physical.register_merging -value true set_app_options -name compile_top.physical.multi_bit_registers -value true set_app_options -name compile_top.physical.mbff_max_bit_number -value 4 set_app_options -name compile_top.physical.mbff_max_distance -value 50 set_app_options -name compile_top.physical.mbff_merge_enable -value true逐个解释这几个参数。register_merging是总开关控制工具是否允许做寄存器合并优化。multi_bit_registers是专门针对MBFF库单元的开关只有这个打开工具才会去库里搜MBFF单元并尝试替换。mbff_max_bit_number控制单个MBFF最多能合并几个bit。设成4表示工具可以在2bit和4bit单元里选不会做8bit的夸张合并。这个值不是越大越好bit数越多单元摆放越受限制常常导致布局阶段为了塞进这个大单元而浪费周边空间反而亏面积。mbff_max_distance就更有讲究了。它控制候选FF之间的最大物理距离单位是库的row单位或者微米要看你的工艺和floorplan尺寸。我一般先跑一版距离设为0的baseline再逐次加大观察收益变化。距离设得太大工具会把相距很远的FF强行合一布线拥塞急剧上升设得太小优化力度不够。50这个值是我在中等规模模块里试出来的折中点具体项目请务必跑距离扫描实验。拥塞感知选项也建议打开这能让合并决策参考局部拥塞程度set_app_options -name compile_top.physical.congestion_aware -value true set_app_options -name compile_top.physical.max_congestion -value 0.853.3 跑compile_ultra别急着用一堆激进option数据都准备好之后执行综合命令compile_ultra -no_seq_output_cst_opt -retime这里的-no_seq_output_cst_opt是为了防止工具在输出路径上做过度的寄存器优化干扰MBFF合并边界。-retime可以保留它能在不影响功能的前提下适当调整寄存器位置有时候能给MBFF合并创造更好的条件。跑完之后先看基础QoRreport_qor report_timing report_power然后专门看寄存器合并报告report_register_merging -type multi_bit -all重点看两个数字合并了多少组寄存器总共合并了多少个bit。如果全是零回3.2节逐个检查选项和库单元。3.4 增量式细化距离、bit数、例外约束逐个收紧第一版跑通只是开始真正花时间的其实是后面的调参循环。我的习惯是按照下面这个顺序来做增量优化先用宽松参数跑一版拿到合并数量和QoR数据然后把mbff_max_distance从50降到30、20看合并数量跌幅大不大、时序和拥塞有没有明显改善。如果距离从50降到30合并数只掉了5%但布线拥塞好了不少那就果断用30。如果距离一降合并数就崩了说明FF本来就分散保持50反而更合理。再调mbff_max_bit_number。先把4bit和2bit的收益分开看report里通常会区分按bit数的合并统计。如果大部分收益来自2bit单元4bit单元用得很少那可能是库里的4bit单元驱动选择有限或者物理约束太紧。这时候可以尝试把max_bit_number设成2简化单元选择工具运行时间也会缩短。还需要留意特殊寄存器的处理。比如有些寄存器在时序上非常关键或者被mark了dont_touch工具默认不会去动它们这是好事。但有些寄存器你可能不想让它们参与合并原因是逻辑上跨模块边界太远或者后续要做低功耗cell替换。这时要显式排除set_dont_touch [get_cells $special_regs -quiet] true这种白名单式控制远比先全开再想办法排除要稳我在关键路径上吃过一次亏后才改成这个策略。3.5 在DCG界面里亲眼看看合并前后发生了什么命令行跑完之后我强烈建议你用DCG的图形界面亲眼看一下结果。启动GUI的方式很简单start_gui然后打开layout view打开congestion map和timing paths overlay。重点看几个东西合并后的MBFF单元在floorplan上是否分布均匀有没有出现扎堆现象。扎堆往往出现在原FF密集区域合并完虽然寄存器少了但单元面积在局部反而更集中拥塞风险更大。关键路径有没有因为合并而变得扭曲。我之前遇到过一个case某条关键路径上的FF被合并后工具为了满足连接关系硬是绕了一大圈布线setup直接差了两百多ps后来靠排除规则才解决。congestion map上有没有明显的红色热点。如果热点正好在MBFF密集区请立即缩小mbff_max_distance或者对热点区域单独设置placement blockage。4. SPG流程避坑指南六个高频翻车现场4.1 坑一没喂floorplan就跑等于闭着眼睛猜这个坑在第三节提过但值得单独拿出来骂。DCT/DCG的整个优势就是物理感知不喂floorplan等于把它的眼睛蒙上。别说MBFF合并做不好连基础的线长预估都会失真综合出来的网表到了后端手里几乎是必然要返工。更闹心的是有些人喂的是简化版floorplan比如只给了芯片边框没给macro位置。DCT里macro的位置会直接影响周边区域的可布线性macro位置缺失工具会把macro周边当成普通区域来预估合并策略自然就偏了。我现在的原则是没有带macro位置的完整floorplan就不开MBFF流程省得给自己埋雷。4.2 坑二合并开关一开关键路径时序反而更差这个坑我印象最深。之前有个模块开了MBFF优化后寄存器面积看着很漂亮但一条关键路径的setup从满足变成了违例查了半天发现是合并且在路径中段大改FF位置造成的。原因是合并后单元内部走线变长虽然cell单位小了但单元之间的连接距离可能更远。工具虽然做了物理感知但它的时序引擎对某些边界case的判断并不比后端更准。解决办法有两个方向。一是对已知关键路径上的寄存器组设置dont_touch强制不参与合并代价是损失一部分优化机会。二是跑完合并后用DCG的时序分析功能逐条路径检查发现违例后单独排除问题寄存器这样能最大程度保留收益。我倾向于先用第二个方向因为关键路径上的寄存器往往只占全部寄存器的一小部分单独排除的损失可以接受。4.3 坑三scan chain完全没考虑DFT阶段直接堵死如果你的设计与扫描链相关MBFF流程必须提前跟DFT工程师对齐否则ATPG阶段会哭都哭不出来。不同库的MBFF单元在scan结构上差别很大。有些是每个bit有独立的scan in和scan out内部不串联这种对scan chain长度没有影响有些是bit之间内部串联后整体输出这种就相当于在scan chain里插进了串联顺序chain上的顺序可能被打乱。更麻烦的是有些MBFF单元在scan shift模式下会有额外的内部逻辑延迟导致scan clock的timing更难满足。我在一个16nm项目里就遇到过MBFF替换后scan hold violation暴增的情况最后后端不得不把scan clock tree上的buffer加大一圈功耗收益被吃掉不少。所以建议在跑MBFF优化之前先用小模块做一个DFT验证实验确认库单元的scan行为再全量跑。4.4 坑四多电压域和低功耗单元区的MBFF误合并多电压域设计里电平转换器level shifter和隔离单元isolation cell周边的寄存器强烈建议不要轻易合并。原因在于不同电压域的FF如果被合并进同一个MBFF内部接口的电平匹配和隔离控制信号连接都会变得极其复杂低功耗意图很难正确实现。我处理这类情况的方法也比较粗暴在DCG里把电压域边界、level shifter区域和isolation区都设成placement blockage把merge候选限制在单一电压域内部。同时专门跑一次低功耗检查确认合并后的网表在power state table下没有问题。这块如果不小心后端低功耗验证阶段会被当成大问题揪出来到时候再改就不是一两天的事。4.5 坑五网表交接后命名全变ECO追踪成了噩梦MBFF优化会重写寄存器的命名规则原来单比特FF的实例名在新的网表里可能找不到。这个对芯片实现影响很大尤其是后续ECO阶段逻辑分析、时序修timing、物理改版都需要精确定位到某个寄存器。名字没了定位全靠猜。我的习惯是每一步关键优化之后都会导出一份ECO映射文件或者ddc存档记录优化前后的实例名对应关系。还可以在综合输出时给网表和SDC加上统一前缀至少能快速区分优化前后的版本write -format ddc -hierarchy -output 你的设计_eco.ddc write_verilog -no_cell_name -output 你的设计_mbff.v保存好这些文件后端的ECO工程师会对你感激涕零。4.6 坑六工具版本和库版本错配选项名对不上DC工具版本更新很快MBFF相关选项的命名在不同大版本之间可能都有差异。我遇到过从某个版本升级之后原来设置的compile_top.physical.multi_bit_registers直接报unknown option查help发现新版本改成了compile_ultra.register_merging之类的新名字。更隐蔽的问题是lib文件版本混用。MBFF单元的db文件和主工艺库db版本不一致工具读库的时候可能把MBFF单元静默丢弃合并量暴跌但不报错最坑没有之一。所以每次跑flow第一步都是查版本和库一致性report_version list_libs redirect -file target_lib_cells.rpt {report_lib -summary}确认库单元数量和关键单元都存在之后再往下走。5. 结果怎么看、怎么交接后端才能少骂两句5.1 用数据说话QoR对比表很多人跑完MBFF优化就急着交接其实应该先做一个完整的收益评估。我自己的习惯是出一张对比表记录同一设计在开关MBFF优化前后的核心指标。列几个关键项寄存器面积、总cell面积、总动态功耗、时钟网络功耗、WNS、TNS、congestion最大值。项目里这份表格每次都能派上大用场因为无论是设计评审还是跟后端扯皮拿数据说话比空口讲我优化了功耗有说服力得多。具体数字不用太精确趋势对就好。5.2 重点检查congestion不能只看面积功耗MBFF优化的直接收益是面积和功耗但真正的风险在拥塞。跑完优化后用DCG的congestion map看全局热点同时用命令导出拥塞热力数据report_congestion -hotspot如果出现红色区域先对比红色区域里有没有刚合并出来的大MBFF单元。如果有考虑缩小mbff_max_distance或者对热点区域设blockage然后重新跑一版。拥塞问题留到后端解决代价往往是好几轮iteration远不如在这边多跑两遍综合。5.3 交接给后端的必备文件清单交接不是丢一个网表和SDC就完了。我给后端同事准备的交接包里除了常规文件还专门加了几样东西一份哪些FF被合并了的报告就是register_merging report的详细版方便后端定位问题。保存完整优化信息的DDC文件后端可以直接读DDC看布局预览不用重新推断。ECO映射文件和实例名前缀说明。如果SDC里用了基于FF实例名的约束务必同步一份更新后的SDC并把命名变化列清楚。后端同事拿到这些东西大概率不会因为找不到某个寄存器而来找你团队协作体验直接上升一个档次。6. 关于SPG流程最后再分享几条私房建议第一不要指望DCG一次跑到位。MBFF优化本质上是搜索问题工具给出的解只是它在约束范围内的局部最优不代表真实后端布局中的全局最优。你要把它当成一个迭代过程距离、bit数、例外约束来回调整至少要跑三到五版才能找到稳定解。第二流程要分层验证。先拿一个中等规模模块做实验模块把参数定下来再推广到整个设计。不要一开始就全芯片跑那是浪费机器时间和你的耐心。我一般在模块级做参数扫描和收益评估最优参数确认后再上全芯片。第三团队同步非常重要。MBFF优化这件事横跨综合、后端、DFT和低功耗多个岗位任何一个环节脱节都可能让整个优化白做。在我的经验里最有效的做法是跑MBFF优化之前先召开一次短会把库单元型号、扫描链方案和后端floorplan版本统一对齐然后后端直接给一版可用的DEF。最后说个小技巧如果你对自己的设计没有十足把握请先给寄存器分组做一个白名单式的合并控制。与其让工具全量开跑再救火不如你主动划好哪些FF可以合并、哪些绝对不能碰两步路而已但能帮你少填一半的坑。SPG这套流程说白了就是库数据、物理数据和团队协作这三块都抓牢MBFF才能真正变成你项目里的功耗杀手锏。