📘 第09课:加减融合

🎯 本课目标

📖 为什么需要加减融合?

许多算法需要同时计算A+B和A-B,例如FFT蝶形运算、CORDIC旋转、复数运算。如果分别计算需要两次加法器,但两次运算共享对齐步骤,可以融合为一个模块节省约30%面积。

FFT蝶形运算: X'[k] = X[k] + W^k · Y[k] Y'[k] = X[k] - W^k · Y[k] 需要同时计算 X+W·Y 和 X-W·Y

📖 融合的数据通路

┌─────────────────────┐ A ─────────→ │ 指数比较与对齐 │ B ─────────→ │ (共享步骤) │ └────────┬────────────┘ │ ┌────────────────┼────────────────┐ │ │ ┌───────▼────────┐ ┌─────────▼────────┐ │ mant + mant │ │ mant - mant │ │ → A+B结果 │ │ → A-B结果 │ └───────┬────────┘ └─────────┬────────┘ │ │ ┌───────▼────────┐ ┌─────────▼────────┐ │ 规格化+舍入 │ │ 规格化+舍入 │ └────────────────┘ └──────────────────┘

📖 面积与延迟分析

组件独立实现(×2)融合实现节省
指数比较2套1套50%
对齐移位2套1套50%
加减逻辑2套2套(共享输入)0%
规格化2套2套0%
舍入2套2套0%
总计~30%

🔧 Verilog实现

