FPU: Add logic for 32-bit integer division

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pull/379/head
Paul Mackerras 2 years ago
parent a95f8aab38
commit 34330552e8

@ -80,7 +80,7 @@ architecture behaviour of fpu is
IDIV_NORMB, IDIV_NORMB2, IDIV_NORMB3,
IDIV_CLZA, IDIV_CLZA2, IDIV_CLZA3,
IDIV_NR0, IDIV_NR1, IDIV_NR2, IDIV_USE0_5,
IDIV_DODIV,
IDIV_DODIV, IDIV_SH32,
IDIV_DIV, IDIV_DIV2, IDIV_DIV3, IDIV_DIV4, IDIV_DIV5,
IDIV_DIV6, IDIV_DIV7, IDIV_DIV8, IDIV_DIV9,
IDIV_EXT_TBH, IDIV_EXT_TBH2, IDIV_EXT_TBH3,
@ -445,17 +445,20 @@ architecture behaviour of fpu is

-- Split a DP floating-point number into components and work out its class.
-- If is_int = 1, the input is considered an integer
function decode_dp(fpr: std_ulogic_vector(63 downto 0); is_int: std_ulogic) return fpu_reg_type is
function decode_dp(fpr: std_ulogic_vector(63 downto 0); is_int: std_ulogic;
is_32bint: std_ulogic; is_signed: std_ulogic) return fpu_reg_type is
variable r : fpu_reg_type;
variable exp_nz : std_ulogic;
variable exp_ao : std_ulogic;
variable frac_nz : std_ulogic;
variable low_nz : std_ulogic;
variable cls : std_ulogic_vector(2 downto 0);
begin
r.negative := fpr(63);
exp_nz := or (fpr(62 downto 52));
exp_ao := and (fpr(62 downto 52));
frac_nz := or (fpr(51 downto 0));
low_nz := or (fpr(31 downto 0));
if is_int = '0' then
r.exponent := signed(resize(unsigned(fpr(62 downto 52)), EXP_BITS)) - to_signed(1023, EXP_BITS);
if exp_nz = '0' then
@ -472,6 +475,16 @@ architecture behaviour of fpu is
when "110" => r.class := INFINITY;
when others => r.class := NAN;
end case;
elsif is_32bint = '1' then
r.negative := fpr(31);
r.mantissa(31 downto 0) := fpr(31 downto 0);
r.mantissa(63 downto 32) := (others => (is_signed and fpr(31)));
r.exponent := (others => '0');
if low_nz = '1' then
r.class := FINITE;
else
r.class := ZERO;
end if;
else
r.mantissa := fpr;
r.exponent := (others => '0');
@ -659,6 +672,7 @@ begin
variable j, k : integer;
variable flm : std_ulogic_vector(7 downto 0);
variable int_input : std_ulogic;
variable is_32bint : std_ulogic;
variable mask : std_ulogic_vector(63 downto 0);
variable in_a0 : std_ulogic_vector(63 downto 0);
variable in_b0 : std_ulogic_vector(63 downto 0);
@ -710,6 +724,8 @@ begin
variable round_inc : std_ulogic_vector(63 downto 0);
variable rbit_inc : std_ulogic;
variable mult_mask : std_ulogic;
variable sign_bit : std_ulogic;
variable rnd_b32 : std_ulogic;
variable int_result : std_ulogic;
variable illegal : std_ulogic;
begin
@ -717,6 +733,7 @@ begin
v.complete := '0';
v.do_intr := '0';
int_input := '0';
is_32bint := '0';

