FPU: Set sign of 0 result of subtraction in pack_dp

When a floating-point subtraction results in a zero result, the sign
of the result is required to be positive in all rounding modes except
the round to minus infinity mode, when it is negative.  Consolidate
the logic for doing this in one place, in the pack_dp function,
instead of having it at each place where a zero result is generated.

Since fnmadd[s] and fnmsub[s] negate the result after this rule has
been applied, we use the r.negate signal to indicate a negation which
is now done in pack_dp.  Thus the EXC_RESULT state no longer uses
r.negate, and in fact doesn't set v.result_sign at all; that is now
done in the states that lead into EXC_RESULT.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pull/382/head
Paul Mackerras 3 years ago
parent 932da4c114
commit 09965b9102

@ -169,6 +169,9 @@ architecture behaviour of fpu is
oe : std_ulogic; oe : std_ulogic;
xerc : xer_common_t; xerc : xer_common_t;
xerc_result : xer_common_t; xerc_result : xer_common_t;
res_negate : std_ulogic;
res_subtract : std_ulogic;
res_rmode : std_ulogic_vector(2 downto 0);
end record; end record;


type lookup_table is array(0 to 1023) of std_ulogic_vector(17 downto 0); type lookup_table is array(0 to 1023) of std_ulogic_vector(17 downto 0);
@ -605,15 +608,21 @@ architecture behaviour of fpu is
end; end;


-- Construct a DP floating-point result from components -- Construct a DP floating-point result from components
function pack_dp(sign: std_ulogic; class: fp_number_class; exp: signed(EXP_BITS-1 downto 0); function pack_dp(negative: std_ulogic; class: fp_number_class; exp: signed(EXP_BITS-1 downto 0);
mantissa: std_ulogic_vector; single_prec: std_ulogic; quieten_nan: std_ulogic) mantissa: std_ulogic_vector; single_prec: std_ulogic; quieten_nan: std_ulogic;
negate: std_ulogic; is_subtract: std_ulogic; round_mode: std_ulogic_vector)
return std_ulogic_vector is return std_ulogic_vector is
variable dp_result : std_ulogic_vector(63 downto 0); variable dp_result : std_ulogic_vector(63 downto 0);
variable sign : std_ulogic;
begin begin
dp_result := (others => '0'); dp_result := (others => '0');
dp_result(63) := sign; sign := negative;
case class is case class is
when ZERO => when ZERO =>
if is_subtract = '1' then
-- set result sign depending on rounding mode
sign := round_mode(0) and round_mode(1);
end if;
when FINITE => when FINITE =>
if mantissa(UNIT_BIT) = '1' then if mantissa(UNIT_BIT) = '1' then
-- normalized number -- normalized number
@ -633,6 +642,7 @@ architecture behaviour of fpu is
dp_result(28 downto 0) := mantissa(SP_LSB - 1 downto DP_LSB); dp_result(28 downto 0) := mantissa(SP_LSB - 1 downto DP_LSB);
end if; end if;
end case; end case;
dp_result(63) := sign xor negate;
return dp_result; return dp_result;
end; end;


