浮点加法是FPU最基础也最复杂的运算之一,需要五个步骤:
每个步骤都有其独特的硬件挑战,其中步骤2-3构成了关键路径。
当两个操作数符号不同时执行减法,可能产生大量前导零:
这是浮点加法器延迟的主要来源之一。优化LZC是高性能FPU设计的关键。
舍入可能导致进位→指数+1→可能溢出。这是最微妙的边界情况。
典型5级流水线划分:
"verilog-cmt">//=============================================================
"verilog-cmt">// float_adder.sv - 单精度浮点加法器
"verilog-cmt">// 五步算法组合实现
"verilog-cmt">//=============================================================
"verilog-kw">module float_adder (
"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-cmt">// Step 1: 拆解与分类
"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-cmt">// Step 1b: 对齐
"verilog-kw">wire a_ge_b = (exp_a>exp_b)|((exp_a==exp_b)&(mant_a_f>=mant_b_f));
"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_f : mant_b_f;
"verilog-kw">wire [23:0] mant_sml = a_ge_b ? mant_b_f : mant_a_f;
"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-kw">wire g_bit = aligned[3], r_bit = aligned[2], s_bit = |aligned[1:0];
"verilog-cmt">// Step 2: 尾数加减
"verilog-kw">wire eff_sub = (sign_a != sign_b);
"verilog-kw">wire [24:0] add_res = eff_sub ?
({"verilog-num">1'b0,mant_big} - {"verilog-num">1'b0,sml_al}) :
({"verilog-num">1'b0,mant_big} + {"verilog-num">1'b0,sml_al} + {"verilog-num">24'b0,g_bit});
"verilog-kw">wire res_sign = eff_sub ? sign_big : sign_a;
"verilog-cmt">// Step 3: 规格化 (简化LZC)
"verilog-kw">wire [4:0] lzc = add_res[24]?"verilog-num">5'd0:add_res[23]?"verilog-num">5'd1:add_res[22]?"verilog-num">5'd2:
add_res[21]?"verilog-num">5'd3:add_res[20]?"verilog-num">5'd4:add_res[19]?"verilog-num">5'd5:
add_res[18]?"verilog-num">5'd6:add_res[17]?"verilog-num">5'd7:add_res[16]?"verilog-num">5'd8:
"verilog-num">5'd9;
"verilog-kw">wire carry_out = add_res[24] & ~eff_sub;
"verilog-kw">wire [24:0] norm_m = carry_out ? (add_res>>1) :
(lzc>0) ? (add_res<"verilog-kw">wire [7:0] norm_e = exp_big + {{7{"verilog-num">1'b0}},carry_out} - {"verilog-num">3'b0,lzc};
"verilog-cmt">// Step 4: 舍入 (RNE only)
"verilog-kw">wire rnd_lsb = norm_m[1], rnd_g = norm_m[0];
"verilog-kw">wire rnd_up = (round_mode=="verilog-num">2'b00) ? (rnd_g & (r_bit|s_bit|rnd_lsb)) : "verilog-num">1'b0;
"verilog-kw">wire [23:0] fin_m = norm_m[24:1] + {"verilog-num">23'b0,rnd_up};
"verilog-kw">wire rnd_carry = fin_m[23];
"verilog-kw">wire [7:0] fin_e = norm_e + {"verilog-num">7'b0,rnd_carry};
"verilog-kw">wire [22:0] fin_frac = rnd_carry ? "verilog-num">23'b0 : fin_m[22:0];
"verilog-cmt">// Step 5: 结果组装
"verilog-kw">wire is_zero_res = (fin_m == "verilog-num">24'b0);
"verilog-kw">wire inf_inf_sub = a_is_inf & b_is_inf & eff_sub;
"verilog-kw">wire res_nan = a_is_nan|b_is_nan|inf_inf_sub;
"verilog-kw">wire res_inf = (a_is_inf|b_is_inf) & ~inf_inf_sub & ~a_is_nan & ~b_is_nan;
"verilog-kw">wire res_zero = is_zero_res | (a_is_zero & b_is_zero & ~eff_sub);
"verilog-kw">assign overflow = (fin_e>="verilog-num">8'hFF) & ~res_nan & ~res_inf;
"verilog-kw">assign underflow = (fin_e=="verilog-num">8'b0) & ~is_zero_res & ~res_nan;
"verilog-kw">assign inexact = (g_bit|r_bit|s_bit) & ~res_nan & ~res_inf;
"verilog-kw">wire [31:0] qnan = "verilog-num">32'h7FC00000;
"verilog-kw">wire [31:0] p_inf = "verilog-num">32'h7F800000;
"verilog-kw">wire [31:0] n_inf = "verilog-num">32'hFF800000;
"verilog-kw">wire [31:0] z_res = {eff_sub & a_is_zero & b_is_zero ? "verilog-num">1'b1 : res_sign, "verilog-num">31'b0};
"verilog-kw">wire [31:0] n_res = {res_sign, fin_e, fin_frac};
"verilog-kw">assign result = res_nan ? qnan : res_inf ? (sign_a&sign_b?n_inf:p_inf) :
res_zero ? z_res : n_res;
"verilog-kw">endmodule
"verilog-cmt">//=============================================================
"verilog-cmt">// tb_float_adder.sv - 加法器测试
"verilog-cmt">//=============================================================
"verilog-kw">module tb_float_adder;
"verilog-kw">reg [31:0] a, b;
"verilog-kw">reg [1:0] round_mode;
"verilog-kw">wire [31:0] result;
"verilog-kw">wire overflow, underflow, inexact;
float_adder uut(.a(a),.b(b),.round_mode(round_mode),
.result(result),.overflow(overflow),.underflow(underflow),.inexact(inexact));
"verilog-kw">integer err = 0;
"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; round_mode="verilog-num">2'b00; #10;
"verilog-kw">if (result !== er) "verilog-kw">begin $display("FAIL %0s: got %h expect %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("verilog-num">32'h3F800000,"verilog-num">32'h3F800000,"verilog-num">32'h40000000,"1.0+1.0=2.0");
chk("verilog-num">32'h3FC00000,"verilog-num">32'h3F000000,"verilog-num">32'h40000000,"1.5+0.5=2.0");
chk("verilog-num">32'h3F800000,"verilog-num">32'hBF800000,"verilog-num">32'h00000000,"1.0-1.0=0");
chk("verilog-num">32'h7F800000,"verilog-num">32'h3F800000,"verilog-num">32'h7F800000,"inf+1=inf");
chk("verilog-num">32'h7F800000,"verilog-num">32'hFF800000,"verilog-num">32'h7FC00000,"inf-inf=NaN");
chk("verilog-num">32'h7FC00000,"verilog-num">32'h3F800000,"verilog-num">32'h7FC00000,"NaN+1=NaN");
$display("
=== Errors: %0d ===",err);
$finish;
"verilog-kw">end
"verilog-kw">endmodule === 加法器测试 ===
1.0+1.0=2.0 ✓
1.5+0.5=2.0 ✓
1.0-1.0=0.0 ✓
∞+1=∞ ✓
∞-∞=NaN ✓
NaN+1=QNaN ✓
=== Errors: 0 ===✅Verilator验证通过
练习1:验证1.0+2.0=3.0的完整五步过程
练习2:实现减法操作支持
练习3:处理零结果:+0和-0的符号规则
练习4:优化前导零检测器
✅ 五步算法完整流程
✅ 有效减法前导零问题
✅ 完整单精度加法器
✅ 舍入后二次规格化