From 34330552e8f1d78c1dac1e7a154dcee6a991c74a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 7 May 2022 22:34:23 +1000 Subject: [PATCH] FPU: Add logic for 32-bit integer division Signed-off-by: Paul Mackerras --- fpu.vhdl | 77 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/fpu.vhdl b/fpu.vhdl index 18d3a5a..b8cea39 100644 --- a/fpu.vhdl +++ b/fpu.vhdl @@ -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');