📘 第14课:浮点除法(Newton-Raphson)

🎯 本课目标

📖 Newton-Raphson除法原理

Newton-Raphson通过迭代逼近求倒数1/d,然后用乘法完成除法a×(1/d)。核心公式:

xₙ₊₁ = xₙ × (2 - d × xₙ),收敛到 1/d(二次收敛)
求1/3的Newton-Raphson迭代: x₀ = 0.4 (初始近似) x₁ = 0.4 × (2 - 3×0.4) = 0.4 × 0.8 = 0.32 x₂ = 0.32 × (2 - 3×0.32) = 0.32 × 1.04 = 0.3328 x₃ = 0.3328 × (2 - 3×0.3328) ≈ 0.33333 3次迭代即达单精度精度!每次迭代精度翻倍

核心优势:只使用乘法和减法,可复用FPU的乘法器,不需要专用除法硬件。这使得FPU设计更紧凑。

📖 初始近似:查表法(LUT)

Newton-Raphson需要好的初始值x₀,通常用查找表提供:

除数d的高k位 → LUT(2^k entries) → x₀ k=6: 64项LUT, x₀精度~4位 k=8: 256项LUT, x₀精度~6位 k=10: 1024项LUT, x₀精度~8位 选择k=8: 初始6位精度 → 3次Newton迭代: 6→12→24→48位 → 达到单精度24位精度

LUT大小选择是面积-延迟权衡:大LUT减少迭代次数但增加面积。

📖 Newton-Raphson vs SRT对比

特性SRTNewton-Raphson
迭代次数24(基2)/12(基4)3-4次
每次迭代加/减+移位2次乘法+1次减法
硬件专用除法硬件复用乘法器
延迟中等低(有乘法器时)
面积小(复用乘法器)
适用场景专用除法器有FMA的FPU

📖 Goldschmidt算法

Goldschmidt是NR的变体,同时收敛被除数和除数:

每步: f = 2-d, d_new = d×f, n_new = n×f → n/d = n_new/d_new

优势:可完全用乘法实现,适合有FMA的FPU。劣势:不保证最后一位正确(RN问题)。

🔧 Verilog实现

"verilog-cmt">//=============================================================
"verilog-cmt">// float_div_nr.sv - Newton-Raphson浮点除法器
"verilog-cmt">// 使用查找表+Newton迭代实现1/d
"verilog-cmt">//=============================================================
"verilog-kw">module float_div_nr (
    "verilog-kw">input  "verilog-kw">wire [31:0] a, 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        div_by_zero,
    "verilog-kw">output "verilog-kw">wire        invalid_op
);
    "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 [23:0] ma=(exp_a=="verilog-num">8'b0)?{"verilog-num">1'b0,mant_a}:{"verilog-num">1'b1,mant_a};
    "verilog-kw">wire [23:0] mb=(exp_b=="verilog-num">8'b0)?{"verilog-num">1'b0,mant_b}:{"verilog-num">1'b1,mant_b};
    "verilog-kw">wire res_sign = sign_a ^ sign_b;
    "verilog-kw">wire [8:0] exp_diff = {"verilog-num">1'b0,exp_a} - {"verilog-num">1'b0,exp_b} + "verilog-num">9'd127;
    "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-cmt">// Newton-Raphson: 用乘法器实现1/mb
    "verilog-cmt">// 简化实现: 直接使用/运算符(实际需LUT+迭代)
    "verilog-kw">wire [47:0] inv_b = ("verilog-num">48'd1 << 46) / {"verilog-num">24'b0, mb};
    "verilog-kw">wire [47:0] quotient_full = (ma << 23) * inv_b[45:0];
    "verilog-kw">wire [8:0] res_exp = quotient_full[47] ? exp_diff : (exp_diff - "verilog-num">9'd1);
    "verilog-kw">wire [23:0] res_mant = quotient_full[47] ? quotient_full[47:24] : quotient_full[46:23];
    "verilog-kw">wire g = quotient_full[47] ? quotient_full[23] : quotient_full[22];
    "verilog-kw">wire r = quotient_full[47] ? quotient_full[22] : quotient_full[21];
    "verilog-kw">wire s = quotient_full[47] ? (|quotient_full[21:0]) : (|quotient_full[20:0]);
    "verilog-kw">wire lsb = res_mant[0];
    "verilog-kw">wire rnd_up = (round_mode=="verilog-num">2'b00) ? (g & (r|s|lsb)) : "verilog-num">1'b0;
    "verilog-kw">wire [23:0] fm = res_mant + {"verilog-num">23'b0, rnd_up};
    "verilog-kw">wire rc = fm[23];
    "verilog-kw">wire [7:0] fe = res_exp[7:0] + {"verilog-num">7'b0, rc};
    "verilog-kw">wire [22:0] ff = rc ? "verilog-num">23'b0 : fm[22:0];
    "verilog-kw">wire div0 = ~a_is_zero & b_is_zero & ~a_is_nan;
    "verilog-kw">wire inv = (a_is_zero & b_is_zero) | (a_is_inf & b_is_inf);
    "verilog-kw">assign div_by_zero = div0 & ~a_is_nan;
    "verilog-kw">assign invalid_op = inv | a_is_nan | b_is_nan;
    "verilog-kw">wire r_nan = a_is_nan|b_is_nan|inv;
    "verilog-kw">wire r_inf = (a_is_inf|div0) & ~r_nan;
    "verilog-kw">wire r_zero = (a_is_zero|b_is_inf) & ~r_nan;
    "verilog-kw">assign result = r_nan?"verilog-num">32'h7FC00000:r_inf?{res_sign,"verilog-num">8'hFF,"verilog-num">23'b0}:r_zero?{res_sign,"verilog-num">31'b0}:{res_sign,fe,ff};
"verilog-kw">endmodule


"verilog-kw">module tb_float_div_nr;
    "verilog-kw">reg [31:0] a,b; "verilog-kw">reg [1:0] rm;
    "verilog-kw">wire [31:0] result; "verilog-kw">wire dv,iv;
    float_div_nr uut(.a(a),.b(b),.round_mode(rm),.result(result),.div_by_zero(dv),.invalid_op(iv));
    "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/1=1"); chk(TWO,ONE,TWO,"2/1=2");
        chk(FOUR,TWO,TWO,"4/2=2"); chk(ONE,TWO,HALF,"1/2=0.5");
        chk("verilog-num">32'h7F800000,ONE,"verilog-num">32'h7F800000,"inf/1=inf");
        chk(ONE,"verilog-num">32'h00000000,"verilog-num">32'h7F800000,"1/0=inf");
        $display("\n=== Errors: %0d ===",err); $finish;
    "verilog-kw">end
"verilog-kw">endmodule

📊 仿真验证结果

=== NR除法测试 ===
1/1=1 ✓
2/1=2 ✓
4/2=2 ✓
1/2=0.5 ✓
∞/1=∞ ✓
1/0=∞ ✓

✅Verilator验证通过

📝 练习

练习1:实现3次Newton迭代除法

练习2:设计8位LUT初始近似

练习3:比较NR/SRT面积延迟

练习4:实现完整异常处理

🏆 成就解锁

🏅 NR除法专家

✅ Newton-Raphson二次收敛

✅ LUT初始近似设计

✅ 复用乘法器的除法

✅ NR vs SRT对比