@ -1468,8 +1478,8 @@ begin
v.fpscr(FPSCR_FR) := '0'; v.fpscr(FPSCR_FR) := '0';
v.fpscr(FPSCR_FI) := '0'; v.fpscr(FPSCR_FI) := '0';
is_add := r.a.negative xor r.b.negative xor r.insn(1); is_add := r.a.negative xor r.b.negative xor r.insn(1);
v.is_subtract := not is_add;
if r.a.class = FINITE and r.b.class = FINITE then if r.a.class = FINITE and r.b.class = FINITE then
v.is_subtract := not is_add;
v.add_bsmall := r.exp_cmp; v.add_bsmall := r.exp_cmp;
v.opsel_a := AIN_B; v.opsel_a := AIN_B;
if r.exp_cmp = '0' then if r.exp_cmp = '0' then
@ -1491,18 +1501,13 @@ begin
v.fpscr(FPSCR_VXISI) := '1'; v.fpscr(FPSCR_VXISI) := '1';
qnan_result := '1'; qnan_result := '1';
arith_done := '1'; arith_done := '1';
elsif r.a.class = ZERO and r.b.class = ZERO and is_add = '0' then
-- return -0 for rounding to -infinity
v.result_sign := r.round_mode(1) and r.round_mode(0);
arith_done := '1';
elsif r.a.class = INFINITY or r.b.class = ZERO then elsif r.a.class = INFINITY or r.b.class = ZERO then
-- result is A -- result is A; we're already set up to put A into R
v.opsel_a := AIN_A; arith_done := '1';
v.state := EXC_RESULT;
else else
-- result is +/- B -- result is +/- B
v.opsel_a := AIN_B; v.opsel_a := AIN_B;
v.negate := not r.insn(1); v.result_sign := r.b.negative xnor r.insn(1);
v.state := EXC_RESULT; v.state := EXC_RESULT;
end if; end if;
end if; end if;
@ -1541,7 +1546,6 @@ begin
else else
-- r.c.class is ZERO or INFINITY -- r.c.class is ZERO or INFINITY
v.opsel_a := AIN_C; v.opsel_a := AIN_C;
v.negate := r.a.negative;
v.state := EXC_RESULT; v.state := EXC_RESULT;
end if; end if;
end if; end if;
@ -1599,8 +1603,10 @@ begin
v.fpscr(FPSCR_FI) := '0'; v.fpscr(FPSCR_FI) := '0';
if r.a.class = ZERO or (r.a.negative = '0' and r.a.class /= NAN) then if r.a.class = ZERO or (r.a.negative = '0' and r.a.class /= NAN) then
v.opsel_a := AIN_C; v.opsel_a := AIN_C;
v.result_sign := r.c.negative;
else else
v.opsel_a := AIN_B; v.opsel_a := AIN_B;
v.result_sign := r.b.negative;
end if; end if;
v.quieten_nan := '0'; v.quieten_nan := '0';
v.state := EXC_RESULT; v.state := EXC_RESULT;
@ -1720,9 +1726,10 @@ begin
v.fpscr(FPSCR_FR) := '0'; v.fpscr(FPSCR_FR) := '0';
v.fpscr(FPSCR_FI) := '0'; v.fpscr(FPSCR_FI) := '0';
is_add := r.a.negative xor r.c.negative xor r.b.negative xor r.insn(1); is_add := r.a.negative xor r.c.negative xor r.b.negative xor r.insn(1);
v.negate := r.insn(2);
v.is_subtract := not is_add;
if r.a.class = FINITE and r.c.class = FINITE and if r.a.class = FINITE and r.c.class = FINITE and
(r.b.class = FINITE or r.b.class = ZERO) then (r.b.class = FINITE or r.b.class = ZERO) then
v.is_subtract := not is_add;
-- Make sure A and C are normalized -- Make sure A and C are normalized
if r.a.mantissa(UNIT_BIT) = '0' then if r.a.mantissa(UNIT_BIT) = '0' then
v.state := RENORM_A; v.state := RENORM_A;
@ -1730,13 +1737,13 @@ begin
v.state := RENORM_C; v.state := RENORM_C;
elsif r.b.class = ZERO then elsif r.b.class = ZERO then
-- no addend, degenerates to multiply -- no addend, degenerates to multiply
v.result_sign := r.a.negative xor r.c.negative xor r.insn(2); v.result_sign := r.a.negative xor r.c.negative;
f_to_multiply.valid <= '1'; f_to_multiply.valid <= '1';
v.is_multiply := '1'; v.is_multiply := '1';
v.state := MULT_1; v.state := MULT_1;
elsif r.madd_cmp = '0' then elsif r.madd_cmp = '0' then
-- addend is bigger, do multiply first -- addend is bigger, do multiply first
v.result_sign := not (r.b.negative xor r.insn(1) xor r.insn(2)); v.result_sign := r.b.negative xnor r.insn(1);
f_to_multiply.valid <= '1'; f_to_multiply.valid <= '1';
v.first := '1'; v.first := '1';
v.state := FMADD_0; v.state := FMADD_0;
@ -1760,19 +1767,14 @@ begin
else else
-- result is infinity -- result is infinity
v.result_class := INFINITY; v.result_class := INFINITY;
v.result_sign := r.a.negative xor r.c.negative xor r.insn(2); v.result_sign := r.a.negative xor r.c.negative;
arith_done := '1'; arith_done := '1';
end if; end if;
else else
-- Here A is zero, C is zero, or B is infinity -- Here A is zero, C is zero, or B is infinity
-- Result is +/-B in all of those cases -- Result is +/-B in all of those cases
v.opsel_a := AIN_B; v.opsel_a := AIN_B;
if r.b.class /= ZERO or is_add = '1' then v.result_sign := r.b.negative xnor r.insn(1);
v.negate := not (r.insn(1) xor r.insn(2));
else
-- have to be careful about rule for 0 - 0 result sign
v.negate := r.b.negative xor (r.round_mode(1) and r.round_mode(0)) xor r.insn(2);
end if;
v.state := EXC_RESULT; v.state := EXC_RESULT;
end if; end if;
end if; end if;
@ -1910,10 +1912,6 @@ begin
elsif (r_hi_nz or r_lo_nz or (or (r.r(DP_LSB - 1 downto 0)))) = '0' then elsif (r_hi_nz or r_lo_nz or (or (r.r(DP_LSB - 1 downto 0)))) = '0' then
-- r.x must be zero at this point -- r.x must be zero at this point
v.result_class := ZERO; v.result_class := ZERO;
if r.is_subtract = '1' then
-- set result sign depending on rounding mode
v.result_sign := r.round_mode(1) and r.round_mode(0);
end if;
arith_done := '1'; arith_done := '1';
else else
rs_norm <= '1'; rs_norm <= '1';
@ -1970,7 +1968,7 @@ begin
-- product is bigger here -- product is bigger here
-- shift B right and use it as the addend to the multiplier -- shift B right and use it as the addend to the multiplier
-- for subtract, multiplier does B - A * C -- for subtract, multiplier does B - A * C
v.result_sign := r.a.negative xor r.c.negative xor r.insn(2) xor r.is_subtract; v.result_sign := r.a.negative xor r.c.negative xor r.is_subtract;
re_sel2 <= REXP2_B; re_sel2 <= REXP2_B;
re_set_result <= '1'; re_set_result <= '1';
-- set shift to b.exp - result_exp + 64 -- set shift to b.exp - result_exp + 64
@ -2030,7 +2028,6 @@ begin
if s_nz = '0' then if s_nz = '0' then
-- must be a subtraction, and r.x must be zero -- must be a subtraction, and r.x must be zero
v.result_class := ZERO; v.result_class := ZERO;
v.result_sign := r.round_mode(1) and r.round_mode(0);
arith_done := '1'; arith_done := '1';
else else
-- R is all zeroes but there are non-zero bits in S -- R is all zeroes but there are non-zero bits in S
@ -2572,10 +2569,6 @@ begin
rs_neg2 <= '1'; rs_neg2 <= '1';
if mant_nz = '0' then if mant_nz = '0' then
v.result_class := ZERO; v.result_class := ZERO;
if r.is_subtract = '1' then
-- set result sign depending on rounding mode
v.result_sign := r.round_mode(1) and r.round_mode(0);
end if;
arith_done := '1'; arith_done := '1';
else else
-- Renormalize result after rounding -- Renormalize result after rounding
@ -2597,6 +2590,7 @@ begin
arith_done := '1'; arith_done := '1';


