HyperFlex 架构(5):Hyper-Optimization 通用优化技术

📅 2026/7/5 13:03:58
HyperFlex 架构(5):Hyper-Optimization 通用优化技术
一、Hyper-OptimizationHyper-Retiming 和 Hyper-Pipelining 把数据路径加速之后限制性能的往往就变成了控制逻辑长的 feedback loop、状态机、复杂的组合判断。Hyper-Optimization 的思路是把这些限制 retiming 的逻辑结构改写成功能等效但工具能优化的形式用前馈或预计算路径替代长的组合反馈路径。手册给出的预期是相比前一代高性能 FPGA 有 2x 的性能提升。二、通用优化技术2.1 Shannon 分解Shannon 分解在 Hyper-Optimization 里扮演一个关键角色。它是布尔函数的一种因式分解方式。一个函数FFF可以写成Fx⋅Fxxˉ⋅FxˉF x \cdot F_x \bar{x} \cdot F_{\bar{x}}Fx⋅Fx​xˉ⋅Fxˉ​其中FxF_xFx​和FxˉF_{\bar{x}}Fxˉ​是FFF关于xxx的正、负 co-factor。对于四输入函数F(a,b,c,x)F(a, b, c, x)F(a,b,c,x)可以分解为x⋅F(a,b,c,1)xˉ⋅F(a,b,c,0)x \cdot F(a, b, c, 1) \bar{x} \cdot F(a, b, c, 0)x⋅F(a,b,c,1)xˉ⋅F(a,b,c,0)。在 Hyper-Optimization 中Shannon 分解把关键信号 x 推到输入逻辑锥的最前面让它穿过逻辑锥的路径最短。代价是面积翻倍其他信号的路径变长。综合工具可以利用常量驱动的输入对 co-factor 做化简如上图所示。可以对多个关键输入信号反复应用 Shannon 分解每次分解面积再翻一倍。Shannon 分解对处理 loop 特别有效。在 loop 中的逻辑上做 Shannon 分解后逻辑被移出 loopCompiler 就可以对它做流水线或 retiming 了。上图是一个含有一个寄存器、四级组合逻辑和一个额外输入的 loop。在 loop 里面加寄存器会改变功能但通过 Shannon 分解可以把组合逻辑移到 loop 外面。loop 里的寄存器输出只有 0 或 1把喂给它的组合逻辑复制两份一份输入接 0一份接 1。loop 里的寄存器从两份复制逻辑中选一份。分解后 loop 里的逻辑变少了移出去的逻辑 Compiler 可以做 retiming 或 Hyper-Pipelining电路性能就上去了。Shannon 分解示例以下代码是一个根据目标值对 internal_total 做加减的电路。module target_loop (clk, sclr, data, target, running_total); parameter WIDTH 32; input clk, sclr; input [WIDTH-1:0] data, target; output [WIDTH-1:0] running_total; reg [WIDTH-1:0] internal_total; always (posedge clk) begin if (sclr) internal_total 0; else internal_total internal_total (((internal_total target) ? data : data) * (target/4)); end assign running_total internal_total; endmoduleFast Forward 编译报告显示大约 302 MHz最后一条 Fast Forward Limit 提示 critical chain 是一个 loop。下图是上述表达式对应的电路结构包含比较、加法和乘法操作。这个表达式适合做 Shannon 分解。与其根据比较结果只做一个加法加正或负的 data不如同时算两条路径internal_total - (data * target/4) 和 internal_total (data * target/4)然后用 internal_total target 的比较结果来选。改后的代码如下module target_loop_shannon (clk, sclr, data, target, running_total); parameter WIDTH 32; input clk, sclr; input [WIDTH-1:0] data, target; output [WIDTH-1:0] running_total; reg [WIDTH-1:0] internal_total; wire [WIDTH-1:0] total_minus, total_plus; assign total_minus internal_total - (data * (target / 4)); assign total_plus internal_total (data * (target / 4)); always (posedge clk) begin if (sclr) internal_total 0; else internal_total (internal_total target) ? total_minus : total_plus; end assign running_total internal_total; endmodule重编译后性能几乎翻倍。什么电路适合 Shannon 分解能重新安排许多输入来控制最后一级选择阶段的电路。逻辑锥里只有一两个信号是真正关键的其余是静态的或优先级明显更低的信号。重组逻辑时要注意选择信号的逻辑深度和选择器输入端的逻辑深度理想情况两边相近。Shannon 分解面积代价显著复杂函数尤其大。后面还会介绍面积代价更小的其他优化技术。2.2 时域复用时域复用用多个计算线程来提高电路吞吐量也叫 C-slow retiming 或多线程化。做法是把电路里的每个寄存器替换成 C 个串联寄存器每份额外寄存器创建一个新计算线程。数据通过整个设计需要 C 倍的时钟周期但 Compiler 可以用这些额外寄存器做 retiming把 fMAX 提高 C 倍。比如与其例化两个跑 400 MHz 的模块不如例化一个跑 800 MHz 的模块。修改 RTL把每个寄存器包括 loop 里的替换成 C 个寄存器每个对应一个独立的计算线程。上例中每个寄存器替换成了两个。编译后额外的寄存器让 retiming 有更大的灵活度。除了替换寄存器还需要把多路输入数据流做 MUX 进入模块输出端做 DEMUX 出去。时域复用适用于有多条并行线程、每条线程各自受 loop 限制的设计。被优化的模块必须对延迟不敏感。2.3 Loop 展开Loop 展开把逻辑从 loop 里移出来变成前馈流之后可以加流水线级进一步优化。2.4 Loop 流水线化Loop 到处都是但它也是 Hyper-Retiming 的死角。Compiler 不会自动对 loop 内部的逻辑做流水线因为往 loop 里加或减时序元件可能改变功能。但你可以手动改 loop 结构让 Compiler 能插入流水线级而不改变功能。三步走重组 loop 和非 loop 逻辑手动往 loop 里加流水线级级联 loop 逻辑下图是一个逻辑 loop 的定义。结果 Zn 是输入 Xn 和它自身延迟版本的函数。如果函数 f(·) 满足交换律、结合律和分配律比如加法、XOR、取最大值以下变换在数学上等价Loop 流水线化演示以下用累加器来演示完整的 loop 流水线化过程。原始代码输入 in 乘以 x加上 out 的上一个值乘以 y。module orig_loop_strct (rstn, clk, in, x, y, out); input clk, rstn, in, x, y; output out; reg out, in_reg; always (posedge clk) if (!rstn) in_reg 1b0; else in_reg in; always (posedge clk) if (!rstn) out 1b0; else out y*out x*in_reg; endmodule第一步是重写逻辑尽可能把逻辑从 loop 里移出来形成前向逻辑块。Compiler 不会优化 loop 里的任何逻辑。原则能提前算的决策和计算在进 loop 之前算完能塞进 loop 前面寄存器级的尽量塞进去。第二步retime loop 寄存器确保功能与原始 loop 电路相同。第三步重复前面优化步骤对高亮边界内的逻辑再做处理。最终结果是四级优化综合工具会把多个乘法器塌缩合并实际资源比看上去少得多。2.5 预计算预计算是最简单也最实用的提速技术之一。遇到关键路径上的逻辑先确认涉及的计算能不能更早拿到信号。把计算尽量提前不让它出现在关键路径上。处理 loop 的时候先考虑预计算。Compiler 很难单独靠 retiming 优化 loop 里的逻辑不能把寄存器从 loop 外移进去也不能从 loop 里移出来。所以 loop 里的逻辑越少越好。预计算之后loop 里的逻辑最小化编码值提前算好放在 loop 外面可以单独做流水线和 retiming。Loop 本身还在但它对速度的拖累被控制住了。代码示例原始 loop 里有比较运算符// 原始 StateJam: if (RetryCnt MaxRetry JamCounter 16) Next_state StateBackOff; else if (RetryCnt MaxRetry) Next_state StateJamDrop; else Next_state Current_state;把 RetryCnt MaxRetry 和 JamCounter 16 的预计算结果提到 loop 外面reg RetryCntGTMaxRetry; reg JamCounterEqSixteen; // 预计算在 loop 外完成 StateJam: if (!RetryCntGTMaxRetry JamCounterEqSixteen) Next_state StateBackOff; else if (RetryCntGTMaxRetry) Next_state StateJamDrop; else Next_state Current_state;