Skip to main content

FP16_ADD_V

Constant FP16_ADD_V 

Source
pub const FP16_ADD_V: &str = r#"module fp16_add(input [15:0] a, input [15:0] b_in, input sub, output reg [15:0] y);
  localparam [15:0] QNAN = 16'h7E00;
  reg [15:0] b;
  reg sa, sb;
  reg [4:0] ea, eb;
  reg [9:0] ma, mb;
  reg azero,bzero,ainf,binf,anan,bnan,asub,bsub;
  reg [5:0] xa, xb;
  reg ha, hb;
  reg [10:0] siga, sigb, sig_big, sig_small, final_sig;
  reg [11:0] exp_l, shamt, lead;
  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 [11:0] rounded;
  reg guard, roundb, sticky, lsb, round_up;

  function [15:0] pack_sub16(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 [11:0] rr;
    begin
      rshift = 12'd1 - bexp;
      if (wlead >= 12'd13) begin
        win = wsum >> (wlead - 12'd13);
        if (|(wsum & ((128'd1 << (wlead - 12'd13)) - 128'd1))) win[0] = 1'b1;
      end else begin
        win = wsum << (12'd13 - 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[13:3]} + (up2 ? 12'd1 : 12'd0);
      if (rr[10]) pack_sub16 = {sgn, 5'd1, rr[9:0]};
      else        pack_sub16 = {sgn, 5'd0, rr[9:0]};
    end
  endfunction

  always @(*) begin
    b  = sub ? {~b_in[15], b_in[14:0]} : b_in;
    sa = a[15]; sb = b[15];
    ea = a[14:10]; eb = b[14:10];
    ma = a[9:0];   mb = b[9:0];
    azero = (ea==5'd0)  && (ma==10'd0); bzero = (eb==5'd0)  && (mb==10'd0);
    ainf  = (ea==5'h1F) && (ma==10'd0); binf  = (eb==5'h1F) && (mb==10'd0);
    anan  = (ea==5'h1F) && (ma!=10'd0); bnan  = (eb==5'h1F) && (mb!=10'd0);
    asub  = (ea==5'd0)  && (ma!=10'd0); bsub  = (eb==5'd0)  && (mb!=10'd0);
    xa = asub ? 6'd1 : {1'b0, ea};
    xb = bsub ? 6'd1 : {1'b0, eb};
    ha = (ea!=5'd0); hb = (eb!=5'd0);
    siga = {ha, ma}; sigb = {hb, mb};
    y = 16'd0;
    exp_l=12'd0; shamt=12'd0; sig_big=11'd0; sig_small=11'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=12'd0; final_sig=11'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,5'h1F,10'd0} : QNAN;
    else if (ainf) y = {sa,5'h1F,10'd0};
    else if (binf) y = {sb,5'h1F,10'd0};
    else if (azero && bzero) y = (sa==sb) ? {sa,15'd0} : 16'd0;
    else if (azero) y = b;
    else if (bzero) y = a;
    else begin
      if (xa > xb || (xa == xb && siga >= sigb)) begin
        exp_l={6'd0,xa}; sig_big=siga; sgn_big=sa; sig_small=sigb; sgn_small=sb; shamt={6'd0,(xa-xb)};
      end else begin
        exp_l={6'd0,xb}; sig_big=sigb; sgn_big=sb; sig_small=siga; sgn_small=sa; shamt={6'd0,(xb-xa)};
      end
      op_is_sub = (sgn_big != sgn_small);
      res_sign  = sgn_big;
      big_ext = {53'd0, sig_big, 64'd0};
      if (shamt >= 12'd128) begin small_ext = 128'd0; small_ext[0] = |sig_small; end
      else begin
        small_ext = ({53'd0, sig_small, 64'd0}) >> shamt;
        if (shamt != 12'd0 && |(({53'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 = 16'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({6'd0, exp_l[5:0]}) + $signed({3'd0, lead[8:0]}) - 12'sd74;
        if (lead >= 12'd13) begin
          norm_sig = sum_ext >> (lead - 12'd13);
          if (|(sum_ext & ((128'd1 << (lead - 12'd13)) - 128'd1))) norm_sig[0] = 1'b1;
        end else norm_sig = sum_ext << (12'd13 - 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[13:3]} + (round_up ? 12'd1 : 12'd0);
        if (rounded[11]) begin final_sig = rounded[11:1]; norm_exp = norm_exp + 12'sd1; end
        else final_sig = rounded[10:0];
        if (norm_exp >= 12'sd31) y = {res_sign, 5'h1F, 10'd0};
        else if (norm_exp <= 12'sd0) y = pack_sub16(res_sign, sum_ext, lead, norm_exp);
        else y = {res_sign, norm_exp[4:0], final_sig[9:0]};
      end
    end
  end
endmodule
"#;
Expand description

The synthesizable combinational IEEE-754 f16 (binary16) adder — the f32 adder narrowed to 5-bit exponent, 10-bit mantissa, bias 15 (window 128-bit, LSB0=64, leading-ref=74, normalize-target=13). Proven bit-exact to the f16_add reference under iverilog; instantiated once per lane by the packed F16×4 tile.