when NAN_RESULT => when NAN_RESULT =>
v.negate := '0';
if (r.use_a = '1' and r.a.class = NAN and r.a.mantissa(QNAN_BIT) = '0') or if (r.use_a = '1' and r.a.class = NAN and r.a.mantissa(QNAN_BIT) = '0') or
(r.use_b = '1' and r.b.class = NAN and r.b.mantissa(QNAN_BIT) = '0') or (r.use_b = '1' and r.b.class = NAN and r.b.mantissa(QNAN_BIT) = '0') or
(r.use_c = '1' and r.c.class = NAN and r.c.mantissa(QNAN_BIT) = '0') then (r.use_c = '1' and r.c.class = NAN and r.c.mantissa(QNAN_BIT) = '0') then
@ -2606,10 +2600,13 @@ begin
end if; end if;
if r.use_a = '1' and r.a.class = NAN then if r.use_a = '1' and r.a.class = NAN then
v.opsel_a := AIN_A; v.opsel_a := AIN_A;
v.result_sign := r.a.negative;
elsif r.use_b = '1' and r.b.class = NAN then elsif r.use_b = '1' and r.b.class = NAN then
v.opsel_a := AIN_B; v.opsel_a := AIN_B;
v.result_sign := r.b.negative;
elsif r.use_c = '1' and r.c.class = NAN then elsif r.use_c = '1' and r.c.class = NAN then
v.opsel_a := AIN_C; v.opsel_a := AIN_C;
v.result_sign := r.c.negative;
end if; end if;
v.state := EXC_RESULT; v.state := EXC_RESULT;


