FPU: Implement fadd[s] and fsub[s] and add tests for them
authorPaul Mackerras <paulus@ozlabs.org>
Thu, 23 Jul 2020 07:56:15 +0000 (17:56 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Thu, 3 Sep 2020 07:45:03 +0000 (17:45 +1000)
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
decode1.vhdl
fpu.vhdl
tests/fpu/fpu.c
tests/test_fpu.bin
tests/test_fpu.console_out

index 34170dda8452bafc224c9d5684b0ffe63ba55c07..737d83cba33657fbe84403124d327b66ad8fe791 100644 (file)
@@ -58,6 +58,7 @@ architecture behaviour of decode1 is
     type op_59_subop_array_t is array(0 to 31) of decode_rom_t;
     type minor_rom_array_2_t is array(0 to 3) of decode_rom_t;
     type op_63_subop_array_0_t is array(0 to 511) of decode_rom_t;
+    type op_63_subop_array_1_t is array(0 to 16) of decode_rom_t;
 
     constant major_decode_rom_array : major_rom_array_t := (
         --          unit     internal      in1         in2          in3   out   CR   CR   inv  inv  cry   cry  ldst  BR   sgn  upd  rsrv 32b  sgn  rc    lk   sgl
@@ -415,6 +416,8 @@ architecture behaviour of decode1 is
         --             unit   internal       in1   in2   in3   out   CR   CR   inv  inv  cry   cry  ldst  BR   sgn  upd  rsrv 32b  sgn  rc    lk   sgl
         --                          op                               in   out   A   out  in    out  len        ext                                pipe
         2#01110#  =>  (FPU,   OP_FPOP_I,     NONE, FRB,  NONE, FRT,  '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '1', '0', RC,   '0', '0'), -- fcfid[u]s
+        2#10100#  =>  (FPU,   OP_FPOP,       FRA,  FRB,  NONE, FRT,  '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '1', '0', RC,   '0', '0'), -- fsubs
+        2#10101#  =>  (FPU,   OP_FPOP,       FRA,  FRB,  NONE, FRT,  '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '1', '0', RC,   '0', '0'), -- fadds
         others => illegal_inst
         );
 
@@ -461,6 +464,15 @@ architecture behaviour of decode1 is
         others => illegal_inst
         );
 
+    -- indexed by bits 4..1 of instruction word
+    constant decode_op_63h_array : op_63_subop_array_1_t := (
+        --            unit   internal       in1   in2   in3   out   CR   CR   inv  inv  cry   cry  ldst  BR   sgn  upd  rsrv 32b  sgn  rc    lk   sgl
+        --                         op                               in   out   A   out  in    out  len        ext                                pipe
+        2#0100#  =>  (FPU,   OP_FPOP,       FRA,  FRB,  NONE, FRT,  '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', RC,   '0', '0'), -- fsub
+        2#0101#  =>  (FPU,   OP_FPOP,       FRA,  FRB,  NONE, FRT,  '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', RC,   '0', '0'), -- fadd
+        others => illegal_inst
+        );
+
     --                                        unit   internal         in1         in2          in3   out   CR   CR   inv  inv  cry   cry  ldst  BR   sgn  upd  rsrv 32b  sgn  rc    lk   sgl
     --                                                     op                                              in   out   A   out  in    out  len        ext                                 pipe
     constant nop_instr      : decode_rom_t := (ALU,  OP_NOP,          NONE,       NONE,        NONE, NONE, '0', '0', '0', '0', ZERO, '0', NONE, '0', '0', '0', '0', '0', '0', NONE, '0', '0');
@@ -626,8 +638,11 @@ begin
         when 63 =>
             if HAS_FPU then
                 -- floating point operations, general and double-precision
