Verilog 组合逻辑 FP32加法器

单精度浮点数
根据 IEEE 754 标准,单精度浮点数(float)为32位,其存储格式为:
单精度浮点数格式
sign:符号位,1bit,S
exponent:指数,8 bits,原码,E = (exponent)10-12710
fraction:尾数,23 bits,原码。在浮点数中隐去了小数点前的1,因此 M = 1+(fraction)10/223
该浮点数表示的10进制数为:(-1)S×M×2E

一般情况下,浮点数加法的处理步骤为:

  1. 对阶(align)
    通过exponent将计算数对齐到相同指数,尾数只有在指数相同时才可以做加减运算。对齐方式为向高阶对齐,即将阶数低的尾数右移,这样不会损失高位数据的精度。
  2. 尾数求和
  3. 规格化(normalization)
    将计算后的数据规格化。对于产生溢出位的结果右移;对于操作数符号位不同,产生结果多个高位为0时,需要左移,直到1出现在最高位。
  4. 舍入(round)
    因为计算过程中需要扩展位数保证精度,因此在输出浮点数结果前需要截断扩展的尾数。

但浮点数表示的数据范围有限,处理时会遇到正溢出、负溢出、数值无效的特殊情况,就需要对这些情况进行特殊处理:
Special Number 处理:
当exp=8’hff 且man=23’h0时,输入为inf,表示溢出,根据sign,可以为+inf/ -inf。
当exp=8’hff 且man!=23’h0时,表示NaN。

计算过程中可产生inf的情况:

  • input为存在+inf或-inf
  • rounding前的exp=8’hfe,且rounding产生了进位

计算过程中可产生NaN的情况:

  • input为NaN
  • input两操作数分别为+inf与-inf

Round 处理
就近舍入:
即十进制下的四舍五入。保留23位尾数,当需要舍去的数大于2-24,进一;小于2-24,舍去;当等于2-24时,舍向最近的偶数。

例如:

  • 多余数字是 (1001)2,它大于 (0.5)10,故最低位进 12
  • 多余数字是 (0111)2,它小于 (0.5)10,则直接舍掉多余数字。
  • 多余数字是 (1000)2,正好是等于 (0.5)10 的特殊情况;那么此时最低位为 02 则舍掉多余位,最低位为 12 则进位 12

对于浮点数加法实现方式的学习,可参考的代码大多数是通过时序逻辑实现的,其条理清晰,也容易理解,但大多用到循环语句,并不高效,在此不再进行赘述。以下是参考了Howeng98在GitHub上的组合逻辑实现的浮点数加法器。
链接: 参考github代码
用优先编码器修改了代码的normalization部分,使其可综合(原代码用while循环,不知道是怎么跑通的=_=);
添加了sepcial处理:针对输入为inf和NaN的情况,并对inf与NaN区分输出。

/* 对于数据格式的说明:
signed  exp       man
31      30...23   22...0
特殊情况:
  exponent  fraction  value
  0         zero      0
  0         non-zero  +-2^(-126)*0.(man)
  1~254     any       +-2^(exp-127)*1.(man)
  255       zero      +-inf
  255       non-zero  NaN
*/
module fpadder (
    input       [31:0]  src1,
    input       [31:0]  src2,
    output reg  [31:0]  out
    );
    reg [66:0]  fraction_1;
    reg [66:0]  fraction_2;
    reg [66:0]  sum;
    reg [66:0]  fraction_Ans;
    reg [7:0]   exponent_1;
    reg [7:0]   exponent_2;
    reg [7:0]   exponent_Ans;
    reg [4:0]   norm_count;
    reg         norm_vld;
    reg         sign_1;
    reg         sign_2;
    reg         sign_Ans;
    reg         guard_bit;
    reg         round_bit;
    reg         sticky_bit;
    reg         inf_1;
    reg         inf_2;
    reg         nan_1;
    reg         nan_2;
