用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
后序
如果要使用或引用本文代码,请保留本作者信息。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。