-                v.decode := decode_op_63l_array(to_integer(unsigned(f_in.insn(4 downto 1) & f_in.insn(10 downto 6))));
-                vi.override := f_in.insn(5);
+                if f_in.insn(5) = '0' then
+                    v.decode := decode_op_63l_array(to_integer(unsigned(f_in.insn(4 downto 1) & f_in.insn(10 downto 6))));
+                else
+                    v.decode := decode_op_63h_array(to_integer(unsigned(f_in.insn(4 downto 1))));
+                end if;
             end if;
 
         when others =>
index e97461c9ae9402961993dd867aab356d170d93bc..e9edfb45f4c79949c45a422318bbc8459d97f79e 100644 (file)
--- a/fpu.vhdl
+++ b/fpu.vhdl
@@ -40,7 +40,9 @@ architecture behaviour of fpu is
                      DO_FMR, DO_FMRG,
                      DO_FCFID, DO_FCTI,
                      DO_FRSP, DO_FRI,
+                     DO_FADD,
                      FRI_1,
+                     ADD_SHIFT, ADD_2, ADD_3,
                      INT_SHIFT, INT_ROUND, INT_ISHIFT,
                      INT_FINAL, INT_CHECK, INT_OFLOW,
                      FINISH, NORMALIZE,
@@ -79,6 +81,9 @@ architecture behaviour of fpu is
         tiny         : std_ulogic;
         denorm       : std_ulogic;
         round_mode   : std_ulogic_vector(2 downto 0);
+        is_subtract  : std_ulogic;
+        exp_cmp      : std_ulogic;
+        add_bsmall   : std_ulogic;
     end record;
 
     signal r, rin : reg_type;
@@ -89,6 +94,7 @@ architecture behaviour of fpu is
     signal opsel_r       : std_ulogic_vector(1 downto 0);
     signal opsel_ainv    : std_ulogic;
     signal opsel_amask   : std_ulogic;
+    signal opsel_binv    : std_ulogic;
     signal in_a          : std_ulogic_vector(63 downto 0);
     signal in_b          : std_ulogic_vector(63 downto 0);
     signal result        : std_ulogic_vector(63 downto 0);
@@ -368,6 +374,9 @@ begin
         variable mshift      : signed(EXP_BITS-1 downto 0);
         variable need_check  : std_ulogic;
         variable msb         : std_ulogic;
+        variable is_add      : std_ulogic;
+        variable qnan_result : std_ulogic;
+        variable longmask    : std_ulogic;
     begin
         v := r;
         illegal := '0';
@@ -397,10 +406,16 @@ begin
             v.tiny := '0';
             v.denorm := '0';
             v.round_mode := '0' & r.fpscr(FPSCR_RN+1 downto FPSCR_RN);
+            v.is_subtract := '0';
+            v.add_bsmall := '0';
             adec := decode_dp(e_in.fra, int_input);
             bdec := decode_dp(e_in.frb, int_input);
             v.a := adec;
             v.b := bdec;
+            v.exp_cmp := '0';
+            if adec.exponent > bdec.exponent then
+                v.exp_cmp := '1';
+            end if;
         end if;
 
         r_hi_nz <= or (r.r(55 downto 31));
@@ -433,6 +448,7 @@ begin
         opsel_ainv <= '0';
         opsel_amask <= '0';
         opsel_b <= BIN_ZERO;
+        opsel_binv <= '0';
         opsel_r <= RES_SUM;
         carry_in <= '0';
         misc_sel <= "0000";
@@ -442,6 +458,8 @@ begin
         invalid := '0';
         renormalize := '0';
         set_x := '0';
+        qnan_result := '0';
+        longmask := r.single_prec;
 
         case r.state is
             when IDLE =>
@@ -483,6 +501,8 @@ begin
                         when "01111" =>
                             v.round_mode := "001";
                             v.state := DO_FCTI;
+                        when "10100" | "10101" =>
+                            v.state := DO_FADD;
                         when others =>
                             illegal := '1';
                     end case;
@@ -717,6 +737,117 @@ begin
                     v.state := FINISH;
                 end if;
 