if r.complete = '1' or r.do_intr = '1' then
v.instr_done := '0';
@ -735,12 +752,25 @@ begin
v.fe_mode := or (e_in.fe_mode);
v.dest_fpr := e_in.frt;
v.single_prec := e_in.single;
v.longmask := e_in.single;
v.is_signed := e_in.is_signed;
v.rc := e_in.rc;
v.is_cmp := e_in.out_cr;
int_input := '0';
if e_in.op = OP_FPOP_I then
v.longmask := '0';
v.divext := '0';
v.divmod := '0';
if e_in.op = OP_FPOP or e_in.op = OP_FPOP_I then
v.longmask := e_in.single;
if e_in.op = OP_FPOP_I then
int_input := '1';
end if;
else -- OP_DIV, OP_DIVE, OP_MOD
int_input := '1';
is_32bint := e_in.single;
if e_in.op = OP_DIVE then
v.divext := '1';
elsif e_in.op = OP_MOD then
v.divmod := '1';
end if;
end if;
v.quieten_nan := '1';
v.tiny := '0';
@ -751,15 +781,12 @@ begin
v.is_sqrt := '0';
v.add_bsmall := '0';
v.doing_ftdiv := "00";
v.divext := e_in.insn(8) and not e_in.insn(7);
v.divmod := not e_in.insn(8);
v.is_signed := e_in.is_signed;
v.int_ovf := '0';
v.div_close := '0';

adec := decode_dp(e_in.fra, int_input);
bdec := decode_dp(e_in.frb, int_input);
cdec := decode_dp(e_in.frc, int_input);
adec := decode_dp(e_in.fra, int_input, is_32bint, e_in.is_signed);
bdec := decode_dp(e_in.frb, int_input, is_32bint, e_in.is_signed);
cdec := decode_dp(e_in.frc, int_input, '0', '0');
v.a := adec;
v.b := bdec;
v.c := cdec;
@ -870,6 +897,7 @@ begin
shiftin0 := '0';
rbit_inc := '0';
mult_mask := '0';
rnd_b32 := '0';
int_result := '0';
illegal := '0';
case r.state is
@ -918,7 +946,7 @@ begin
else
v.state := DO_FRI;
end if;
when "01001" =>
when "01001" | "01011" =>
-- integer divides and mods, major opcode 31
v.opsel_a := AIN_B;
v.state := DO_IDIVMOD;
@ -2552,6 +2580,10 @@ begin
v.shift := to_signed(-UNIT_BIT, EXP_BITS);
v.first := '1';
v.state := IDIV_DIV;
elsif r.single_prec = '1' then
-- divwe[u][o], shift A left 32 bits
v.shift := to_signed(32, EXP_BITS);
v.state := IDIV_SH32;
elsif r.div_close = '0' then
v.shift := to_signed(64 - UNIT_BIT, EXP_BITS);
v.state := IDIV_EXTDIV;
@ -2561,6 +2593,12 @@ begin
v.opsel_a := AIN_C;
v.state := IDIV_EXT_TBH;
end if;
when IDIV_SH32 =>
-- r.shift = 32, R contains the dividend
opsel_r <= RES_SHIFT;
v.shift := to_signed(-UNIT_BIT, EXP_BITS);
v.first := '1';
v.state := IDIV_DIV;
when IDIV_DIV =>
-- Dividing A by C, r.shift = -56; A is in R
-- Put A into the bottom 64 bits of Ahi/A/Alo
@ -2805,13 +2843,22 @@ begin
-- and also negate R if the answer is negative
opsel_ainv <= r.result_sign;
carry_in <= r.inc_quot xor r.result_sign;
rnd_b32 := '1';
if r.divmod = '0' then
opsel_b <= BIN_RND;
end if;
if r.is_signed = '0' then
v.state := IDIV_DONE;
else
v.state := IDIV_OVFCHK;
end if;
when IDIV_OVFCHK =>
v.int_ovf := r.r(63) xor r.result_sign;
if r.single_prec = '0' then
sign_bit := r.r(63);
else
sign_bit := r.r(31);
end if;
v.int_ovf := sign_bit xor r.result_sign;
if v.int_ovf = '1' then
v.state := IDIV_ZERO;
else
@ -2953,7 +3000,9 @@ begin
when BIN_R =>
in_b0 := r.r;
when BIN_RND =>
if rbit_inc = '0' then
if rnd_b32 = '1' then
round_inc := (32 => r.result_sign and r.single_prec, others => '0');
elsif rbit_inc = '0' then
round_inc := (SP_LSB => r.single_prec, DP_LSB => not r.single_prec, others => '0');
else
round_inc := (DP_RBIT => '1', others => '0');

Loading…
Cancel
Save