浮点乘法的硬件实现

用verilog实现浮点的乘法

总结

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

代码

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

//
// Created by         :  Harris Zhu ([email protected])
// 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,   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

后序

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

你可能感兴趣的:(verilog,hardware,float,asic)