📘 第13课:浮点除法(SRT)

🎯 本课目标

📖 浮点除法基本原理

浮点除法:指数相减、尾数相除、符号异或。除法的核心挑战是尾数除法——与乘法不同,除法是迭代过程,无法一步完成。这是FPU中最耗时的运算之一。

A/B = (-1)^(sa⊕sb) × 2^(ea-eb) × (ma/mb)
示例: 1.10×2³ / 1.01×2¹ 指数: 3 - 1 = 2 尾数: 1.10 / 1.01 ≈ 1.011 符号: 0⊕0 = 0 结果: +1.011 × 2²

📖 SRT除法算法

SRT(Sweeney-Robertson-Tocher)是最常用的硬件除法算法:

SRT基2除法步骤: 1. 初始化: R₀ = 被除数, D = 除数 2. 每步选择商位 qᵢ ∈ {-1, 0, +1} 3. 更新余数: Rᵢ₊₁ = 2×Rᵢ - qᵢ×D 4. 重复24次(单精度) 商位选择(基于余数高几位): R ∈ [0.5, 1.0) → q=+1 R ∈ [-0.5, 0.5) → q=0 R ∈ (-1.0,-0.5] → q=-1

SRT核心优势:商位选择只依赖余数高几位,不需要完整比较器,延迟很低。

📖 基4 SRT与高基除法

基4 SRT每次产生2位商,迭代次数减半但选择逻辑更复杂:

基4 SRT: 商位 qᵢ ∈ {-2, -1, 0, +1, +2} 每步2位商, 单精度12次迭代(vs基2的24次) 代价: 更复杂的商选择逻辑(P-D图) 收益: 速度提升约1.8×

📖 SRT除法器关键路径

SRT除法器延迟由迭代次数决定:

基2 SRT: 24次迭代 × (选择+减法+移位) 基4 SRT: 12次迭代 × (选择+减法+移位) 每次迭代约2级逻辑 优化: 迭代展开(unrolling) 2次展开: 每2步1周期 4次展开: 每4步1周期 面积增加但延迟减少

📖 除法的特殊值处理

除法有最多的特殊值情况需要处理:

ABA/B异常
±∞±有限非零±∞
±有限非零0±∞DivByZero
±∞±∞NaNInvalid
00NaNInvalid
NaN任意NaN
0±∞±0

🔧 Verilog实现

"verilog-cmt">//=============================================================
"verilog-cmt">// float_divider_srt.sv - SRT浮点除法器
"verilog-cmt">// 支持特殊值处理和DivByZero/Invalid异常
"verilog-cmt">//=============================================================
"verilog-kw">module float_divider_srt (
    "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-kw">wire [47:0] quotient = (ma << 24) / {"verilog-num">24'b0, mb};
    "verilog-kw">wire [8:0] res_exp = quotient[47] ? exp_diff : (exp_diff - "verilog-num">9'd1);
    "verilog-kw">wire [23:0] res_mant = quotient[47] ? quotient[47:24] : quotient[46:23];
    "verilog-kw">wire g = quotient[47] ? quotient[23] : quotient[22];
    "verilog-kw">wire r = quotient[47] ? quotient[22] : quotient[21];
    "verilog-kw">wire s = quotient[47] ? (|quotient[21:0]) : (|quotient[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-cmt">//=============================================================
"verilog-cmt">// tb_float_div_srt.sv - SRT除法器完整测试
"verilog-cmt">//=============================================================
"verilog-kw">module tb_float_div_srt;
    "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_divider_srt 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");
        chk("verilog-num">32'h7F800000,"verilog-num">32'h7F800000,"verilog-num">32'h7FC00000,"inf/inf=NaN");
        chk("verilog-num">32'h00000000,"verilog-num">32'h00000000,"verilog-num">32'h7FC00000,"0/0=NaN");
        $display("\n=== Errors: %0d ===",err); $finish;
    "verilog-kw">end
"verilog-kw">endmodule

📊 仿真验证结果

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

✅Verilator验证通过

📝 练习

练习1:实现真正SRT迭代24步

练习2:基4SRT设计验证

练习3:SRT vs 恢复余数法对比

练习4:迭代展开优化

🏆 成就解锁

🏅 SRT除法专家

✅ SRT商位选择规则

✅ 基4SRT优化

✅ 除法关键路径

✅ 迭代展开技术