"verilog-cmt">//=============================================================
"verilog-cmt">// float_addsub_fused.sv - 加减融合模块
"verilog-cmt">// 同时计算 A+B 和 A-B
"verilog-cmt">//=============================================================
"verilog-kw">module float_addsub_fused (
    "verilog-kw">input  "verilog-kw">wire [31:0] a,
    "verilog-kw">input  "verilog-kw">wire [31:0] b,
    "verilog-kw">input  "verilog-kw">wire [1:0]  round_mode,
    "verilog-kw">output "verilog-kw">wire [31:0] result_add,   "verilog-cmt">// A + B
    "verilog-kw">output "verilog-kw">wire [31:0] result_sub    "verilog-cmt">// A - B
);
    "verilog-cmt">// 共享对齐逻辑
    "verilog-kw">wire        sign_a = a[31], sign_b = b[31];
    "verilog-kw">wire [7:0]  exp_a  = a[30:23], exp_b = b[30:23];
    "verilog-kw">wire [23:0] mant_a = (exp_a=="verilog-num">8'b0) ? {"verilog-num">1'b0,a[22:0]} : {"verilog-num">1'b1,a[22:0]};
    "verilog-kw">wire [23:0] mant_b = (exp_b=="verilog-num">8'b0) ? {"verilog-num">1'b0,b[22:0]} : {"verilog-num">1'b1,b[22:0]};
    "verilog-kw">wire a_ge_b = (exp_a > exp_b) | ((exp_a == exp_b) & (mant_a >= mant_b));
    "verilog-kw">wire [7:0]  exp_big  = a_ge_b ? exp_a : exp_b;
    "verilog-kw">wire [23:0] mant_big = a_ge_b ? mant_a : mant_b;
    "verilog-kw">wire [23:0] mant_sml = a_ge_b ? mant_b : mant_a;
    "verilog-kw">wire        sign_big = a_ge_b ? sign_a : sign_b;
    "verilog-kw">wire [7:0]  exp_diff = a_ge_b ? (exp_a - exp_b) : (exp_b - exp_a);
    "verilog-kw">wire [27:0] sml_ext = {mant_sml, "verilog-num">4'b0};
    "verilog-kw">wire [27:0] aligned = sml_ext >> exp_diff;
    "verilog-kw">wire [23:0] sml_al  = aligned[27:4];

    "verilog-cmt">// 分支1: A+B
    "verilog-kw">wire [24:0] sum_add = {"verilog-num">1'b0, mant_big} + {"verilog-num">1'b0, sml_al};
    "verilog-kw">wire        add_carry = sum_add[24];
    "verilog-kw">wire [23:0] add_mant = add_carry ? sum_add[24:1] : sum_add[23:0];
    "verilog-kw">wire [7:0]  add_exp  = exp_big + {"verilog-num">7'b0, add_carry};
    "verilog-kw">wire        add_sign = sign_a;

    "verilog-cmt">// 分支2: A-B (有效减法)
    "verilog-kw">wire [24:0] sum_sub = {"verilog-num">1'b0, mant_big} - {"verilog-num">1'b0, sml_al};
    "verilog-kw">wire [4:0] sub_lzc = sum_sub[23] ? "verilog-num">5'd0 : sum_sub[22] ? "verilog-num">5'd1 :
                         sum_sub[21] ? "verilog-num">5'd2 : sum_sub[20] ? "verilog-num">5'd3 :
                         sum_sub[19] ? "verilog-num">5'd4 : "verilog-num">5'd5;
    "verilog-kw">wire [24:0] sub_norm = sum_sub << sub_lzc;
    "verilog-kw">wire [23:0] sub_mant = sub_norm[23:0];
    "verilog-kw">wire [7:0]  sub_exp  = exp_big - {"verilog-num">3'b0, sub_lzc};
    "verilog-kw">wire        sub_sign = sign_big;

    "verilog-kw">assign result_add = {add_sign, add_exp, add_mant[22:0]};
    "verilog-kw">assign result_sub = {sub_sign, sub_exp, sub_mant[22:0]};
"verilog-kw">endmodule

"verilog-cmt">//=============================================================
"verilog-cmt">// tb_float_addsub_fused.sv - 融合模块测试
"verilog-cmt">//=============================================================
"verilog-kw">module tb_float_addsub_fused;
    "verilog-kw">reg  [31:0] a, b;
    "verilog-kw">reg  [1:0]  round_mode;
    "verilog-kw">wire [31:0] result_add, result_sub;
    "verilog-kw">integer err = 0;
    float_addsub_fused uut(.a(a),.b(b),.round_mode(round_mode),
        .result_add(result_add),.result_sub(result_sub));
    "verilog-kw">localparam [31:0] ONE="verilog-num">32'h3F800000, TWO="verilog-num">32'h40000000;
    "verilog-kw">localparam [31:0] THREE="verilog-num">32'h40400000, FOUR="verilog-num">32'h40800000;
    "verilog-kw">localparam [31:0] ZERO="verilog-num">32'h00000000, HALF="verilog-num">32'h3F000000;
    "verilog-kw">task chk; "verilog-kw">input [31:0] ea,eb,er_add,er_sub; "verilog-kw">input [255:0] nm; "verilog-kw">begin
        a=ea; b=eb; round_mode="verilog-num">2'b00; #10;
        "verilog-kw">if(result_add!==er_add||result_sub!==er_sub) "verilog-kw">begin
            $display("FAIL %0s: add=%h sub=%h",nm,result_add,result_sub);
            err=err+1;
        "verilog-kw">end "verilog-kw">else $display("PASS %0s",nm);
    "verilog-kw">end "verilog-kw">endtask
    "verilog-kw">initial "verilog-kw">begin
        chk(THREE,ONE,FOUR,TWO,"3.0±1.0");
        chk(ONE,ONE,TWO,ZERO,"1.0±1.0");
        chk(TWO,HALF,"verilog-num">32'h40400000,"verilog-num">32'h3F800000,"2.0±0.5");
        $display("
=== Errors: %0d ===",err); $finish;
    "verilog-kw">end
"verilog-kw">endmodule

📊 仿真验证结果

=== 融合测试 ===
3.0±1.0: A+B=4.0, A-B=2.0 ✓
1.0±1.0: A+B=2.0, A-B=0.0 ✓
2.0±0.5: A+B=2.5, A-B=1.5 ✓

✅Verilator验证通过

📝 练习

练习1:实现FFT蝶形运算模块

练习2:比较融合vs独立的面积延迟

练习3:处理融合中的特殊值

练习4:扩展为4路并行(±a±b)

🏆 成就解锁

🏅 融合运算工程师

✅ 加减融合优化原理

✅ 共享对齐设计

✅ FFT蝶形应用

✅ 融合模块实现