+            when DO_FADD =>
+                -- fadd[s] and fsub[s]
+                opsel_a <= AIN_A;
+                v.result_sign := r.a.negative;
+                v.result_class := r.a.class;
+                v.result_exp := r.a.exponent;
+                v.fpscr(FPSCR_FR) := '0';
+                v.fpscr(FPSCR_FI) := '0';
+                is_add := r.a.negative xor r.b.negative xor r.insn(1);
+                if r.a.class = FINITE and r.b.class = FINITE then
+                    v.is_subtract := not is_add;
+                    v.add_bsmall := r.exp_cmp;
+                    if r.exp_cmp = '0' then
+                        v.shift := r.a.exponent - r.b.exponent;
+                        v.result_sign := r.b.negative xnor r.insn(1);
+                        if r.a.exponent = r.b.exponent then
+                            v.state := ADD_2;
+                        else
+                            v.state := ADD_SHIFT;
+                        end if;
+                    else
+                        opsel_a <= AIN_B;
+                        v.shift := r.b.exponent - r.a.exponent;
+                        v.result_exp := r.b.exponent;
+                        v.state := ADD_SHIFT;
+                    end if;
+                else
+                    if (r.a.class = NAN and r.a.mantissa(53) = '0') or
+                        (r.b.class = NAN and r.b.mantissa(53) = '0') then
+                        -- Signalling NAN
+                        v.fpscr(FPSCR_VXSNAN) := '1';
+                        invalid := '1';
+                    end if;
+                    if r.a.class = NAN then
+                        -- nothing to do, result is A
+                    elsif r.b.class = NAN then
+                        v.result_class := NAN;
+                        v.result_sign := r.b.negative;
+                        opsel_a <= AIN_B;
+                    elsif r.a.class = INFINITY and r.b.class = INFINITY and is_add = '0' then
+                        -- invalid operation, construct QNaN
+                        v.fpscr(FPSCR_VXISI) := '1';
+                        qnan_result := '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);
+                    elsif r.a.class = INFINITY or r.b.class = ZERO then
+                        -- nothing to do, result is A
+                    else
+                        -- result is +/- B
+                        v.result_sign := r.b.negative xnor r.insn(1);
+                        v.result_class := r.b.class;
+                        v.result_exp := r.b.exponent;
+                        opsel_a <= AIN_B;
+                    end if;
+                    arith_done := '1';
+                end if;
+
+            when ADD_SHIFT =>
+                opsel_r <= RES_SHIFT;
+                set_x := '1';
+                longmask := '0';
+                v.state := ADD_2;
+
+            when ADD_2 =>
+                if r.add_bsmall = '1' then
+                    opsel_a <= AIN_A;
+                else
+                    opsel_a <= AIN_B;
+                end if;
+                opsel_b <= BIN_R;
+                opsel_binv <= r.is_subtract;
+                carry_in <= r.is_subtract and not r.x;
+                v.shift := to_signed(-1, EXP_BITS);
+                v.state := ADD_3;
+
+            when ADD_3 =>
+                -- check for overflow or negative result (can't get both)
+                if r.r(63) = '1' then
+                    -- result is opposite sign to expected
+                    v.result_sign := not r.result_sign;
+                    opsel_ainv <= '1';
+                    carry_in <= '1';
+                    v.state := FINISH;
+                elsif r.r(55) = '1' then
+                    -- sum overflowed, shift right
+                    opsel_r <= RES_SHIFT;
+                    set_x := '1';
+                    v.shift := to_signed(-2, EXP_BITS);
+                    if exp_huge = '1' then
+                        v.state := ROUND_OFLOW;
+                    else
+                        v.state := ROUNDING;
+                    end if;
+                elsif r.r(54) = '1' then
+                    set_x := '1';
+                    v.shift := to_signed(-2, EXP_BITS);
+                    v.state := ROUNDING;
+                elsif (r_hi_nz or r_lo_nz or r.r(1) or r.r(0)) = '0' then
+                    -- r.x must be zero at this point
+                    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';
+                else
+                    renormalize := '1';
+                    v.state := NORMALIZE;
+                end if;
+
             when INT_SHIFT =>
                 opsel_r <= RES_SHIFT;
                 set_x := '1';
@@ -927,6 +1058,10 @@ begin
                 mant_nz := r_hi_nz or (r_lo_nz and not r.single_prec);
                 if mant_nz = '0' then
                     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';
                 else
                     -- Renormalize result after rounding
@@ -946,6 +1081,13 @@ begin
 
         end case;
 
+        if qnan_result = '1' then
+            invalid := '1';
+            v.result_class := NAN;
+            v.result_sign := '0';
+            misc_sel <= "0001";
+            opsel_r <= RES_MISC;
+        end if;
         if arith_done = '1' then
             -- Enabled invalid exception doesn't write result or FPRF
             if (invalid and r.fpscr(FPSCR_VE)) = '0' then
@@ -960,7 +1102,7 @@ begin
         -- Data path.
         -- This has A and B input multiplexers, an adder, a shifter,
         -- count-leading-zeroes logic, and a result mux.
-        if r.single_prec = '1' then
+        if longmask = '1' then
             mshift := r.shift + to_signed(-29, EXP_BITS);
         else
             mshift := r.shift;
@@ -1000,6 +1142,9 @@ begin
             when others =>
                 in_b0 := (others => '0');
         end case;
+        if opsel_binv = '1' then
+            in_b0 := not in_b0;
+        end if;
         in_b <= in_b0;
         if r.shift >= to_signed(-64, EXP_BITS) and r.shift <= to_signed(63, EXP_BITS) then
             shift_res := shifter_64(r.r & x"00000000000000",
@@ -1016,6 +1161,9 @@ begin
                 case misc_sel is
                     when "0000" =>
                         misc := x"00000000" & (r.fpscr and fpscr_mask);
+                    when "0001" =>
+                        -- generated QNaN mantissa
+                        misc := x"0020000000000000";
                     when "0010" =>
                         -- mantissa of max representable DP number
                         misc := x"007ffffffffffffc";
index e7a13347cb96c15c93fa466ae68851528e1a164a..8f7407a7b46996ee9b98742304b0167ab3794e1d 100644 (file)
@@ -843,6 +843,158 @@ int fpu_test_12(void)
        return trapit(0, test12);
 }
 
+struct addvals {
+       unsigned long val_a;
+       unsigned long val_b;
+       unsigned long sum;
+       unsigned long diff;
+} addvals[] = {
+       { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 },
+       { 0x8000000000000000, 0x8000000000000000, 0x8000000000000000, 0x0000000000000000 },
+       { 0x3fdfffffffffffff, 0x0000000000000000, 0x3fdfffffffffffff, 0x3fdfffffffffffff },
+       { 0x3ff0000000000000, 0x3ff0000000000000, 0x4000000000000000, 0x0000000000000000 },
+       { 0xbff0000000000000, 0xbff0000000000000, 0xc000000000000000, 0x0000000000000000 },
+       { 0x402123456789abcd, 0x4021000000000000, 0x403111a2b3c4d5e6, 0x3fb1a2b3c4d5e680 },
+       { 0x4061200000000000, 0x406123456789abcd, 0x407121a2b3c4d5e6, 0xbfba2b3c4d5e6800 },
+       { 0x4061230000000000, 0x3fa4560000000000, 0x4061244560000000, 0x406121baa0000000 },
+       { 0xc061230000000000, 0x3fa4560000000000, 0xc06121baa0000000, 0xc061244560000000 },
+       { 0x4061230000000000, 0xbfa4560000000000, 0x406121baa0000000, 0x4061244560000000 },
+       { 0xc061230000000000, 0xbfa4560000000000, 0xc061244560000000, 0xc06121baa0000000 },
+       { 0x3fa1230000000000, 0x4064560000000000, 0x4064571230000000, 0xc06454edd0000000 },
+       { 0xbfa1230000000000, 0x4064560000000000, 0x406454edd0000000, 0xc064571230000000 },
+       { 0x3fa1230000000000, 0xc064560000000000, 0xc06454edd0000000, 0x4064571230000000 },
+       { 0xbfa1230000000000, 0xc064560000000000, 0xc064571230000000, 0x406454edd0000000 },
+       { 0x6780000000000001, 0x6470000000000000, 0x6780000000000009, 0x677ffffffffffff2 },
+       { 0x6780000000000001, 0x6460000000000000, 0x6780000000000005, 0x677ffffffffffffa },
+       { 0x6780000000000001, 0x6450000000000000, 0x6780000000000003, 0x677ffffffffffffe },
+       { 0x6780000000000001, 0x6440000000000000, 0x6780000000000002, 0x6780000000000000 },
+       { 0x7ff8888888888888, 0x7ff9999999999999, 0x7ff8888888888888, 0x7ff8888888888888 },
+       { 0xfff8888888888888, 0x7ff9999999999999, 0xfff8888888888888, 0xfff8888888888888 },
+       { 0x7ff8888888888888, 0x7ff0000000000000, 0x7ff8888888888888, 0x7ff8888888888888 },
+       { 0x7ff8888888888888, 0x0000000000000000, 0x7ff8888888888888, 0x7ff8888888888888 },
+       { 0x7ff8888888888888, 0x0001111111111111, 0x7ff8888888888888, 0x7ff8888888888888 },
+       { 0x7ff8888888888888, 0x3ff0000000000000, 0x7ff8888888888888, 0x7ff8888888888888 },
+       { 0x7ff0000000000000, 0x7ff9999999999999, 0x7ff9999999999999, 0x7ff9999999999999 },
+       { 0x7ff0000000000000, 0x7ff0000000000000, 0x7ff0000000000000, 0x7ff8000000000000 },
+       { 0x7ff0000000000000, 0xfff0000000000000, 0x7ff8000000000000, 0x7ff0000000000000 },
+       { 0x7ff0000000000000, 0x0000000000000000, 0x7ff0000000000000, 0x7ff0000000000000 },
+       { 0x7ff0000000000000, 0x8000000000000000, 0x7ff0000000000000, 0x7ff0000000000000 },
+       { 0x7ff0000000000000, 0x8002222222222222, 0x7ff0000000000000, 0x7ff0000000000000 },
+       { 0x7ff0000000000000, 0xc002222222222222, 0x7ff0000000000000, 0x7ff0000000000000 },
+       { 0x0000000000000000, 0x7ff9999999999999, 0x7ff9999999999999, 0x7ff9999999999999 },
+       { 0x0000000000000000, 0x7ff0000000000000, 0x7ff0000000000000, 0xfff0000000000000 },
+       { 0x8000000000000000, 0x7ff0000000000000, 0x7ff0000000000000, 0xfff0000000000000 },
+       { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 },
+       { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 },
+       { 0x8000000000000000, 0x8000000000000000, 0x8000000000000000, 0x0000000000000000 },
+       { 0x8000000000000000, 0x8000000000000000, 0x8000000000000000, 0x0000000000000000 },
+       { 0x8002222222222222, 0x0001111111111111, 0x8001111111111111, 0x8003333333333333 },
+       { 0x0000022222222222, 0x0000111111111111, 0x0000133333333333, 0x80000eeeeeeeeeef },
+       { 0x401ffffffbfffefe, 0x406b8265196bd89e, 0x406c8265194bd896, 0xc06a8265198bd8a6 },
+       { 0x4030020000000004, 0xbf110001ffffffff, 0x403001fbbfff8004, 0x4030020440008004 },
+       { 0x3fdfffffffffffff, 0x3fe0000000000000, 0x3ff0000000000000, 0xbc90000000000000 },
+};
+
+int test13(long arg)
+{
+       long i;
+       unsigned long results[2];
+       struct addvals *vp = addvals;
+
+       set_fpscr(FPS_RN_NEAR);
+       for (i = 0; i < sizeof(addvals) / sizeof(addvals[0]); ++i, ++vp) {
+               asm("lfd 5,0(%0); lfd 6,8(%0); fadd 7,5,6; fsub 8,5,6; stfd 7,0(%1); stfd 8,8(%1)"
+                   : : "b" (&vp->val_a), "b" (results) : "memory");
+               if (results[0] != vp->sum || results[1] != vp->diff) {
+                       print_hex(i, 2, " ");
+                       print_hex(results[0], 16, " ");
+                       print_hex(results[1], 16, "\r\n");
+                       return i + 1;
+               }
+       }
+       return 0;
+}
+
+int fpu_test_13(void)
+{
+       enable_fp();
+       return trapit(0, test13);
+}
+
+struct addvals sp_addvals[] = {
+       { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 },
+       { 0x8000000000000000, 0x8000000000000000, 0x8000000000000000, 0x0000000000000000 },
+       { 0x3fdfffffffffffff, 0x0000000000000000, 0x3fe0000000000000, 0x3fe0000000000000 },
+       { 0x3ff0000000000000, 0x3ff0000000000000, 0x4000000000000000, 0x0000000000000000 },
+       { 0xbff0000000000000, 0xbff0000000000000, 0xc000000000000000, 0x0000000000000000 },
+       { 0x402123456789abcd, 0x4021000000000000, 0x403111a2c0000000, 0x3fb1a2b000000000 },
+       { 0x4061200000000000, 0x406123456789abcd, 0x407121a2c0000000, 0xbfba2b0000000000 },
+       { 0x4061230000000000, 0x3fa4560000000000, 0x4061244560000000, 0x406121baa0000000 },
+       { 0xc061230000000000, 0x3fa4560000000000, 0xc06121baa0000000, 0xc061244560000000 },
+       { 0x4061230000000000, 0xbfa4560000000000, 0x406121baa0000000, 0x4061244560000000 },
+       { 0xc061230000000000, 0xbfa4560000000000, 0xc061244560000000, 0xc06121baa0000000 },
+       { 0x3fa1230000000000, 0x4064560000000000, 0x4064571240000000, 0xc06454edc0000000 },
+       { 0xbfa1230000000000, 0x4064560000000000, 0x406454edc0000000, 0xc064571240000000 },
+       { 0x3fa1230000000000, 0xc064560000000000, 0xc06454edc0000000, 0x4064571240000000 },
+       { 0xbfa1230000000000, 0xc064560000000000, 0xc064571240000000, 0x406454edc0000000 },
+       { 0x6780000000000001, 0x6470000000000000, 0x7ff0000000000000, 0x7ff8000000000000 },
+       { 0x6780000000000001, 0x6460000000000000, 0x7ff0000000000000, 0x7ff8000000000000 },
+       { 0x6780000000000001, 0x6450000000000000, 0x7ff0000000000000, 0x7ff8000000000000 },
+       { 0x6780000000000001, 0x6440000000000000, 0x7ff0000000000000, 0x7ff8000000000000 },
+       { 0x7ff8888888888888, 0x7ff9999999999999, 0x7ff8888880000000, 0x7ff8888880000000 },
+       { 0xfff8888888888888, 0x7ff9999999999999, 0xfff8888880000000, 0xfff8888880000000 },
+       { 0x7ff8888888888888, 0x7ff0000000000000, 0x7ff8888880000000, 0x7ff8888880000000 },
+       { 0x7ff8888888888888, 0x0000000000000000, 0x7ff8888880000000, 0x7ff8888880000000 },
+       { 0x7ff8888888888888, 0x0001111111111111, 0x7ff8888880000000, 0x7ff8888880000000 },
+       { 0x7ff8888888888888, 0x3ff0000000000000, 0x7ff8888880000000, 0x7ff8888880000000 },
+       { 0x7ff0000000000000, 0x7ff9999999999999, 0x7ff9999980000000, 0x7ff9999980000000 },
+       { 0x7ff0000000000000, 0x7ff0000000000000, 0x7ff0000000000000, 0x7ff8000000000000 },
+       { 0x7ff0000000000000, 0xfff0000000000000, 0x7ff8000000000000, 0x7ff0000000000000 },
+       { 0x7ff0000000000000, 0x0000000000000000, 0x7ff0000000000000, 0x7ff0000000000000 },
+       { 0x7ff0000000000000, 0x8000000000000000, 0x7ff0000000000000, 0x7ff0000000000000 },
+       { 0x7ff0000000000000, 0x8002222222222222, 0x7ff0000000000000, 0x7ff0000000000000 },
+       { 0x7ff0000000000000, 0xc002222222222222, 0x7ff0000000000000, 0x7ff0000000000000 },
+       { 0x0000000000000000, 0x7ff9999999999999, 0x7ff9999980000000, 0x7ff9999980000000 },
+       { 0x0000000000000000, 0x7ff0000000000000, 0x7ff0000000000000, 0xfff0000000000000 },
+       { 0x8000000000000000, 0x7ff0000000000000, 0x7ff0000000000000, 0xfff0000000000000 },
+       { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 },
+       { 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 },
+       { 0x8000000000000000, 0x8000000000000000, 0x8000000000000000, 0x0000000000000000 },
+       { 0x8000000000000000, 0x8000000000000000, 0x8000000000000000, 0x0000000000000000 },
+       { 0x8002222222222222, 0x0001111111111111, 0x0000000000000000, 0x8000000000000000 },
+       { 0x0000022222222222, 0x0000111111111111, 0x0000000000000000, 0x0000000000000000 },
+       { 0x47dc000020000000, 0x47ec03ffe0000000, 0x7ff0000000000000, 0xc7dc07ffa0000000 },
+       { 0x47dbffffe0000000, 0x47eff7ffe0000000, 0x7ff0000000000000, 0xc7e1f80000000000 },
+       { 0x47efffffc0000000, 0xc7efffffc0000000, 0x0000000000000000, 0x7ff0000000000000 },
+};
+
+int test14(long arg)
+{
+       long i;
+       unsigned long results[2];
+       struct addvals *vp = sp_addvals;
+
+       set_fpscr(FPS_RN_NEAR);
+       for (i = 0; i < sizeof(sp_addvals) / sizeof(sp_addvals[0]); ++i, ++vp) {
+               asm("lfd 5,0(%0); frsp 5,5; lfd 6,8(%0); frsp 6,6; "
+                   "fadds 7,5,6; fsubs 8,5,6; stfd 7,0(%1); stfd 8,8(%1)"
+                   : : "b" (&vp->val_a), "b" (results) : "memory");
+               if (results[0] != vp->sum || results[1] != vp->diff) {
+                       print_hex(i, 2, " ");
+                       print_hex(results[0], 16, " ");
+                       print_hex(results[1], 16, "\r\n");
+                       return i + 1;
+               }
+       }
+       return 0;
+}
+
+int fpu_test_14(void)
+{
+       enable_fp();
+       return trapit(0, test14);
+}
+
 int fail = 0;
 
 void do_test(int num, int (*test)(void))
@@ -880,6 +1032,8 @@ int main(void)
        do_test(10, fpu_test_10);
        do_test(11, fpu_test_11);
        do_test(12, fpu_test_12);
+       do_test(13, fpu_test_13);
+       do_test(14, fpu_test_14);
 
        return fail;
 }
index 668ff65367cbf02294b638e77fd93f9173db9532..623db3f690b0aad69472a9a8740964239799983c 100755 (executable)
Binary files a/tests/test_fpu.bin and b/tests/test_fpu.bin differ
index d926abc9e5e039696b235a8a412a1aab941f30c2..440cd77cf854e440012ed590b16b74ef2ae35383 100644 (file)
@@ -10,3 +10,5 @@ test 09:PASS
 test 10:PASS\r
 test 11:PASS\r
 test 12:PASS\r
+test 13:PASS\r
+test 14:PASS\r