/ FP ADDER ///
    always@(*) begin 
        //loading
        begin
            fraction_1  = {2'd0,src1[22:0],42'd0};	// 对尾数扩展。前2bit第一位为进位位,第二位为隐藏的1,
            fraction_2  = {2'd0,src2[22:0],42'd0};	// 此处不赋值2'b01是留在special case中处理指数全0的情况。后42bit保障精度,可适当减少
            exponent_1  = src1[30:23];
            exponent_2  = src2[30:23];
            sign_1      = src1[31];
            sign_2      = src2[31]; 
            inf_1       = 1'b0;
            nan_1       = 1'b0;
            inf_2       = 1'b0;
            nan_2       = 1'b0;
        end    
        //preprocessing
        begin
            if(exponent_1 == 0) begin //when exponent is zero but fraction is non-zero,set it to 1
                exponent_1 = 1;
                fraction_1[65] = 0;    //make 0.(Frac) //参考代码开头对于特殊情况的说明
            end
            else
                fraction_1[65] = 1;            
            if(exponent_2 == 0) begin
                exponent_2 = 1;
                fraction_2[65] = 0;
            end
            else
                fraction_2[65] = 1;   //make 1.(Frac)
        end
        //special case
        begin
            if((exponent_1 == 0) && (fraction_1 == 0)) begin //if src1 is zero, then return src2
                sign_Ans     = sign_2;
                exponent_Ans = exponent_2;
                fraction_Ans = fraction_2;
            end
            else if (exponent_1 == 8'hff) begin
                if (fraction_1 == 23'h0) inf_1 = 1'b1;		// 补充原代码缺失的对inf与NaN的处理
                else nan_1 = 1'b1;
            end 
            else begin
            end
            if((exponent_2 == 0) && (fraction_2 == 0)) begin // if src2 is zero, then return src1
                sign_Ans     = sign_1;
                exponent_Ans = exponent_1;
                fraction_Ans = fraction_1;
            end  
            else if (exponent_2 == 8'hff) begin
                if (fraction_2 == 23'h0) inf_2 = 1'b1;
                else nan_2 = 1'b1;
            end 
            else begin
            end
        end    
        //align
        begin
            if(exponent_1 > exponent_2) begin
                fraction_2 = fraction_2 >> (exponent_1 - exponent_2);
                exponent_Ans = exponent_1;
            end
            else if(exponent_1 < exponent_2) begin
                fraction_1 = fraction_1 >> (exponent_2 - exponent_1);
                exponent_Ans = exponent_2;
            end
            else begin
                exponent_Ans = exponent_1;  
            end
        end
        //add significands
        begin
            if(sign_1 == sign_2) begin
                fraction_Ans = fraction_1 + fraction_2;
                sign_Ans = sign_1;
            end
            else begin
                if(fraction_1 >= fraction_2) begin
                    fraction_Ans = fraction_1 - fraction_2;
                    sign_Ans = sign_1;
                end
                else begin
                    fraction_Ans = fraction_2 - fraction_1;
                    sign_Ans = sign_2;
                end
            end
        end
        sum = fraction_Ans; //sum is for checking the addition of src1 and src2    
        //overflow
        begin
            if(fraction_Ans[66]) begin
                fraction_Ans = fraction_Ans >> 1;
                exponent_Ans = exponent_Ans + 1;
            end
        end    
        //normalization
        begin
            if(fraction_Ans == 67'b0) begin
                fraction_Ans = 67'b0;
                exponent_Ans = 8'b0;
            end
            else begin
                if(fraction_Ans[65] == 0)begin
                    PENC32({11'b0, fraction_Ans[64:42]}, norm_count, norm_vld);	// 用优先编码器计算最高位1所在bit
                    fraction_Ans = fraction_Ans << (5'd23 - norm_count);		// 左移完成规格化
                    exponent_Ans = exponent_Ans + norm_count - 5'd23;      		// 对应更新阶数
                    
			        // while((fraction_Ans[65] == 0) && (fraction_Ans[64:42] > 0))begin
			        //   fraction_Ans = fraction_Ans << 1;
			        //   exponent_Ans = exponent_Ans - 1;            
			        // end
                end    
                else if(fraction_Ans[65]) begin
                // do nothing
                end
            end
        end
        //round
        begin
            guard_bit = fraction_Ans[41]; 
            round_bit = fraction_Ans[40];
            if(fraction_Ans[39:0] > 0)
                sticky_bit = 1;  
            else
                sticky_bit = 0;                         
            if(guard_bit && (fraction_Ans[42] | round_bit | sticky_bit)) begin	// 对照进位的条件
                fraction_Ans = fraction_Ans + 67'b0000000000000000000000001000000000000000000000000000000000000000000;
            end
        end
        //convert:
        begin
            out[22:0]  = fraction_Ans[64:42];
            out[30:23] = exponent_Ans[7:0];
            out[31]    = sign_Ans; 
            //special case
            if(fraction_Ans == 0) //when fraction is 23'd0
                out = 0;
            if(nan_1 || nan_2 || (inf_1 && inf_2 && (sign_1 ^ sign_2))) begin // return NaN
                exponent_Ans = 8'hff;
                fraction_Ans = 23'h1;
            end
            else if(inf_1 || inf_2 || (exponent_Ans == 8'b11111111)) begin // return inf
                fraction_Ans = 0;
                out[22:0]  = 23'h0;
            end
            // if(exponent_Ans == 8'b11111111) //when exponent is 11111111
            //     fraction_Ans = 0;
            // out[22:0]  = fraction_Ans[64:42];
        end
    end
// PENC TASK // 用4个PENC8拼接实现32bit优先编码
    task PENC8;
        //port declaration
        input   [7:0] D;
        output  [2:0] Q;
        output        vld;
        //internal node signals declaration
        begin
            vld  = D[7]|D[6]|D[5]|D[4]|D[3]|D[2]|D[1]|D[0];
            Q[2] = D[7]|D[6]|D[5]|D[4];
            Q[1] = (D[7]|D[6]|D[5]|D[4]) ? (D[7]|D[6]) : (D[3]|D[2]);
            Q[0] = (D[7]|D[6]|D[5]|D[4]) ? (D[7]|((~D[6])&D[5])) : (D[3]|((~D[2])&D[1]));
        end
    endtask

    task PENC16;
        //port declaration
        input   [15:0] D;
        output  [3:0]  Q;
        output         vld;
        //internal node signals declaration
        reg [2:0] Q1_h, Q1_l;
        reg       vld1_h, vld1_l;
        begin
            PENC8( D[15:8],  Q1_h,   vld1_h);
            PENC8( D[7:0],   Q1_l,   vld1_l);
            Q[3]    = vld1_h;
            Q[2:0]  = vld1_h ? Q1_h : Q1_l;
            vld     = vld1_h | vld1_l;
        end
    endtask

    task PENC32;
        //port declaration
        input   [31:0] D;
        output  [4:0]  Q;
        output         vld;
        //internal node signals declaration
        reg [3:0] Q2_h, Q2_l;
        reg       vld2_h, vld2_l;
        begin
            PENC16( D[31:16],   Q2_h,   vld2_h);
            PENC16( D[15:0],    Q2_l,   vld2_l);
            Q[4]    = vld2_h;
            Q[3:0]  = vld2_h ? Q2_h : Q2_l;
            vld     = vld2_h | vld2_l;
        end
    endtask      
endmodule

这个代码应该还可以用assign的方式重写,将一些分支语句改为并行执行,然后通过mux输出,以获得更高的速度。

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值