用verilog实现浮点的乘法

总结

本实现采用的是面积换时间, 一拍出结果。如果为了后端实现,可以多拍出结果。在这个算法的基础上其实可以做些修改达到。核心逻辑本算法已经完成。

代码

在代码中已经添加简单的注释,相信阅读过IEEE754标准的人很容易看懂。对于结果的取舍,按照S家的标准共六种取舍,同时会输出status表明结果的精准度。

//
// Created by         :  Harris Zhu (zhuzhzh@163.com)
// Filename           :  HZ_fp_mult.v
// Author             :  Harris Zhu
// Created On         :  2020-08-14 11:36:49
// Last Modified      :  2020-08-14 11:36:49
// Update Count       :  1
// Tags               :   
// Description        :  z = a * b; 
// Conclusion         :  
// License            :  GPL
//                      
//=======================================================================


module HZ_fp_mult (a, b, rnd, z, status);

  parameter sig_width = 23;      
  parameter exp_width = 8;       
  parameter ieee_compliance = 0; 

  input  [exp_width + sig_width:0] a;
  input  [exp_width + sig_width:0] b;
  input  [2:0] rnd;
  output [exp_width + sig_width:0] z;
  output [7:0] status;



localparam [exp_width-1:0] ebias = ((1<<(exp_width-1)) - 1);  
localparam totalbit= (1 + sig_width + exp_width);
localparam einf = ((1<<(exp_width)) - 1);
localparam emax = (einf - 1);
localparam emin = 1;

localparam sexp_width = (exp_width + 1);

localparam frac_width = (sig_width + 1);
localparam spwidth = (2 * sig_width + 2);

localparam log_swidth = (sig_width+1>256)?9:
                        (sig_width+1>128)?8:
                        (sig_width+1>64)?7:
                        (sig_width+1>32)?6:
                        (sig_width+1>16)?5:
                        (sig_width+1>8)?4:
                        (sig_width+1>4)?3:
                        (sig_width+1>2)?2:1;       

localparam log_spwidth = (spwidth+1>256)?9:
                        (spwidth+1>128)?8:
                        (spwidth+1>64)?7:
                        (spwidth+1>32)?6:
                        (spwidth+1>16)?5:
                        (spwidth+1>8)?4:
                        (spwidth+1>4)?3:
                        (spwidth+1>2)?2:1;       

function [9:0] oneindex(input [2*sig_width+1:0] data);
    integer i;
    reg found;
begin
    oneindex = 0;
    found = 0;
    for(i=0;i<=2*sig_width+1;i=i+1)
    begin
        if((found == 0) && (data[2*sig_width+1-i] == 1))
        begin
            oneindex = i+1;
            found = 1;
        end
    end
end
endfunction

//status
reg [6:0] status_reg = 0;

// get sign, exp and sig
reg sign ;
reg [exp_width-1:0] aexp ;   // exponent of A
reg [exp_width-1:0] bexp ;  // exponent of B
reg [sexp_width-1:0] sign_aexp ;   // add 1 sign bit
reg [sexp_width-1:0] sign_bexp ;  // add 1 sign bit

reg [sig_width-1:0] asig;
reg [sig_width-1:0] bsig;
reg [frac_width-1:0] fracA;          // fraction of A, add 1 hidden bit
reg [frac_width-1:0] fracB;            // fraction of B, add 1 hidden bit

// exp calc
reg signed [exp_width+1:0] sign_ext_zexp;
reg signed [exp_width+1:0] z_exp_sh_full;
reg [exp_width-1:0] z_exp_sh;


// sig calc
wire [2*sig_width+1:0] fracAxB;        //the production of fracA and fracB
HZ_int_mult #(.A_width(sig_width+1), .B_width(sig_width+1)) u0 (fracA, fracB, fracAxB);

reg [2*sig_width+1:0] normFracAxB;        //normalized fracAxB
reg [log_spwidth:0]  lshift;                        // the index of highest 1 in fracAxB 
//oneindex #(.dwidth(2*sig_width+2), .iwidth(10)) u1 (fracAxB, lshift);
reg [sig_width-1:0] z_sig;                //the significand of Z

// status
reg isOf, isUf, isUUf;            //>maxnorm,  <minnorm, denormalize 
reg found =0;
integer i;

//NaN, inft
reg isANan, isAInft;
reg isBNan, isBInft;
reg isAZero, isBZero;
reg isADenorm, isBDenorm;


reg isAExpInf, isBExpInf;
reg isAExpZero, isBExpZero;
reg isASigZero, isBSigZero;

