FPU: Eliminate EXC_RESULT state

This lets us remove r.opsel_a and is a step towards moving the
handling of exceptional cases out to a separate process.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pull/442/head
Paul Mackerras 1 year ago
parent 850b87c83f
commit 8648ddb64f

@ -79,7 +79,6 @@ architecture behaviour of fpu is
RENORM_A, RENORM_A2, RENORM_A, RENORM_A2,
RENORM_B, RENORM_B2, RENORM_B, RENORM_B2,
RENORM_C, RENORM_C2, RENORM_C, RENORM_C2,
EXC_RESULT,
IDIV_NORMB, IDIV_NORMB2, IDIV_NORMB3, IDIV_NORMB, IDIV_NORMB2, IDIV_NORMB3,
IDIV_CLZA, IDIV_CLZA2, IDIV_CLZA3, IDIV_CLZA, IDIV_CLZA2, IDIV_CLZA3,
IDIV_NR0, IDIV_NR1, IDIV_NR2, IDIV_USE0_5, IDIV_NR0, IDIV_NR1, IDIV_NR2, IDIV_USE0_5,
@ -152,7 +151,6 @@ architecture behaviour of fpu is
first : std_ulogic; first : std_ulogic;
count : unsigned(1 downto 0); count : unsigned(1 downto 0);
doing_ftdiv : std_ulogic_vector(1 downto 0); doing_ftdiv : std_ulogic_vector(1 downto 0);
opsel_a : std_ulogic_vector(1 downto 0);
use_a : std_ulogic; use_a : std_ulogic;
use_b : std_ulogic; use_b : std_ulogic;
use_c : std_ulogic; use_c : std_ulogic;
@ -872,7 +870,6 @@ begin
variable rsgn_op : std_ulogic_vector(1 downto 0); variable rsgn_op : std_ulogic_vector(1 downto 0);
variable is_nan_inf : std_ulogic; variable is_nan_inf : std_ulogic;
variable is_zero_den : std_ulogic; variable is_zero_den : std_ulogic;
variable sign_inv : std_ulogic;
begin begin
v := r; v := r;
v.complete := '0'; v.complete := '0';
@ -881,7 +878,6 @@ begin
exec_state := IDLE; exec_state := IDLE;
is_nan_inf := '0'; is_nan_inf := '0';
is_zero_den := '0'; is_zero_den := '0';
sign_inv := '0';
v.cycle_1 := e_in.valid; v.cycle_1 := e_in.valid;


if r.complete = '1' or r.do_intr = '1' then if r.complete = '1' or r.do_intr = '1' then
@ -1133,7 +1129,6 @@ begin
v.update_fprf := '0'; v.update_fprf := '0';
v.first := '0'; v.first := '0';
v.doing_ftdiv := "00"; v.doing_ftdiv := "00";
v.opsel_a := AIN_R;
opsel_a <= AIN_R; opsel_a <= AIN_R;
opsel_ainv <= '0'; opsel_ainv <= '0';
opsel_mask <= '0'; opsel_mask <= '0';
@ -1216,7 +1211,13 @@ begin


when DO_NAN_INF => when DO_NAN_INF =>
-- At least one floating-point operand is infinity or NaN -- At least one floating-point operand is infinity or NaN
invalid_mul := '0'; if r.a.class = NAN then
opsel_a <= AIN_A;
elsif r.b.class = NAN then
opsel_a <= AIN_B;
else
opsel_a <= AIN_C;
end if;


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,33 +1226,34 @@ 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 -- Check for this case here since VXIMZ can be set along with VXSNAN
invalid_mul := '0';
if r.is_multiply = '1' and if r.is_multiply = '1' and
((r.a.class = INFINITY and r.c.class = ZERO) or ((r.a.class = INFINITY and r.c.class = ZERO) or
(r.a.class = ZERO and r.c.class = INFINITY)) then (r.a.class = ZERO and r.c.class = INFINITY)) then
v.fpscr(FPSCR_VXIMZ) := '1'; v.fpscr(FPSCR_VXIMZ) := '1';
qnan_result := '1';
invalid_mul := '1'; invalid_mul := '1';
end if; end if;


if r.int_result = '1' then
opsel_r <= RES_MISC;
misc_sel <= "110";
v.fpscr(FPSCR_VXCVI) := '1';
invalid := '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 rsgn_op := RSGN_SEL;
v.state := INT_OFLOW; v.result_class := NAN;
else
if r.a.class = NAN then
v.opsel_a := AIN_A;
elsif r.b.class = NAN then
v.opsel_a := AIN_B;
elsif r.c.class = NAN then
v.opsel_a := AIN_C;
end if;
rsgn_op := RSGN_SEL;
v.state := EXC_RESULT;
end if;


else else
if (r.a.class = INFINITY or r.c.class = INFINITY) and invalid_mul = '0' then if invalid_mul = '1' then
sign_inv := r.is_multiply and r.is_subtract; qnan_result := '1';
elsif (r.a.class = INFINITY or r.c.class = INFINITY) then
if r.is_multiply = '1' then
rsgn_op := RSGN_SUB;
end if;
if r.is_subtract = '1' and r.b.class = INFINITY then if 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';
@ -1271,67 +1273,55 @@ begin
else else
v.result_class := INFINITY; v.result_class := INFINITY;
end if; end if;
if r.b.class = INFINITY and r.int_result = '1' then
-- fcti*
v.state := INT_OFLOW;
else
arith_done := '1';
end if;
end if; end if;
arith_done := '1';


