FPU: Add logic for 32-bit integer division
authorPaul Mackerras <paulus@ozlabs.org>
Sat, 7 May 2022 12:34:23 +0000 (22:34 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Fri, 22 Jul 2022 12:20:25 +0000 (22:20 +1000)
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
fpu.vhdl

index 18d3a5ab1c70fe393645d1928f02794765a2fd4d..b8cea397139d78807fd23ea4228eb53595cb7183 100644 (file)
--- 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');