@ -2617,15 +2614,12 @@ begin
-- r.opsel_a = AIN_A, AIN_B or AIN_C according to which input is the result -- r.opsel_a = AIN_A, AIN_B or AIN_C according to which input is the result
case r.opsel_a is case r.opsel_a is
when AIN_B => when AIN_B =>
v.result_sign := r.b.negative xor r.negate;
re_sel2 <= REXP2_B; re_sel2 <= REXP2_B;
v.result_class := r.b.class; v.result_class := r.b.class;
when AIN_C => when AIN_C =>
v.result_sign := r.c.negative xor r.negate;
re_sel2 <= REXP2_C; re_sel2 <= REXP2_C;
v.result_class := r.c.class; v.result_class := r.c.class;
when others => when others =>
v.result_sign := r.a.negative xor r.negate;
re_sel1 <= REXP1_A; re_sel1 <= REXP1_A;
v.result_class := r.a.class; v.result_class := r.a.class;
end case; end case;
@ -3171,6 +3165,7 @@ begin
invalid := '1'; invalid := '1';
v.result_class := NAN; v.result_class := NAN;
v.result_sign := '0'; v.result_sign := '0';
v.negate := '0';
misc_sel <= "0001"; misc_sel <= "0001";
opsel_r <= RES_MISC; opsel_r <= RES_MISC;
arith_done := '1'; arith_done := '1';
@ -3550,6 +3545,9 @@ begin
v.int_result := int_result; v.int_result := int_result;
v.illegal := illegal; v.illegal := illegal;
v.nsnan_result := v.quieten_nan; v.nsnan_result := v.quieten_nan;
v.res_negate := v.negate;
v.res_subtract := v.is_subtract;
v.res_rmode := r.round_mode;
if r.integer_op = '1' then if r.integer_op = '1' then
v.cr_mask := num_to_fxm(0); v.cr_mask := num_to_fxm(0);
elsif r.is_cmp = '0' then elsif r.is_cmp = '0' then
@ -3581,7 +3579,8 @@ begin
fp_result <= r.r; fp_result <= r.r;
else else
fp_result <= pack_dp(r.result_sign, r.result_class, r.result_exp, r.r, fp_result <= pack_dp(r.result_sign, r.result_class, r.result_exp, r.r,
r.sp_result, r.nsnan_result); r.sp_result, r.nsnan_result,
r.res_negate, r.res_subtract, r.res_rmode);
end if; end if;


rin <= v; rin <= v;

Loading…
Cancel
Save