Wallace Tree与Dadda Tree:如何在Synopsys Design Compiler中优化部分积压缩策略
1. 从乘法器的心脏说起为什么部分积压缩这么重要做数字电路设计的朋友尤其是搞过高性能计算、DSP或者AI加速器的肯定都跟乘法器打过交道。乘法器这东西说白了就是芯片里干“乘法”这个重体力活的单元。它的性能好坏直接决定了整个数据通路Datapath是健步如飞还是步履蹒跚。而乘法器性能的核心瓶颈往往不在最后的那个进位传播加法器CPA而是在它前面那个“化繁为简”的步骤——部分积压缩。让我给你打个比方。想象一下你要算一个16位数乘以另一个16位数。用最朴素的笔算方法你会得到16个“部分积”就像竖式乘法里那一行行的中间结果。在硬件里这些部分积就是一堆需要相加的二进制数。如果直接把这16个数丢给一个巨大的加法器去加那面积和延迟都会爆炸完全不现实。所以我们必须先想办法把这十几行、几十行甚至上百行的“部分积”压缩成只有两行一行和一行进位然后再用一个相对快速的CPA来完成最后的加法。这个把多行变两行的过程就是部分积压缩而负责执行这个压缩任务的“骨架”或“算法”就是我们今天要深入聊的Wallace Tree和Dadda Tree。在Synopsys Design CompilerDC这样的综合工具里当你写下一句简单的assign c a * b;时DC并不会魔法般地变出一个最优乘法器。它需要根据你的设计约束是要求跑得快还是要求面积小、所用的工艺库特性以及它内置的算法来为你选择并构建一个具体的乘法器结构。其中选择Wallace还是Dadda来构建压缩树就是一个对最终PPA性能、功耗、面积影响巨大的关键决策。但DC通常把这个过程藏在黑盒里默认自动选择。作为设计者如果我们能理解背后的原理并且知道如何在DC中施加影响、观察结果就能从“听天由命”变成“心中有数”甚至能主动优化。这就是咱们这篇文章要干的事不光弄懂Wallace和Dadda是啥更要学会在DC这个实战环境里怎么去用它们、调它们、分析它们。2. 两大压缩策略的核心哲学激进派 vs. 保守派在深入技术细节之前我们得先把握住这两种树形结构的“性格”。它们代表了两种不同的优化哲学理解了这一点后面的所有步骤和选择都会变得清晰。2.1 Wallace Tree追求极致的速度狂人Wallace Tree的设计哲学非常直接甚至有点“贪婪”能压缩就马上压缩绝不拖延。它的目标是在每一级压缩中尽可能多地减少部分积的行数从而用最少的逻辑级数到达最终的两行状态。你可以把它想象成一个急性子的项目经理看到任务部分积就想立刻分配人手加法器解决追求最短的项目总周期关键路径延迟。它的工作方式是扫描所有列检查部分积阵列中每一列相同权重的比特位的高度即有多少个1需要相加。立即行动只要某一列的高度大于等于3它就立刻调用全加器Full Adder, FA进行3:2压缩3个输入比特产生1个和位与1个进位位。如果某列高度是2且没有其他更优组合它也可能用半加器Half Adder, HA进行2:2压缩。迭代进行压缩后产生的新和位与进位位会和剩余未压缩的比特一起组成新的、行数更少的阵列。然后重复步骤1和2直到所有列的高度都不超过2。这种“火力全开”的策略带来了一个显著优点逻辑深度最小。因为它在每一级都尽可能做最多的工作所以总的压缩级数往往是最少的。这对于时序非常紧张的关键路径比如高频CPU的乘法单元、MAC运算中的乘法部分来说是巨大的优势。但它的缺点也同样明显。由于压缩是“贪婪”且局部的它不太考虑全局的连线规整性。这会导致压缩树的结构看起来比较“乱”不同列的压缩进度不一致产生的中间信号扇出和布线长度可能难以预测从而引发布线拥塞Routing Congestion。在先进工艺节点下互连线延迟占比越来越高杂乱的布线不仅可能抵消掉逻辑级数少带来的好处还会给后端布局布线带来挑战并可能占用更多的面积来绕线。2.2 Dadda Tree精打细算的面积管家与Wallace的激进相反Dadda Tree是个精明的保守派。它的核心思想是不要急着压缩先算好每一步的目标按计划行事。Dadda追求的不是单级压缩的最大化而是在满足最终目标的前提下让每一步的压缩操作都尽可能规整、高效从而节省硬件资源。它更像一个严谨的规划师先制定一个详细的阶段性目标每一级允许的最大列高序列然后严格按照这个目标来执行压缩绝不超额完成。它的步骤是制定计划首先确定最终目标将所有列压缩到高度不超过2。然后反向推导出一个高度序列。经典的Dadda序列是从最终高度d02开始计算前一级允许的最大高度d1 floor(1.5 * d0) 3再前一级d2 floor(1.5 * d1) 4以此类推直到得到的数值大于或等于初始部分积阵列的最大列高。这个序列就是它的“施工蓝图”。按图施工从最高层级对应初始阵列开始目标是将每一列的高度压缩到不大于当前层级规定的数值。例如在某一级目标是列高≤4那么对于高度为6的列只需要压缩掉2个比特使用FA或HA使其降到4或以下即可而不是像Wallace那样尽可能压到最低。逐级推进完成一级后进入下一级目标列高更小的层级重复压缩直到达到最终级列高≤2。这种方法的优点是结构更规整使用的加法器FA/HA总数通常更少因此面积更优。同时由于压缩是分阶段、有控制地进行产生的中间信号在布局上往往更整齐有助于降低布线复杂度对面积和功耗都更友好。代价是它的逻辑级数通常会比Wallace Tree多一级左右在绝对速度上会稍逊一筹。简单总结一下两者的“人设”Wallace是“快枪手”为了速度可以接受一些混乱Dadda是“老会计”为了整洁和节省愿意多花一点时间。3. 在DC中实战如何观察与影响压缩树的选择理论懂了但咱们是工程师得落地。在Synopsys Design Compiler里我们怎么知道它到底用了哪种树又怎么能“暗示”它按照我们的意愿去选择呢DC通常不会在报告里直接写明“使用了Wallace Tree”但我们可以通过一些方法和命令来窥探和施加影响。3.1 读懂综合报告里的蛛丝马迹首先最直接的方法是使用report_datapath命令。当你综合了一个包含乘法器的模块比如一个简单的乘法器或者一个乘累加单元MAC后在DC的交互界面里report_datapath -structure -design your_multiplier_module_name这个命令会详细列出数据通路中的各种组件。你需要重点关注报告中关于加法器树的部分。虽然它不会直接命名Wallace或Dadda但你可以通过观察加法器的层级结构和分布模式来推断。Wallace的迹象如果你看到加法器特别是全加器FA的层级很少但在同一层级内分布非常密集不同比特位的压缩进度差异较大结构看起来不那么对称这很可能是Wallace Tree。Dadda的迹象如果加法器的层级相对清晰每层内的压缩操作看起来更有规律整体结构呈现出一种从宽到窄的、相对规整的锥形这更符合Dadda Tree的特点。此外查看综合后的网表用图形化界面比如DC的GUI或Verdi观察乘法器部分的电路结构有经验的设计师也能从拓扑形状上做出大致判断。Wallace树看起来更像一个“网”而Dadda树更像一个“塔”。3.2 尝试用变量施加你的偏好虽然DC有自己内置的算法来决定用哪种树但它也提供了一些变量允许我们在一定程度上表达倾向。需要注意的是这些变量的效果强烈依赖于你所使用的工艺库。有些库对这两种树的支持和优化程度不同因此变量的作用可能是“建议性”的而非强制性的。最常用的一个变量是datapath_prefer_wallace_tree# 在综合前设置倾向于使用Wallace Tree结构 set_app_var datapath_prefer_wallace_tree true设置这个变量为true相当于告诉DC“如果可能的话请优先考虑使用Wallace Tree来构建压缩部分。” 这在你明确知道当前设计是时序关键型时非常有用。反过来如果你没有设置这个变量或者将其设为falseDC通常会基于其内部的成本函数综合考虑时序、面积、功耗来做出选择在很多面积敏感的场景下它可能自动倾向于Dadda。还有一个相关的变量是datapath_use_custom_adder_tree这个变量用于控制是否使用用户自定义或库中提供的特殊加法器树结构。通常我们将其设为false让DC使用它自己优化的通用树结构Wallace或Dadda。set_app_var datapath_use_custom_adder_tree false这里有个非常重要的实践建议不要盲目设置这些变量。最好的流程是先用默认设置不设置这些变量综合一次作为基准。记录下此时的时序report_timing、面积report_area和功耗report_power数据。然后设置datapath_prefer_wallace_tree true在同样的约束下再综合一次。对比两次的结果。如果时序确实有显著改善而面积增加在可接受范围内那么这个选择就是有益的。如果时序改善微乎其微面积却涨了很多那可能说明在当前工艺和约束下DC默认的选择可能是Dadda已经接近最优或者库本身对Wallace的支持并不高效。4. 基于设计目标的策略选择什么时候该用谁了解了原理掌握了工具接下来就是做选择题了。在实际项目中我们该如何在Wallace和Dadda之间做出明智的抉择呢这个选择没有银弹完全取决于你的设计优先级。4.1 毫不犹豫选择Wallace Tree的场景如果你的设计满足以下一个或多个特征那么你应该强烈考虑引导DC使用Wallace Tree处于绝对关键路径Critical Path这是最典型的场景。例如你的乘法器是整个芯片时钟周期决定因素的一部分任何一级逻辑的延迟都是宝贵的。Wallace Tree最少的逻辑级数能直接转化为更短的路径延迟帮助你满足苛刻的时序要求。高频设计High-Frequency Design目标频率很高每个逻辑级的延迟预算非常紧张。Wallace Tree的浅深度特性在这里是天然优势。对面积不敏感对性能要求极致比如在一些高性能计算HPC芯片、网络处理器的核心数据通路中为了吞吐量可以牺牲一定的面积。乘法器作为核心算子其速度至关重要。与Booth编码强耦合的设计Booth编码是一种减少部分积行数的技术。在实际中Wallace Tree经常与Booth编码结合使用形成“Booth Wallace”的高性能组合拳。DC在处理经过Booth编码的乘法时有时会更倾向于搭配Wallace Tree来实现激进压缩。4.2 优先考虑Dadda Tree的场景相反当你的设计目标转向以下方面时Dadda Tree通常是更好的伙伴面积敏感Area-Sensitive设计对芯片成本控制严格每一平方微米都要精打细算。Dadda Tree通过更规整的结构和更少的加法器数量通常能实现比Wallace更小的面积。低功耗设计Low-Power Design面积小往往意味着电容小动态功耗也会随之降低。此外规整的结构可能有助于降低布线带来的额外功耗。虽然Wallace延迟小可能允许降低电压来省电但在同等电压下Dadda的面积优势通常能带来更优的功耗效率。布线拥塞成为主要问题特别是在规模较大的设计或使用较旧工艺节点时布线资源可能比逻辑资源更紧张。Dadda Tree产生的更规整、更可预测的连线能极大缓解布线拥塞提高设计可布通率Routability并减少迭代次数。对时序要求不那么极端如果时序裕量Slack比较充足比如乘法器不在最关键的路径上那么用Dadda Tree换取面积和功耗的收益是非常划算的。很多时候Dadda Tree多出来的一级延迟完全在可接受范围内。为了更直观我把两者的选择策略总结成下面这个表格你可以在做架构权衡时参考考量维度推荐 Wallace Tree推荐 Dadda Tree说明与建议首要优化目标时序Timing面积Area这是最根本的取舍。速度换面积还是面积换速度。关键路径情况乘法器位于关键路径上乘法器不在关键路径上或时序裕量充足分析report_timing看乘法器路径的 Slack。功耗要求对功耗有一定容忍度性能优先低功耗是重要指标Dadda面积小静态和动态功耗通常更有优势。布线预期设计规模较小或布线资源丰富设计规模大担心布线拥塞在先进工艺或大型模块中Dadda的规整性价值凸显。工艺库特性库中FA/HA速度很快面积代价可接受库对密集、不规则布线不友好需要结合工艺库的详细报告如Wire Load Model判断。设计阶段早期原型追求最高性能后期优化进行面积修复和功耗收敛可以前期用Wallace保时序后期若有余量可尝试切Dadda收面积。5. 进阶技巧结合DC综合策略进行深度优化对于资深设计者来说仅仅选择Wallace或Dadda可能还不够。我们还需要将压缩树的选择融入到整个综合优化流程中进行更精细的控制。5.1 分模块差异化策略一个芯片里可能有很多个乘法器它们的重要性各不相同。我们可以采用分而治之的策略。例如在顶层脚本中我们可以针对不同的模块设置不同的综合导向。# 对性能核心的乘法模块采用激进策略 current_design high_perf_mult_core set_app_var datapath_prefer_wallace_tree true set_max_area 0 # 暂时放开面积约束优先满足时序 compile_ultra -timing_high_effort # 对辅助或后台计算的乘法模块采用面积优化策略 current_design low_power_mult_unit set_app_var datapath_prefer_wallace_tree false # 让DC倾向于Dadda set_max_delay -from [all_inputs] -to [all_outputs] 2.0 # 设置宽松的时序约束 compile_ultra -area_high_effort通过这种差异化编译可以在满足整体PPA目标的前提下让每个模块都得到最适合的优化。5.2 利用多轮编译Multi-Pass Compilation有时候一次编译的结果可能不理想。我们可以尝试多轮编译通过改变变量和约束来探索设计空间。第一轮基准不设置datapath_prefer_wallace_tree让DC自由发挥。记录结果。第二轮速度优先设置datapath_prefer_wallace_tree true并可能加强时序约束如减小clock_period。编译后对比时序和面积变化。第三轮面积修复如果第二轮时序达标但面积过大可以在保持datapath_prefer_wallace_tree true的基础上启用compile_ultra -inc进行增量编译并施加严格的面积极限让DC在保持时序的前提下进行面积优化。5.3 关注工艺库的特定指令一些先进的工艺库如某些FinFET工艺库可能会提供自己优化的、专有的乘法器IP或构建策略。这些可能超越了传统的Wallace/Dadda二分法。你需要查阅库文档看看是否有类似set_lib_cell_preference这样的命令来为乘法器选择特定的、性能面积更优的底层单元。这时DC的datapath_prefer_wallace_tree变量可能就不再起作用而是遵循库的推荐。6. 验证与调试确保你的选择真的有效做了选择改了设置最后一定要验证。综合不是一锤子买卖我们需要通过严谨的分析来确认优化策略是否真的达到了预期效果。时序分析Timing Analysis这是最重要的验证步骤。使用report_timing命令仔细查看乘法器相关路径的建立时间Setup和保持时间Hold是否满足要求。比较策略切换前后关键路径的 Slack变化。如果改用Wallace后Worst Negative Slack (WNS) 得到了显著改善那说明这个选择是有效的。面积与功耗报告Area Power Report使用report_area和report_power。切换到Wallace后面积上涨了多少功耗增加了多少这个代价是否在项目预算之内切换到Dadda后面积节省是否显著功耗是否有下降这些数据是做出最终决策的依据。电路结构检查Structural Inspection再次使用report_datapath或直接查看综合后网表。确认综合工具是否真的遵从了或大致遵从了你的偏好。有时由于库单元或拓扑限制DC可能进行了一些折中但整体结构应该能反映出你的倾向。形式验证Formal Verification在修改了综合策略后虽然功能上应该保持一致都是乘法器但严谨的流程中应该使用形式验证工具如Formality将新的网表与参考网表或RTL进行等价性检查确保功能100%正确。在我经历的一个图像处理芯片项目中有一个8x8的无符号乘法器位于数据流的关键路径上。最初采用默认综合时序总是差一点。通过设置datapath_prefer_wallace_tree true并重新编译乘法器内部路径延迟减少了约15%使得整条关键路径满足了时序要求。虽然面积增加了约8%但由于该乘法器并非大量例化总芯片面积影响微乎其微这个交换是非常值得的。这个案例告诉我理解底层机制并善用工具变量往往能解决那些看似棘手的时序瓶颈。