FPU: Reorganize NaN and infinity handling and improve arch compliance

The architecture specifies that an invalid operation exception for
signalling NaN (VXSNAN) can occur in the same instructions as an
invalid operation exception for infinity times zero (VXIMZ) in the
case of a multiply-add instruction where B is a signalling NaN, and
one of A and C is infinity and the other is zero.  This moves the
invalid operation tests around so as to handle this case correctly.
It also restructures the infinity and NaN cases to simplify the logic
a little.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pull/442/head
Paul Mackerras 1 year ago
parent 9ac71cfbf2
commit 7812a55b6c

@ -838,6 +838,7 @@ begin
variable set_y : std_ulogic; variable set_y : std_ulogic;
variable set_s : std_ulogic; variable set_s : std_ulogic;
variable qnan_result : std_ulogic; variable qnan_result : std_ulogic;
variable invalid_mul : std_ulogic;
variable px_nz : std_ulogic; variable px_nz : std_ulogic;
variable pcmpb_eq : std_ulogic; variable pcmpb_eq : std_ulogic;
variable pcmpb_lt : std_ulogic; variable pcmpb_lt : std_ulogic;
@ -1217,6 +1218,7 @@ begin
-- At least one floating-point operand is infinity or NaN -- At least one floating-point operand is infinity or NaN
v.fpscr(FPSCR_FR) := '0'; v.fpscr(FPSCR_FR) := '0';
v.fpscr(FPSCR_FI) := '0'; v.fpscr(FPSCR_FI) := '0';
invalid_mul := '0';


if (r.a.class = NAN and r.a.mantissa(QNAN_BIT) = '0') or if (r.a.class = NAN and r.a.mantissa(QNAN_BIT) = '0') or
(r.b.class = NAN and r.b.mantissa(QNAN_BIT) = '0') or (r.b.class = NAN and r.b.mantissa(QNAN_BIT) = '0') or
@ -1225,6 +1227,15 @@ begin
v.fpscr(FPSCR_VXSNAN) := '1'; v.fpscr(FPSCR_VXSNAN) := '1';
invalid := '1'; invalid := '1';
end if; end if;
-- Check for this case here since VXIMZ can be set along with VXSNAN
if r.is_multiply = '1' and
((r.a.class = INFINITY and r.c.class = ZERO) or
(r.a.class = ZERO and r.c.class = INFINITY)) then
v.fpscr(FPSCR_VXIMZ) := '1';
qnan_result := '1';
invalid_mul := '1';
end if;

if r.a.class = NAN or r.b.class = NAN or r.c.class = NAN then if r.a.class = NAN or r.b.class = NAN or r.c.class = NAN then
if r.int_result = '1' then if r.int_result = '1' then
v.state := INT_OFLOW; v.state := INT_OFLOW;
@ -1241,51 +1252,32 @@ begin
end if; end if;


else else
if r.a.class = INFINITY then if (r.a.class = INFINITY or r.c.class = INFINITY) and invalid_mul = '0' then
if r.is_multiply = '1' and r.c.class = ZERO then sign_inv := r.is_multiply and r.is_subtract;
-- invalid operation, construct QNaN if r.is_subtract = '1' and r.b.class = INFINITY then
v.fpscr(FPSCR_VXIMZ) := '1';
qnan_result := '1';
elsif r.is_subtract = '1' and r.b.class = INFINITY then
v.fpscr(FPSCR_VXISI) := '1'; v.fpscr(FPSCR_VXISI) := '1';
qnan_result := '1'; qnan_result := '1';
elsif r.is_inverse = '1' and r.b.class = INFINITY then
v.fpscr(FPSCR_VXIDI) := '1';
qnan_result := '1';
else
sign_inv := r.is_multiply and r.is_subtract;
v.result_class := INFINITY;
end if; end if;
arith_done := '1'; end if;
elsif r.c.class = INFINITY then if r.is_inverse = '1' and r.a.class = INFINITY and r.b.class = INFINITY then
if r.is_multiply = '1' and r.a.class = ZERO then v.fpscr(FPSCR_VXIDI) := '1';
-- invalid operation, construct QNaN qnan_result := '1';
v.fpscr(FPSCR_VXIMZ) := '1'; end if;
qnan_result := '1'; if r.b.class = INFINITY and r.is_sqrt = '1' and r.b.negative = '1' then
elsif r.is_subtract = '1' and r.b.class = INFINITY then v.fpscr(FPSCR_VXSQRT) := '1';
v.fpscr(FPSCR_VXISI) := '1'; qnan_result := '1';
qnan_result := '1'; end if;
else if r.b.class = INFINITY and r.is_inverse = '1' then
sign_inv := r.is_multiply and r.is_subtract; -- fdiv, fre, frsqrte
v.result_class := INFINITY; v.result_class := ZERO;
end if;
arith_done := '1';
else else
-- r.b.class = INFINITY v.result_class := INFINITY;
if r.int_result = '1' then end if;
-- fcti* if r.b.class = INFINITY and r.int_result = '1' then
v.state := INT_OFLOW; -- fcti*
elsif r.is_sqrt = '1' and r.b.negative = '1' then v.state := INT_OFLOW;
v.fpscr(FPSCR_VXSQRT) := '1'; else
qnan_result := '1'; arith_done := '1';
elsif r.is_inverse = '1' then
-- fdiv, fre, frsqrte
v.result_class := ZERO;
arith_done := '1';
else
v.result_class := INFINITY;
arith_done := '1';
end if;
end if; end if;
end if; end if;



Loading…
Cancel
Save