when DO_ZERO_DEN => when DO_ZERO_DEN =>
-- At least one floating point operand is zero or denormalized -- At least one floating point operand is zero or denormalized
if r.is_addition = '1' then if r.use_a = '1' and r.a.class = ZERO then
opsel_a <= AIN_A;
else
opsel_a <= AIN_B; opsel_a <= AIN_B;
end if; re_sel2 <= REXP2_B;
if (r.use_a = '1' and r.a.class = ZERO) or re_set_result <= '1';
(r.use_b = '1' and r.b.class = ZERO and r.is_multiply = '0') or if r.is_inverse = '1' and r.b.class = ZERO then
(r.use_c = '1' and r.c.class = ZERO) then -- fdiv with B=0
if r.use_a = '1' and r.a.class = ZERO then v.fpscr(FPSCR_VXZDZ) := '1';
if r.is_inverse = '1' then qnan_result := '1';
-- fdiv; result is 0 unless B=0 end if;
if r.b.class = ZERO then if r.is_addition = '1' then
v.fpscr(FPSCR_VXZDZ) := '1'; -- result is +/- B
qnan_result := '1'; v.result_class := r.b.class;
else
v.result_class := ZERO;
end if;
arith_done := '1';
elsif r.is_addition = '1' then
-- result is +/- B
v.opsel_a := AIN_B;
v.state := EXC_RESULT;
else
v.result_class := ZERO;
arith_done := '1';
end if;
elsif r.use_c = '1' and r.c.class = ZERO then
v.opsel_a := AIN_B;
v.state := EXC_RESULT;
else else
-- B is zero, other operands are finite v.result_class := ZERO;
if r.int_result = '1' then
-- fcti*
arith_done := '1';
elsif r.is_inverse = '1' then
-- fdiv, fre, frsqrte
v.result_class := INFINITY;
zero_divide := '1';
arith_done := '1';
elsif r.is_addition = '1' then
-- fadd, fsub
v.result_class := FINITE;
re_sel1 <= REXP1_A;
re_set_result <= '1';
arith_done := '1';
else
-- other things, result is zero
v.result_class := ZERO;
arith_done := '1';
end if;
end if; end if;
arith_done := '1';
elsif r.use_c = '1' and r.c.class = ZERO then
-- fmul or fmadd/sub with C=0
opsel_a <= AIN_B;
re_sel2 <= REXP2_B;
re_set_result <= '1';
if r.is_addition = '1' then
v.result_class := r.b.class;
else
v.result_class := ZERO;
end if;
arith_done := '1';
elsif (r.use_b = '1' and r.b.class = ZERO and r.is_multiply = '0') then
-- B is zero, other operands are finite, not fmadd*
opsel_a <= AIN_A;
re_sel1 <= REXP1_A;
re_set_result <= '1';
if r.is_inverse = '1' then
-- fdiv, fre, frsqrte
v.result_class := INFINITY;
zero_divide := '1';
elsif r.is_addition = '1' then
-- fadd, fsub
v.result_class := FINITE;
else
-- other things, result is zero
v.result_class := ZERO;
end if;
arith_done := '1';


else else
-- some operand is denorm, and/or it's fmadd/fmsub with B=0 -- some operand is denorm, and/or it's fmadd/fmsub with B=0
@ -1682,11 +1672,16 @@ begin
when DO_FSEL => when DO_FSEL =>
rsgn_op := RSGN_SEL; rsgn_op := RSGN_SEL;
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; opsel_a <= AIN_C;
re_sel2 <= REXP2_C;
v.result_class := r.c.class;
else else
v.opsel_a := AIN_B; opsel_a <= AIN_B;
re_sel2 <= REXP2_B;
v.result_class := r.b.class;
end if; end if;
v.state := EXC_RESULT; re_set_result <= '1';
arith_done := '1';


when DO_FSQRT => when DO_FSQRT =>
opsel_a <= AIN_B; opsel_a <= AIN_B;
@ -2572,23 +2567,6 @@ begin
re_set_result <= '1'; re_set_result <= '1';
arith_done := '1'; arith_done := '1';


when EXC_RESULT =>
-- r.opsel_a = AIN_A, AIN_B or AIN_C according to which input is the result
opsel_a <= r.opsel_a;
case r.opsel_a is
when AIN_B =>
re_sel2 <= REXP2_B;
v.result_class := r.b.class;
when AIN_C =>
re_sel2 <= REXP2_C;
v.result_class := r.c.class;
when others =>
re_sel1 <= REXP1_A;
v.result_class := r.a.class;
end case;
re_set_result <= '1';
arith_done := '1';

when DO_IDIVMOD => when DO_IDIVMOD =>
opsel_a <= AIN_B; opsel_a <= AIN_B;
if r.b.class = ZERO then if r.b.class = ZERO then
@ -3113,25 +3091,28 @@ begin


end case; end case;


rsign := r.result_sign;
case rsgn_op is case rsgn_op is
when RSGN_SEL => when RSGN_SEL =>
case v.opsel_a is case opsel_a is
when AIN_A => when AIN_A =>
v.result_sign := r.a.negative; rsign := r.a.negative;
when AIN_B => when AIN_B =>
v.result_sign := r.b.negative; rsign := r.b.negative;
when AIN_C => when AIN_C =>
v.result_sign := r.c.negative; rsign := r.c.negative;
when others => when others =>
end case; end case;
v.result_sign := rsign;
when RSGN_SUB => when RSGN_SUB =>
v.result_sign := r.result_sign xor r.is_subtract; rsign := r.result_sign xor r.is_subtract;
v.result_sign := rsign;
when RSGN_INV => when RSGN_INV =>
v.result_sign := not r.result_sign; rsign := not r.result_sign;
v.result_sign := rsign;
when others => when others =>
end case; end case;


rsign := r.result_sign xor sign_inv;
if zero_divide = '1' then if zero_divide = '1' then
v.fpscr(FPSCR_ZX) := '1'; v.fpscr(FPSCR_ZX) := '1';
end if; end if;

Loading…
Cancel
Save