用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
后序
如果要使用或引用本文代码,请保留本作者信息。