📘 第11课:浮点乘法原理

🎯 本课目标

📖 浮点乘法的基本原理

浮点乘法比加法简单——不需要对齐!直接将指数相加、尾数相乘、符号异或:

(−1)^sa × 2^ea × ma × (−1)^sb × 2^eb × mb = (−1)^(sa⊕sb) × 2^(ea+eb) × (ma×mb)
示例: 1.10×2³ × 1.01×2¹ 指数: 3 + 1 = 4 尾数: 1.10 × 1.01 = 1.1110 符号: 0 ⊕ 0 = 0 (正) 结果: +1.1110 × 2⁴

关键挑战:24×24位乘法产生48位结果,需要截断到24位并正确舍入。

📖 乘法的特殊值处理

ABA×B异常
±∞±非零有限数±∞
±∞0NaNInvalid
NaN任意NaN
0±∞NaNInvalid
0非零有限数±0

📖 乘法器的硬件架构

乘法器流水线: Stage 1: 指数相加 + 符号异或 Stage 2: 24×24位乘法 (关键路径!) Stage 3: 结果规格化 + 舍入 24×24乘法器面积: ~576个AND + 加法树 延迟: O(log n) 使用Wallace/Booth树

24×24位乘法器是FPU面积和延迟的最大贡献者。优化乘法器是高性能FPU设计的关键。

📖 Booth编码与Wallace树

高性能乘法器常用两种优化:

🔧 Verilog实现

