Skip to main content

FP32_ADD_V

Constant FP32_ADD_V 

Source
pub const FP32_ADD_V: &str = r#"module fp32_add(input [31:0] a, input [31:0] b_in, input sub, output reg [31:0] y);
  localparam [31:0] QNAN = 32'h7FC00000;
  reg [31:0] b;
  reg sa, sb;
  reg [7:0] ea, eb;
  reg [22:0] ma, mb;
  reg azero,bzero,ainf,binf,anan,bnan,asub,bsub;
  reg [8:0] xa, xb;
  reg ha, hb;
  reg [23:0] siga, sigb;
  reg [11:0] exp_l, shamt, lead;
  reg [23:0] sig_big, sig_small;
  reg sgn_big, sgn_small, res_sign, op_is_sub;
  reg [127:0] big_ext, small_ext, sum_ext, norm_sig;
  integer i;
  reg signed [11:0] norm_exp;
  reg [24:0] rounded;
  reg guard, roundb, sticky, lsb, round_up;
  reg [23:0] final_sig;

  function [31:0] pack_sub32(input sgn, input [127:0] wsum, input [11:0] wlead, input signed [11:0] bexp);
    reg [11:0] rshift; reg [127:0] win; reg g2,r2,s2,l2,up2; reg [24:0] rr;
    begin
      rshift = 12'd1 - bexp;
      if (wlead >= 12'd26) begin
        win = wsum >> (wlead - 12'd26);
        if (|(wsum & ((128'd1 << (wlead - 12'd26)) - 128'd1))) win[0] = 1'b1;
      end else begin
        win = wsum << (12'd26 - wlead);
      end
      if (rshift >= 12'd128) begin s2 = |win; win = 128'd0; win[0] = s2; end
      else if (rshift != 12'd0) begin
        if (|(win & ((128'd1 << rshift) - 128'd1))) begin win = win >> rshift; win[0] = 1'b1; end
        else win = win >> rshift;
      end
      g2 = win[2]; r2 = win[1]; s2 = win[0]; l2 = win[3];
      up2 = g2 & (r2 | s2 | l2);
      rr = {1'b0, win[26:3]} + (up2 ? 25'd1 : 25'd0);
      if (rr[23]) pack_sub32 = {sgn, 8'd1, rr[22:0]};
      else        pack_sub32 = {sgn, 8'd0, rr[22:0]};
    end
  endfunction

  always @(*) begin
    b  = sub ? {~b_in[31], b_in[30:0]} : b_in;
    sa = a[31]; sb = b[31];
    ea = a[30:23]; eb = b[30:23];
    ma = a[22:0];  mb = b[22:0];
    azero = (ea==8'd0)  && (ma==23'd0); bzero = (eb==8'd0)  && (mb==23'd0);
    ainf  = (ea==8'hFF) && (ma==23'd0); binf  = (eb==8'hFF) && (mb==23'd0);
    anan  = (ea==8'hFF) && (ma!=23'd0); bnan  = (eb==8'hFF) && (mb!=23'd0);
    asub  = (ea==8'd0)  && (ma!=23'd0); bsub  = (eb==8'd0)  && (mb!=23'd0);
    xa = asub ? 9'd1 : {1'b0, ea};
    xb = bsub ? 9'd1 : {1'b0, eb};
    ha = (ea!=8'd0); hb = (eb!=8'd0);
    siga = {ha, ma}; sigb = {hb, mb};
    y = 32'd0;
    // pre-set datapath regs (avoid latches in the combinational always)
    exp_l=12'd0; shamt=12'd0; sig_big=24'd0; sig_small=24'd0; sgn_big=1'b0; sgn_small=1'b0;
    op_is_sub=1'b0; res_sign=1'b0; big_ext=128'd0; small_ext=128'd0; sum_ext=128'd0; lead=12'd0;
    norm_exp=12'sd0; norm_sig=128'd0; rounded=25'd0; final_sig=24'd0;
    guard=1'b0; roundb=1'b0; sticky=1'b0; lsb=1'b0; round_up=1'b0;

    if (anan || bnan) y = QNAN;
    else if (ainf && binf) y = (sa==sb) ? {sa,8'hFF,23'd0} : QNAN;
    else if (ainf) y = {sa,8'hFF,23'd0};
    else if (binf) y = {sb,8'hFF,23'd0};
    else if (azero && bzero) y = (sa==sb) ? {sa,31'd0} : 32'd0;
    else if (azero) y = b;
    else if (bzero) y = a;
    else begin
      if (xa > xb || (xa == xb && siga >= sigb)) begin
        exp_l={3'd0,xa}; sig_big=siga; sgn_big=sa; sig_small=sigb; sgn_small=sb; shamt={3'd0,(xa-xb)};
      end else begin
        exp_l={3'd0,xb}; sig_big=sigb; sgn_big=sb; sig_small=siga; sgn_small=sa; shamt={3'd0,(xb-xa)};
      end
      op_is_sub = (sgn_big != sgn_small);
      res_sign  = sgn_big;
      big_ext = {40'd0, sig_big, 64'd0};
      if (shamt >= 12'd128) begin small_ext = 128'd0; small_ext[0] = |sig_small; end
      else begin
        small_ext = ({40'd0, sig_small, 64'd0}) >> shamt;
        if (shamt != 12'd0 && |(({40'd0, sig_small, 64'd0}) & ((128'd1 << shamt) - 128'd1))) small_ext[0] = 1'b1;
      end
      if (op_is_sub) sum_ext = big_ext - small_ext; else sum_ext = big_ext + small_ext;
      if (sum_ext == 128'd0) y = 32'd0;
      else begin
        lead = 12'd0;
        for (i = 127; i >= 0; i = i - 1) if (sum_ext[i] && lead == 12'd0) lead = i[11:0];
        norm_exp = $signed({3'd0, exp_l[8:0]}) + $signed({3'd0, lead[8:0]}) - 12'sd87;
        if (lead >= 12'd26) begin
          norm_sig = sum_ext >> (lead - 12'd26);
          if (|(sum_ext & ((128'd1 << (lead - 12'd26)) - 128'd1))) norm_sig[0] = 1'b1;
        end else norm_sig = sum_ext << (12'd26 - lead);
        guard=norm_sig[2]; roundb=norm_sig[1]; sticky=norm_sig[0]; lsb=norm_sig[3];
        round_up = guard & (roundb | sticky | lsb);
        rounded = {1'b0, norm_sig[26:3]} + (round_up ? 25'd1 : 25'd0);
        if (rounded[24]) begin final_sig = rounded[24:1]; norm_exp = norm_exp + 12'sd1; end
        else final_sig = rounded[23:0];
        if (norm_exp >= 12'sd255) y = {res_sign, 8'hFF, 23'd0};
        else if (norm_exp <= 12'sd0) y = pack_sub32(res_sign, sum_ext, lead, norm_exp);
        else y = {res_sign, norm_exp[7:0], final_sig[22:0]};
      end
    end
  end
endmodule
"#;
Expand description

The synthesizable combinational IEEE-754 f32 adder — a faithful port of the PROVEN f64 fp_addsub (benchmarks/silicon/fpu.v): align both significands in a wide 128-bit window (64 bits of alignment room, so every bit survives to sticky), add/sub, leading-zero-count, normalize the leading 1 to bit 26 (24-bit significand at [26:3], guard/round/sticky at [2:0]), round-to-nearest-ties-even, and pack (overflow→Inf, underflow→subnormal via pack_sub32). sub=1 computes a-b. Proven bit-exact to Rust f32 under iverilog by simd_float_rtl_lock. This module is instantiated once per lane by the packed F32×2 tile.