FP ISA tests bug fixes:

Added nanboxing for float in a 64-bit FPR system
Fixed behaviour around NaNs for comparison opcodes
This commit is contained in:
Niraj Nayan Sharma
2019-12-06 14:27:33 +05:30
parent 021b374833
commit 09ce172b91
3 changed files with 144 additions and 37 deletions

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2019 Bluepec, Inc
// This package implements utility functions used by the floating point
// related logic
package FP_Utils;
import FloatingPoint::*;
function FloatingPoint#(e,m) canonicalNaN = FloatingPoint{sign: False, exp: '1, sfd: 1 << (valueof(m)-1)};
// nanbox-ing and its inverse (unbox-ing)
// If the raw bits are nan-boxed, the fv_nanbox(fv_unbox) are identity
// functions. However, if the raw input was not properly nanboxed, then the
// output would be a canonical NaN
// Take a single precision value and nanboxes it to be able to write it to a
// 64-bit FPR register file. This is necessary if single precision operands
// used with a register file capable of holding double precision values
function Bit #(64) fv_nanbox (Bit #(64) x);
Bit #(64) fill_bits = (64'h1 << 32) - 1; // [31: 0] all ones
Bit #(64) fill_mask = (fill_bits << 32); // [63:32] all ones
return (x | fill_mask);
endfunction
// Take a 64-bit value and check if it is properly nanboxed if operating in a DP
// capable environment. If not properly nanboxed, return canonicalNaN32
function Float fv_unbox (Bit #(64) x);
//`ifdef ISA_D
if (x [63:32] == 32'hffffffff)
return (unpack (x [31:0]));
else
return (canonicalNaN);
//`else
// return (unpack (x [31:0]));
//`endif
endfunction
endpackage

View File

@@ -41,6 +41,7 @@ import XilinxFpu::*;
import HasSpecBits::*;
import SpecFifo::*;
import SpecPoisonFifo::*;
import FP_Utils::*;
export FpuResult(..);
export FpuResp(..);
@@ -50,8 +51,6 @@ export mkFpuExecPipeline;
typedef FloatingPoint::RoundMode FpuRoundMode;
typedef FloatingPoint::Exception FpuException;
function FloatingPoint#(e,m) canonicalNaN = FloatingPoint{sign: False, exp: '1, sfd: 1 << (valueof(m)-1)};
typedef struct {
Data data;
Bit#(5) fflags;
@@ -172,26 +171,36 @@ function Tuple2#(FloatingPoint#(e,m), FpuException) fcvt_f_wu (Bit#(64) in_bits,
endfunction
function Tuple2#(Bit#(64), FpuException) fmin_s(Bit#(64) in1, Bit#(64) in2);
Float in1_f = unpack(in1[31:0]);
Float in2_f = unpack(in2[31:0]);
// nirajns: interpret the inputs as floats. Observe that this function
// receives raw bits.
Float in1_f = fv_unbox(in1);
Float in2_f = fv_unbox(in2);
Bit #(64) in1_f_packed = fv_nanbox (zeroExtend(pack(in1_f)));
Bit #(64) in2_f_packed = fv_nanbox (zeroExtend(pack(in2_f)));
Float nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
// nirajns: TEST 21 failure on fmin ISA tests
// e.invalid_op should only be signalled only if either operand is a sNaN
// as the fmin and fmax are quiet comparison
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
e.invalid_op = True;
return tuple2(zeroExtend(pack(nan_f)), e);
return tuple2(fv_nanbox (zeroExtend(pack(nan_f))), e);
end else if (isNaN(in1_f) && isNaN(in2_f)) begin
return tuple2(fv_nanbox (zeroExtend(pack(nan_f))), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
return tuple2(in1_f_packed, e);
end else if (isNaN(in1_f)) begin
return tuple2(in2, e);
return tuple2(in2_f_packed, e);
end else begin
let signLT = (in1_f.sign && !in2_f.sign);
let signEQ = in1_f.sign == in2_f.sign;
let absLT = {in1_f.exp, in1_f.sfd} < {in2_f.exp, in2_f.sfd};
if (signLT || (signEQ && (in1_f.sign ? !absLT : absLT))) begin
return tuple2(in1, e);
return tuple2(in1_f_packed, e);
end else begin
return tuple2(in2, e);
return tuple2(in2_f_packed, e);
end
end
endfunction
@@ -202,9 +211,14 @@ function Tuple2#(Bit#(64), FpuException) fmin_d(Bit#(64) in1, Bit#(64) in2);
Double nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
// nirajns: TEST 21 failure on fmin ISA tests
// e.invalid_op should only be signalled only if either operand is a sNaN
// as the fmin and fmax are quiet comparison
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
e.invalid_op = True;
return tuple2(pack(nan_f), e);
end else if (isNaN(in1_f) && isNaN(in2_f)) begin
return tuple2(zeroExtend(pack(nan_f)), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
end else if (isNaN(in1_f)) begin
@@ -222,26 +236,39 @@ function Tuple2#(Bit#(64), FpuException) fmin_d(Bit#(64) in1, Bit#(64) in2);
endfunction
function Tuple2#(Bit#(64), FpuException) fmax_s(Bit#(64) in1, Bit#(64) in2);
Float in1_f = unpack(in1[31:0]);
Float in2_f = unpack(in2[31:0]);
// nirajns: interpret the inputs as floats. Observe that this function
// receives raw bits.
// If the raw bits are nan-boxed, the fv_nanbox(fv_unbox) are identity
// functions. However, if the raw input was not properly nanboxed, then
// the output would be a canonical NaN
Float in1_f = fv_unbox(in1);
Float in2_f = fv_unbox(in2);
Bit #(64) in1_f_packed = fv_nanbox (zeroExtend(pack(in1_f)));
Bit #(64) in2_f_packed = fv_nanbox (zeroExtend(pack(in2_f)));
Float nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
// nirajns: TEST 21 failure on fmin ISA tests
// e.invalid_op should only be signalled only if either operand is a sNaN
// as the fmin and fmax are quiet comparison
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
e.invalid_op = True;
return tuple2(zeroExtend(pack(nan_f)), e);
return tuple2(fv_nanbox (zeroExtend(pack(nan_f))), e);
end else if (isNaN(in1_f) && isNaN(in2_f)) begin
return tuple2(fv_nanbox (zeroExtend(pack(nan_f))), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
return tuple2(in1_f_packed, e);
end else if (isNaN(in1_f)) begin
return tuple2(in2, e);
return tuple2(in2_f_packed, e);
end else begin
let signGT = (!in1_f.sign && in2_f.sign);
let signEQ = in1_f.sign == in2_f.sign;
let absGT = {in1_f.exp, in1_f.sfd} > {in2_f.exp, in2_f.sfd};
if (signGT || (signEQ && (in1_f.sign ? !absGT : absGT))) begin
return tuple2(in1, e);
return tuple2(in1_f_packed, e);
end else begin
return tuple2(in2, e);
return tuple2(in2_f_packed, e);
end
end
endfunction
@@ -252,9 +279,14 @@ function Tuple2#(Bit#(64), FpuException) fmax_d(Bit#(64) in1, Bit#(64) in2);
Double nan_f = qnan(); // canonical NAN
FpuException e = unpack(0);
if (isSNaN(in1_f) || isSNaN(in2_f) || (isNaN(in1_f) && isNaN(in2_f))) begin
// nirajns: TEST 21 failure on fmin ISA tests
// e.invalid_op should only be signalled only if either operand is a sNaN
// as the fmin and fmax are quiet comparison
if (isSNaN(in1_f) || isSNaN(in2_f)) begin
e.invalid_op = True;
return tuple2(pack(nan_f), e);
end else if (isNaN(in1_f) && isNaN(in2_f)) begin
return tuple2(zeroExtend(pack(nan_f)), e);
end else if (isNaN(in2_f)) begin
return tuple2(in1, e);
end else if (isNaN(in1_f)) begin
@@ -417,8 +449,9 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
if (fpu_inst.precision == Single) begin
// single precision
Float in1 = unpack(rVal1[31:0]);
Float in2 = unpack(rVal2[31:0]);
// nirajns: interpret them as floats
Float in1 = fv_unbox(rVal1);
Float in2 = fv_unbox(rVal2);
Float dst = unpack(0);
Maybe#(Data) full_dst = Invalid;
FpuException e = unpack(0);
@@ -436,18 +469,33 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
{x, e} = fmax_s(rVal1, rVal2);
full_dst = tagged Valid x;
end
FEq: dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
FEq: begin
// nirajns: TEST 10 failure on fcmp ISA tests
Data x;
if (isNaN (in1) || isNaN (in2)) x = 0;
else x = zeroExtend(pack(compareFP(in1, in2) == EQ));
if (isSNaN(in1) || isSNaN(in2)) begin
e.invalid_op = True;
end
full_dst = tagged Valid x;
end
FLt: begin
dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
Data x;
if (isNaN (in1) || isNaN (in2)) x = 0;
else x = zeroExtend(pack(compareFP(in1, in2) == LT));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
full_dst = tagged Valid x;
end
FLe: begin
dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
Data x;
if (isNaN (in1) || isNaN (in2)) x = 0;
else x = zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ)));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
full_dst = tagged Valid x;
end
// CLASS functions
FClass: begin
@@ -481,9 +529,11 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
dst.sign = unpack(pack(in1.sign) ^ pack(in2.sign));
end
// Float -> Bits
FMv_XF: full_dst = tagged Valid signExtend(pack(in1));
// nirajns: don't interpret the bits - use raw bits rVal1
FMv_XF: full_dst = tagged Valid signExtend(pack(rVal1[31:0]));
// Bits -> Float
FMv_FX: full_dst = tagged Valid zeroExtend(pack(in1));
// nirajns: don't interpret the bits - use raw bits rVal1
FMv_FX: full_dst = tagged Valid fv_nanbox (zeroExtend(pack(rVal1[31:0])));
// Float -> Float
FCvt_FF: begin
Double in1_double = unpack(rVal1);
@@ -529,7 +579,7 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
if (isNaN(dst)) dst = canonicalNaN;
end
endcase
fpu_result.data = (full_dst matches tagged Valid .data ? data : zeroExtend(pack(dst)));
fpu_result.data = (full_dst matches tagged Valid .data ? data : fv_nanbox(zeroExtend(pack(dst))));
fpu_result.fflags = pack(e);
end else if (fpu_inst.precision == Double) begin
// double precision
@@ -552,15 +602,24 @@ function FpuResult execFpuSimple(FpuInst fpu_inst, Data rVal1, Data rVal2);
{x, e} = fmax_d(rVal1, rVal2);
full_dst = tagged Valid x;
end
FEq: dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
FEq: begin
// nirajns: TEST 10 failure on fcmp ISA tests
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
else dst = unpack(zeroExtend(pack(compareFP(in1, in2) == EQ)));
if (isSNaN(in1) || isSNaN(in2)) begin
e.invalid_op = True;
end
end
FLt: begin
dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
else dst = unpack(zeroExtend(pack(compareFP(in1, in2) == LT)));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
end
FLe: begin
dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
if (isNaN (in1) || isNaN (in2)) dst = unpack (0);
else dst = unpack(zeroExtend(pack((compareFP(in1, in2) == LT) || (compareFP(in1, in2) == EQ))));
if (isNaN(in1) || isNaN(in2)) begin
e.invalid_op = True;
end
@@ -720,7 +779,7 @@ module mkFpuExecPipeline(FpuExec);
// canonicalize NaN
out_f = isNaN(out_f) ? canonicalNaN : out_f;
res = FpuResult {
data: zeroExtend(pack(out_f)),
data: fv_nanbox (zeroExtend(pack(out_f))),
fflags: pack(info.exc_conv_in | exc_op | exc_conv_out)
};
end
@@ -778,9 +837,10 @@ module mkFpuExecPipeline(FpuExec);
Double in3 = unpack(rVal3);
if (fpu_inst.precision == Single) begin
// conver single to double
Float f1 = unpack(rVal1[31:0]);
Float f2 = unpack(rVal2[31:0]);
Float f3 = unpack(rVal3[31:0]);
// nirajns: interpret the raw bits as floats first
Float f1 = fv_unbox(rVal1);
Float f2 = fv_unbox(rVal2);
Float f3 = fv_unbox(rVal3);
let {d1, exc1} = fcvt_d_s(f1, fpu_rm);
let {d2, exc2} = fcvt_d_s(f2, fpu_rm);
let {d3, exc3} = fcvt_d_s(f3, fpu_rm);

View File

@@ -33,6 +33,7 @@ import HasSpecBits::*;
import SpecFifo::*;
import StoreBuffer::*;
import Exec::*;
import FP_Utils::*;
// I don't want to export auxiliary functions, so manually export all types
export LdQMemFunc(..);
@@ -1992,9 +1993,20 @@ module mkSplitLSQ(SplitLSQ);
// mark load as done, and shift resp
ld_done_resp[t] <= True;
res.wrongPath = False;
// nirajns: checking if this is a 32-bit load response to a FPR
// In that case, the data needs to be nanboxed before writing to
// the register files as the Toooba FPR is 64-bit
let bEn = ld_byteEn[t];
let dst = ld_dst[t];
let is32BitLd = (bEn[3] && !bEn[7]);
res.dst = ld_dst[t];
res.data = gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
ld_unsigned[t], alignedData);
if (dst.Valid.isFpuReg && is32BitLd)
res.data = fv_nanbox (gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
ld_unsigned[t], alignedData));
else
res.data = gatherLoad(ld_paddr_resp[t], ld_byteEn[t],
ld_unsigned[t], alignedData);
end
if(verbose) begin
$display("[LSQ - respLd] ", fshow(t), "; ", fshow(alignedData),