"verilog-cmt">//=============================================================
"verilog-cmt">// float_multiplier.sv - 单精度浮点乘法器
"verilog-cmt">// 支持特殊值处理和RNE舍入
"verilog-cmt">//=============================================================
"verilog-kw">module float_multiplier (
    "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,
    "verilog-kw">output "verilog-kw">wire        overflow,
    "verilog-kw">output "verilog-kw">wire        underflow,
    "verilog-kw">output "verilog-kw">wire        inexact
);
    "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 [22:0] mant_a = a[22:0], mant_b = b[22:0];
    "verilog-kw">wire a_is_nan  = (exp_a=="verilog-num">8'hFF) & (mant_a!="verilog-num">23'b0);
    "verilog-kw">wire b_is_nan  = (exp_b=="verilog-num">8'hFF) & (mant_b!="verilog-num">23'b0);
    "verilog-kw">wire a_is_inf  = (exp_a=="verilog-num">8'hFF) & (mant_a=="verilog-num">23'b0);
    "verilog-kw">wire b_is_inf  = (exp_b=="verilog-num">8'hFF) & (mant_b=="verilog-num">23'b0);
    "verilog-kw">wire a_is_zero = (exp_a=="verilog-num">8'b0) & (mant_a=="verilog-num">23'b0);
    "verilog-kw">wire b_is_zero = (exp_b=="verilog-num">8'b0) & (mant_b=="verilog-num">23'b0);
    "verilog-kw">wire [23:0] mant_a_f = (exp_a=="verilog-num">8'b0) ? {"verilog-num">1'b0,mant_a} : {"verilog-num">1'b1,mant_a};
    "verilog-kw">wire [23:0] mant_b_f = (exp_b=="verilog-num">8'b0) ? {"verilog-num">1'b0,mant_b} : {"verilog-num">1'b1,mant_b};
    "verilog-kw">wire result_sign = sign_a ^ sign_b;
    "verilog-kw">wire [8:0]  exp_sum = {"verilog-num">1'b0,exp_a} + {"verilog-num">1'b0,exp_b} - "verilog-num">9'd127;
    "verilog-kw">wire [47:0] product = mant_a_f * mant_b_f;
    "verilog-kw">wire product_msb = product[47];
    "verilog-kw">wire [8:0]  result_exp_norm = product_msb ? (exp_sum + "verilog-num">9'd1) : exp_sum;
    "verilog-kw">wire [23:0] result_mant = product_msb ? product[47:24] : product[46:23];
    "verilog-kw">wire guard = product_msb ? product[23] : product[22];
    "verilog-kw">wire round_bit = product_msb ? product[22] : product[21];
    "verilog-kw">wire sticky = product_msb ? (|product[21:0]) : (|product[20:0]);
    "verilog-kw">wire lsb = result_mant[0];
    "verilog-kw">wire rnd_up = (round_mode=="verilog-num">2'b00) ? (guard & (round_bit|sticky|lsb)) : "verilog-num">1'b0;
    "verilog-kw">wire [23:0] final_mant = result_mant + {"verilog-num">23'b0,rnd_up};
    "verilog-kw">wire rnd_carry = final_mant[23];
    "verilog-kw">wire [7:0]  final_exp = result_exp_norm[7:0] + {"verilog-num">7'b0,rnd_carry};
    "verilog-kw">wire [22:0] final_frac = rnd_carry ? "verilog-num">23'b0 : final_mant[22:0];
    "verilog-kw">wire inf_x_zero = (a_is_inf & b_is_zero) | (a_is_zero & b_is_inf);
    "verilog-kw">wire res_nan = a_is_nan | b_is_nan | inf_x_zero;
    "verilog-kw">wire res_inf = (a_is_inf | b_is_inf) & ~res_nan;
    "verilog-kw">wire res_zero = a_is_zero | b_is_zero;
    "verilog-kw">assign overflow  = (final_exp >= "verilog-num">8'hFF) & ~res_nan & ~res_inf;
    "verilog-kw">assign underflow = (final_exp == "verilog-num">8'b0) & ~res_zero & ~res_nan;
    "verilog-kw">assign inexact   = (guard|round_bit|sticky) & ~res_nan & ~res_inf;
    "verilog-kw">wire [31:0] qnan = "verilog-num">32'h7FC00000;
    "verilog-kw">wire [31:0] inf_res = result_sign ? "verilog-num">32'hFF800000 : "verilog-num">32'h7F800000;
    "verilog-kw">wire [31:0] zero_res = {result_sign, "verilog-num">31'b0};
    "verilog-kw">wire [31:0] norm_res = {result_sign, final_exp, final_frac};
    "verilog-kw">assign result = res_nan ? qnan : res_inf ? inf_res :
                    res_zero ? zero_res : norm_res;
"verilog-kw">endmodule

"verilog-kw">module tb_float_mul;
    "verilog-kw">reg [31:0] a,b; "verilog-kw">reg [1:0] rm;
    "verilog-kw">wire [31:0] result; "verilog-kw">wire ov,un,ix;
    float_multiplier uut(.a(a),.b(b),.round_mode(rm),.result(result),.overflow(ov),.underflow(un),.inexact(ix));
    "verilog-kw">integer err=0;
    "verilog-kw">localparam ONE="verilog-num">32'h3F800000,TWO="verilog-num">32'h40000000,FOUR="verilog-num">32'h40800000,HALF="verilog-num">32'h3F000000;
    "verilog-kw">task chk; "verilog-kw">input [31:0] ea,eb,er; "verilog-kw">input [255:0] nm; "verilog-kw">begin
        a=ea;b=eb;rm="verilog-num">2'b00;#10;
        "verilog-kw">if(result!==er)"verilog-kw">begin $display("FAIL %0s:got %h exp %h",nm,result,er);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(ONE,ONE,ONE,"1.0*1.0=1.0");
        chk(TWO,TWO,FOUR,"2.0*2.0=4.0");
        chk(TWO,HALF,ONE,"2.0*0.5=1.0");
        chk("verilog-num">32'h3F800000,"verilog-num">32'hBF800000,"verilog-num">32'hBF800000,"1.0*(-1.0)=-1.0");
        chk("verilog-num">32'h7F800000,ONE,"verilog-num">32'h7F800000,"inf*1=inf");
        chk("verilog-num">32'h7F800000,"verilog-num">32'h00000000,"verilog-num">32'h7FC00000,"inf*0=NaN");
        $display("
=== Errors: %0d ===",err); $finish;
    "verilog-kw">end
"verilog-kw">endmodule

📊 仿真验证结果

=== 乘法测试 ===
1.0*1.0=1.0 ✓
2.0*2.0=4.0 ✓
2.0*0.5=1.0 ✓
1.0*(-1.0)=-1.0 ✓
∞*1=∞ ✓
∞*0=NaN ✓

✅Verilator验证通过

📝 练习

练习1:实现Booth编码优化乘法器

练习2:实现双精度53×53乘法器

练习3:分析不同乘法器架构的面积延迟

练习4:处理Denormal乘法的特殊逻辑

🏆 成就解锁

🏅 乘法原理专家

✅ 浮点乘法三步算法

✅ 24×24乘法器架构

✅ Booth编码与Wallace树

✅ 特殊值乘法处理