cvw/wally-pipelined/src/fpu/fma2.sv

276 lines
8.3 KiB
Systemverilog
Raw Normal View History

2021-06-04 18:00:11 +00:00
module fma2(
2021-05-01 02:18:01 +00:00
2021-06-04 18:00:11 +00:00
input logic [63:0] FInput1M,
input logic [63:0] FInput2M,
input logic [63:0] FInput3M,
input logic [2:0] FrmM,
input logic [105:0] ProdManM,
input logic [161:0] AlignedAddendM,
input logic [12:0] ProdExpM,
input logic AddendStickyM,
input logic KillProdM,
input logic [3:0] FOpCtrlM,
input logic XZeroM, YZeroM, ZZeroM,
input logic XInfM, YInfM, ZInfM,
input logic XNaNM, YNaNM, ZNaNM,
output logic [63:0] FmaResultM,
output logic [4:0] FmaFlagsM);
2021-05-01 02:18:01 +00:00
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
logic [51:0] XMan, YMan, ZMan, WMan;
logic [10:0] XExp, YExp, ZExp, WExp;
logic XSgn, YSgn, ZSgn, WSgn, PSgn;
logic [105:0] ProdMan2;
logic [162:0] AlignedAddend2;
logic [161:0] Sum;
logic [162:0] SumTmp;
logic [12:0] SumExp;
logic [12:0] SumExpMinus1;
logic [12:0] SumExpTmp, SumExpTmpMinus1, WExpTmp;
logic [53:0] NormSum;
logic [161:0] NormSumTmp;
logic [8:0] NormCnt;
logic NormSumSticky;
logic SumZero;
logic NegSum;
logic InvZ;
logic ResultDenorm;
logic Sticky;
logic Plus1, Minus1, Plus1Tmp, Minus1Tmp;
logic Invalid,Underflow,Overflow,Inexact;
logic [8:0] DenormShift;
logic ProdInf, ProdOf, ProdUf;
logic [63:0] FmaResultTmp;
logic SubBySmallNum;
logic [63:0] FInput3M2;
logic ZeroSgn, ResultSgn;
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
// Set addend to zero if FMUL instruction
assign FInput3M2 = FOpCtrlM[2] ? 64'b0 : FInput3M;
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
// split inputs into the sign bit, mantissa, and exponent for readability
assign XSgn = FInput1M[63];
assign YSgn = FInput2M[63];
assign ZSgn = FInput3M2[63]^FOpCtrlM[0]; //Negate Z if subtraction
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
assign XExp = FInput1M[62:52];
assign YExp = FInput2M[62:52];
assign ZExp = FInput3M2[62:52];
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
assign XMan = FInput1M[51:0];
assign YMan = FInput2M[51:0];
assign ZMan = FInput3M2[51:0];
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
// Calculate the product's sign
// Negate product's sign if FNMADD or FNMSUB
assign PSgn = XSgn ^ YSgn ^ FOpCtrlM[1];
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
// Addition
// Negate Z when doing one of the following opperations:
// -prod + Z
// prod - Z
assign InvZ = ZSgn ^ PSgn;
// Choose an inverted or non-inverted addend - the one is added later
assign AlignedAddend2 = InvZ ? ~{1'b0,AlignedAddendM} : {1'b0,AlignedAddendM};
// Kill the product if the product is too small to effect the addition (determined in fma1.sv)
assign ProdMan2 = KillProdM ? 106'b0 : ProdManM;
// Do the addition
// - add one to negate if the added was inverted
// - the 2 extra bits at the begining and end are needed for rounding
assign SumTmp = AlignedAddend2 + {55'b0, ProdMan2,2'b0} + {162'b0, InvZ};
// Is the sum negitive
assign NegSum = SumTmp[162];
// If the sum is negitive, negate the sum.
assign Sum = NegSum ? -SumTmp[161:0] : SumTmp[161:0];
// Leading one detector
logic [8:0] i;
always_comb begin
i = 0;
while (~Sum[161-i] && $unsigned(i) <= $unsigned(9'd161)) i = i+1; // search for leading one
NormCnt = i+1; // compute shift count
end
// Normalization
// Determine if the sum is zero
assign SumZero = ~(|Sum);
// Determine if the result is denormal
assign ResultDenorm = $signed(SumExpTmp)<=0 & ($signed(SumExpTmp+13'd52)>=0);
// Determine the shift needed for denormal results
assign SumExpTmpMinus1 = SumExpTmp-1;
assign DenormShift = ResultDenorm ? SumExpTmpMinus1[8:0] : 9'b0;
// Normalize the sum
assign NormSumTmp = SumZero ? 162'b0 : Sum << NormCnt+DenormShift;
assign NormSum = NormSumTmp[161:108];
// Calculate the sticky bit
assign NormSumSticky = (|NormSumTmp[107:0]);
assign Sticky = AddendStickyM | NormSumSticky;
// Determine sum's exponent
assign SumExpTmp = KillProdM ? {2'b0, ZExp} : ProdExpM + -({4'b0, NormCnt} - 13'd56);
assign SumExp = SumZero ? 13'b0 :
ResultDenorm ? 13'b0 :
SumExpTmp;
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
// Rounding
// round to nearest even
// {NormSum[1], NormSum[0], Sticky}
// 0xx - do nothing
// 100 - tie - Plus1 if NormSum[2] = 1
// - don't add 1 if there was supposed to be a subtraction by a small number that didn't happen
// 101/110/111 - Plus1
// round to zero - do nothing
// - subtract 1 if a small number was supposed to be subtracted from the positive result
// round to -infinity - Plus1 if negitive
// - don't add 1 if there was supposed to be a subtraction by a small number that didn't happen
// - subtract 1 if a small number was supposed to be subtracted from the positive result
// round to infinity - Plus1 if positive
// - don't add 1 if there was supposed to be a subtraction by a small number that didn't happen
// - subtract 1 if a small number was supposed to be subtracted from the negitive result
// round to nearest max magnitude
// {NormSum[1], NormSum[0], Sticky}
// 0xx - do nothing
// 100 - tie - Plus1
// - don't add 1 if there was supposed to be a subtraction by a small number that didn't happen
// 101/110/111 - Plus1
// Deterimine if the result was supposed to be subtrated by a small number
assign SubBySmallNum = AddendStickyM&InvZ&~NormSumSticky;
always_comb begin
// Determine if you add 1
case (FrmM)
3'b000: Plus1Tmp = NormSum[1] & (NormSum[0] | (Sticky&~(~NormSum[0]&SubBySmallNum)) | (~NormSum[0]&~Sticky&NormSum[2]));//round to nearest even
3'b001: Plus1Tmp = 0;//round to zero
3'b010: Plus1Tmp = WSgn & ~(SubBySmallNum);//round down
3'b011: Plus1Tmp = ~WSgn & ~(SubBySmallNum);//round up
3'b100: Plus1Tmp = (NormSum[1] & (NormSum[0] | (Sticky&~(~NormSum[0]&SubBySmallNum)) | (~NormSum[0]&~Sticky)));//round to nearest max magnitude
default: Plus1Tmp = 1'bx;
endcase
// Determine if you subtract 1
case (FrmM)
3'b000: Minus1Tmp = 0;//round to nearest even
3'b001: Minus1Tmp = SubBySmallNum;//round to zero
3'b010: Minus1Tmp = ~WSgn & SubBySmallNum;//round down
3'b011: Minus1Tmp = WSgn & SubBySmallNum;//round up
3'b100: Minus1Tmp = 0;//round to nearest max magnitude
default: Minus1Tmp = 1'bx;
endcase
end
// If an answer is exact don't round
assign Plus1 = Sticky | (|NormSum[1:0]) ? Plus1Tmp : 1'b0;
assign Minus1 = Sticky | (|NormSum[1:0]) ? Minus1Tmp : 1'b0;
// Compute rounded result
assign {WExpTmp, WMan} = {SumExp, NormSum[53:2]} - {64'b0, Minus1} + {64'b0, Plus1};
assign WExp = WExpTmp[10:0];
// Sign calculation
// Determine the sign if the sum is zero
// if product underflows then use psign
// otherwise
// if cancelation then 0 unless round to -inf
// otherwise psign
assign ZeroSgn = Underflow & ~ResultDenorm ? PSgn :
(PSgn^ZSgn ? FrmM == 3'b010 : PSgn);
// is the result negitive
// if p - z is the Sum negitive
// if -p + z is the Sum positive
// if -p - z then the Sum is negitive
assign ResultSgn = InvZ&(ZSgn)&NegSum | InvZ&PSgn&~NegSum | ((ZSgn)&PSgn);
assign WSgn = SumZero ? ZeroSgn : ResultSgn;
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
// Select the result
assign FmaResultM = XNaNM ? {XSgn, XExp, 1'b1,XMan[50:0]} :
YNaNM ? {YSgn, YExp, 1'b1,YMan[50:0]} :
ZNaNM ? {ZSgn, ZExp, 1'b1,ZMan[50:0]} :
Invalid ? {WSgn, 11'h7ff, 1'b1, 51'b0} : // has to be before inf
XInfM ? {PSgn, XExp, XMan} :
YInfM ? {PSgn, YExp, YMan} :
ZInfM ? {ZSgn, ZExp, ZMan} :
Overflow ? {WSgn, 11'h7ff, 52'b0} :
Underflow & ~ResultDenorm ? {WSgn, 63'b0} - {63'b0, (Minus1&AddendStickyM)} + {63'b0, (Plus1&AddendStickyM)} :
KillProdM ? {ZSgn, ZExp, ZMan} - {63'b0, (Minus1&AddendStickyM)} + {63'b0, (Plus1&AddendStickyM)}: // has to be after Underflow
{WSgn,WExp,WMan};
// Set Invalid flag for following cases:
// 1) Inf - Inf
// 2) 0 * Inf
// 3) any input is a signaling NaN
assign ProdOf = (ProdExpM >= 2047 && ~ProdExpM[12]);
assign ProdInf = ProdOf && ~XNaNM && ~YNaNM;
assign Invalid = (XNaNM&~XMan[51]) | (YNaNM&~YMan[51]) | (ZNaNM&~ZMan[51]) | ((XInfM || YInfM || ProdInf) & ZInfM & (XSgn ^ YSgn ^ ZSgn)) | (XZeroM & YInfM) | (YZeroM & XInfM);
// Set Overflow flag if the number is too big to be represented
assign Overflow = WExpTmp >= 2047 & ~WExpTmp[12];
// Set Underflow flag if the number is too small to be represented in normal numbers
assign ProdUf = KillProdM & ZZeroM;
assign Underflow = SumExp[12] | ProdUf;
// Set Inexact flag if the result is diffrent from what would be outputed given infinite precision
assign Inexact = Sticky|Overflow| (|NormSum[1:0]);
2021-04-15 18:28:00 +00:00
2021-06-04 18:00:11 +00:00
// Combine flags
// - FMA can't set the Divide by zero flag
// - Don't set the underflow flag if the result is exact
assign FmaFlagsM = {Invalid, 1'b0, Overflow, Underflow & Inexact, Inexact};
2021-05-03 19:17:09 +00:00
2021-04-15 18:28:00 +00:00
endmodule