//Nan, Inft
reg [exp_width-1:0] NanInft_Exp = {(exp_width){1'b1}};
reg [sig_width-1:0] Nan_Sig =  {{(sig_width-1){1'b0}},1'b1};
reg [sig_width-1:0] Inft_Sig = {(sig_width){1'b0}};


always @(a or b or rnd or fracAxB)
begin
    status_reg = 7'b0;
    sign = a[totalbit-1] ^ b[totalbit-1];

    aexp = a[totalbit-2:sig_width];
    bexp = b[totalbit-2:sig_width];
    
    sign_aexp = {1'b0, aexp};
    sign_bexp = {1'b0, bexp};

    isAExpInf = (aexp == einf);
    isBExpInf = (bexp == einf);
    isAExpZero = (aexp == 0);
    isBExpZero = (bexp == 0);

    asig = a[sig_width-1:0];
    bsig = b[sig_width-1:0];
    
    isASigZero = (isAExpZero & isASigZero);
    isBSigZero = (isBExpZero & isBSigZero);

    isADenorm = (isAExpZero && (isASigZero == 0));
    isBDenorm = (isBExpZero && (isBSigZero == 0));

    isAZero = (a[totalbit-2:0] == 0);
    isBZero = (b[totalbit-2:0] == 0);

    isANan = (isAExpInf && (isASigZero == 0));
    isBNan = (isBExpInf && (isBSigZero == 0));

    isAInft = (isAExpInf && (isASigZero == 1));
    isBInft = (isBExpInf && (isBSigZero == 1));


    // parse out the fraction of A and B
    if(isADenorm)
        fracA = {1'b0,a[sig_width-1:0]};
    else
        fracA = {1'b1,a[sig_width-1:0]};
    if(isBDenorm)
        fracB = {1'b0,b[sig_width-1:0]};
    else
        fracB = {1'b1,b[sig_width-1:0]};


    if(isANan || isBNan)
    begin
        if(ieee_compliance)
        begin
            status_reg[2] = 1'b1;    // Invalid
            sign = 0;
            z_sig = {{(sig_width-1){1'b0}}, 1'b1};
            z_exp_sh = {exp_width{1'b1}};
        end else begin
            status_reg[1] = 1'b1;        //Infinity
            z_sig = {sig_width{1'b0}};
            z_exp_sh = {exp_width{1'b1}};
        end
    end else if((isAZero && isBInft)||(isBZero && isAInft))
    begin
        if(ieee_compliance)
        begin
            status_reg[2] = 1'b1;        //Invalid
            sign = 0;
            z_sig = {{(sig_width-1){1'b0}}, 1'b1};
            z_exp_sh = {exp_width{1'b1}};
        end else begin
            status_reg[1] = 1'b1;        //Infinity
            z_sig = {sig_width{1'b0}};
            z_exp_sh = {exp_width{1'b1}};
        end
    end else if(isBInft || isAInft)
    begin
            status_reg[1] = 1'b1;        //Infinity
            z_sig = {sig_width{1'b0}};
            z_exp_sh = {exp_width{1'b1}};
    end else if(isAZero || isBZero)
    begin
        status_reg[0] = 1'b1;        //Zero
        z_sig = {sig_width{1'b0}};
        z_exp_sh = {exp_width{1'b0}};
    end else if((ieee_compliance ==0)&& (isADenorm || isBDenorm))
    begin
        status_reg[0] = 1'b1;        //Zero
        z_sig = {sig_width{1'b0}};
        z_exp_sh = {exp_width{1'b0}};
    end else begin
        status_reg[5] = 1'b1;            //Inexact
//        fracAxB = fracA * fracB;      //put this multiply into dedicated module to use ALU
        lshift = 0;
        found = 0;
        for(i=0; i<=2*sig_width+1;i=i+1)
        begin
            if((found == 0) && (fracAxB[2*sig_width+1-i] == 1))
            begin
                lshift = i+1;
                found = 1;
            end
        end

        if(isADenorm)
            sign_aexp = 1;
        if(isBDenorm)
            sign_bexp = 1;

        sign_ext_zexp = sign_aexp + sign_bexp -ebias;

        normFracAxB = (fracAxB << lshift);
        z_exp_sh_full = sign_ext_zexp + 2 - lshift;

        isOf = (z_exp_sh_full > emax)? 1:0;
        isUf = (z_exp_sh_full < emin)? 1:0;

        if(ieee_compliance == 1)
        begin
            isUUf = (z_exp_sh_full <= 0)? 1:0;
        end

        // the result is denormal number
        if(isUUf)
        begin
            if(sign_ext_zexp==0)
            begin
                normFracAxB = (fracAxB << 1);
                z_exp_sh_full = 0;
            end else begin
                normFracAxB = (fracAxB >> (-1*sign_ext_zexp - 1));
                z_exp_sh_full = 0;
            end
        end

        // exp is bigger than emax
        if(isOf) 
        begin
            status_reg[4] = 1'b1;        //Huge
            case(rnd)
                // IEEE round to nearest (even)
                3'b000:
                begin
                    z_exp_sh = einf;           //{exp_width{1'b1}};
                    z_sig = {sig_width{1'b0}};
                    status_reg[1] = 1'b1;        //Infinity
                end
                // IEEE round to zero
                3'b001:
                begin
                    z_exp_sh = emax;
                    z_sig = {sig_width{1'b1}};
                end
                // IEEE round to positive infinity
                3'b010:
                begin
                    if(sign)
                    begin
                        z_exp_sh = emax;
                        z_sig = {sig_width{1'b1}};
                    end else begin
                        z_exp_sh = einf;        // {exp_width{1'b1}};
                        z_sig = {sig_width{1'b0}};
                        status_reg[1] = 1'b1;        //Infinity
                    end
                end
                // IEEE round to negative infinity
                3'b011:
                begin
                    if(sign)
                    begin
                        z_exp_sh = einf;    // {exp_width{1'b1}};
                        z_sig = {sig_width{1'b0}};
                        status_reg[1] = 1'b1;        //Infinity
                    end else begin
                        z_exp_sh = emax;
                        z_sig = {sig_width{1'b1}};
                    end
                end
                // round to nearest up
                3'b100:
                begin
                    z_exp_sh = einf;
                    z_sig = {sig_width{1'b0}};
                    status_reg[1] = 1'b1;        //Infinity
                end
                // round away from zero
                3'b101:
                begin
                    z_exp_sh = einf;
                    z_sig = {sig_width{1'b0}};
                    status_reg[1] = 1'b1;        //infinity
                end
            endcase
        end

        // exp is smaller than emin
        if(isUf) 
        begin
            status_reg[3] = 1'b1;        //Tiny
            if(ieee_compliance==0)
            begin
                case(rnd)
                    // IEEE round to nearest (Even)
                    3'b000:
                    begin
                        z_exp_sh = {exp_width{1'b0}};
                        z_sig = {sig_width{1'b0}};
                        status_reg[0] = 1'b1;        //Zero
                    end
                    // IEEE round to zero
                    3'b001:
                    begin
                        z_exp_sh = {exp_width{1'b0}};
                        z_sig = {sig_width{1'b0}};
                        status_reg[0] = 1'b1;    //Zero
                    end
                    // IEEE round to positive infinity
                    3'b010:
                    begin
                        if(sign)
                        begin
                            z_exp_sh = 0; 
                            z_sig = {sig_width{1'b0}};
                            status_reg[0] = 1'b1;        //Zero
                        end else begin
                            z_exp_sh = emin;
                            z_sig = {sig_width{1'b0}};
                        end
                    end
                    // IEEE round to negative infinity
                    3'b011:
                    begin
                        if(sign)
                        begin
                            z_exp_sh = emin;    
                            z_sig = {sig_width{1'b0}};
                        end else begin
                            z_exp_sh = 0;
                            z_sig = {sig_width{1'b0}};
                            status_reg[0] = 1'b1;        //Zero
                        end
                    end
                    // round to nearest up
                    3'b100:
                    begin
                        z_exp_sh = {exp_width{1'b0}};
                        z_sig = {sig_width{1'b0}};
                        status_reg[0] = 1'b1;        //Zero
                    end
                    // round away from zero
                    3'b101:
                    begin
                            z_exp_sh = emin;    
                            z_sig = {sig_width{1'b0}};
                    end
                endcase
            end
        end

        // rounding the fraction
        if (!((isOf)||(isUf&&(ieee_compliance==0))))
        begin
            case(rnd)
                // IEEE round to nearest(Even)
                3'b000:
                begin
                    if(normFracAxB[sig_width+1:0] > {1'b1, {(sig_width+1){1'b0}}})
                    begin
                        z_sig = normFracAxB[2*sig_width+1:sig_width+2] + 1;
                        if(z_sig == 0)
                        begin
                            z_exp_sh = z_exp_sh_full[exp_width-1:0] + 1;
                        end else begin
                            z_exp_sh = z_exp_sh_full[exp_width-1:0];
                        end
                    end else if(normFracAxB[sig_width+1:0] == {1'b1, {(sig_width+1){1'b0}}})
                    begin
                        if(normFracAxB[sig_width+2] == 1'b1)
                        begin
                            z_sig = normFracAxB[2*sig_width+1:sig_width+2] + 1;
                            if(z_sig == 0)
                            begin
                                z_exp_sh = z_exp_sh_full[exp_width-1:0] + 1;
                            end else begin
                                z_exp_sh = z_exp_sh_full[exp_width-1:0];
                            end
                        end else begin
                            z_sig = normFracAxB[2*sig_width+1:sig_width+2];
                            z_exp_sh = z_exp_sh_full[exp_width-1:0];
                        end
                    end else begin
                        z_sig = normFracAxB[2*sig_width+1:sig_width+2];
                        z_exp_sh = z_exp_sh_full[exp_width-1:0];
                    end
                end
                // IEEE round to zero
                3'b001:
                begin
                    z_sig = normFracAxB[2*sig_width+1:sig_width+2];
                    z_exp_sh = z_exp_sh_full[exp_width-1:0];
                end
                // IEEE round to positive infinity
                3'b010:
                begin
                    if(sign == 1'b0)
                    begin
                        z_sig = normFracAxB[2*sig_width+1:sig_width+2] + 1;
                        if(z_sig == 0)
                        begin
                            z_exp_sh = z_exp_sh_full[exp_width-1:0] + 1;
                        end else begin
                            z_exp_sh = z_exp_sh_full[exp_width-1:0] ;
                        end
                    end else begin
                        z_sig = normFracAxB[2*sig_width+1:sig_width+2];
                        z_exp_sh = z_exp_sh_full[exp_width-1:0];
                    end
                end
                // IEEE round to negative infinity
                3'b011:
                begin
                    if(sign == 1'b0)
                    begin
                        z_sig = normFracAxB[2*sig_width+1:sig_width+2];
                        z_exp_sh = z_exp_sh_full[exp_width-1:0];
                    end else begin
                        z_sig = normFracAxB[2*sig_width+1:sig_width+2] + 1;
                        if(z_sig == 0)
                        begin
                            z_exp_sh = z_exp_sh_full[exp_width-1:0] + 1;
                        end else begin
                            z_exp_sh = z_exp_sh_full[exp_width-1:0] ;
                        end
                    end
                end
                // round to nearest up
                3'b100:
                begin
                    if(normFracAxB[sig_width+1:0] > {1'b1, {(sig_width+1){1'b0}}})
                    begin
                        z_sig = normFracAxB[2*sig_width+1:sig_width+2] + 1;
                        if(z_sig == 0)
                        begin
                            z_exp_sh = z_exp_sh_full[exp_width-1:0] + 1;
                        end else begin
                            z_exp_sh = z_exp_sh_full[exp_width-1:0];
                        end
                    end else if(normFracAxB[sig_width+1:0] == {1'b1, {(sig_width+1){1'b0}}})
                    begin
                        if(sign == 1'b0)
                        begin
                            z_sig = normFracAxB[2*sig_width+1:sig_width+2] + 1;
                            if(z_sig == 0)
                            begin
                                z_exp_sh = z_exp_sh_full[exp_width-1:0] + 1;
                            end else begin
                                z_exp_sh = z_exp_sh_full[exp_width-1:0];
                            end
                        end else begin
                            z_sig=normFracAxB[2*sig_width+1:sig_width+2];
                        end
                    end else begin
                        z_sig = normFracAxB[2*sig_width+1:sig_width+2];
                        z_exp_sh = z_exp_sh_full[exp_width-1:0];
                    end
                end
                // round away from zero
                3'b101:
                begin
                    z_sig = normFracAxB[2*sig_width+1:sig_width+2] + 1;
                    if(z_sig == 0)
                    begin
                        z_exp_sh = z_exp_sh_full[exp_width-1:0] + 1;
                    end else begin
                        z_exp_sh = z_exp_sh_full[exp_width-1:0] ;
                    end
                end
                default:
                begin
                    z_sig = normFracAxB[2*sig_width+1:sig_width+2];
                    z_exp_sh = z_exp_sh_full[exp_width-1:0];
                end
            endcase
        end
        if ((z_sig == 0) && (z_exp_sh == 0))
        begin
            status_reg[0] = 1;        //Zero
        end
    end
end


assign z = {sign, z_exp_sh, z_sig};
assign status = status_reg;

  
endmodule


module oneindex(data, index);
parameter dwidth = 40;
parameter iwidth = 10;
input [dwidth-1:0] data;
output [iwidth-1:0] index;


reg [iwidth-1:0] index;

integer i;
reg found=0;

always @(data)
begin
    index = 0;
    found = 0;
    for(i=0;i<=dwidth;i=i+1)
    begin
        if((found == 0) && (data[dwidth-1-i] == 1))
        begin
            index = i+1;
            found = 1;
        end
    end
end
endmodule


module HZ_int_mult(A, B, Z);

parameter A_width = 32;
parameter B_width = 8;

input   [A_width-1:0]   A;
input   [B_width-1:0]   B;

output [A_width+B_width-1:0] Z;

assign Z = A * B;

endmodule

后序

如果要使用或引用本文代码,请保留本作者信息。


harriszh
338 声望131 粉丝

做些有趣的事,留些有用的存在


引用和评论

0 条评论