From eecf1ca39919fac653beaf1127e16613d312f16e Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 24 Jan 2024 09:16:23 +1100 Subject: [PATCH] FPU: Fix setting of FPRF The sign recorded in FPRF was sometimes wrong because we weren't doing the modifications that were done in pack_dp when setting FPRF (FPSCR field). These modifications are: set sign for zero result of subtraction based on rounding mode; negate result for fnmadd/sub; but don't modify sign of NaNs. Instead we now do these modifications in the main state machine code and put the result in an 'rsign' variable that is used to set v.res_sign, then r.res_sign is used in the next cycle both for setting FPRF and in the pack_dp functions. That simplifies pack_dp and lets us get rid of r.res_negate, r.res_subtract and r.res_rmode. Signed-off-by: Paul Mackerras --- fpu.vhdl | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/fpu.vhdl b/fpu.vhdl index eaa4cf2..6fbc979 100644 --- a/fpu.vhdl +++ b/fpu.vhdl @@ -169,9 +169,7 @@ architecture behaviour of fpu is oe : std_ulogic; xerc : xer_common_t; xerc_result : xer_common_t; - res_negate : std_ulogic; - res_subtract : std_ulogic; - res_rmode : std_ulogic_vector(2 downto 0); + res_sign : std_ulogic; end record; type lookup_table is array(0 to 1023) of std_ulogic_vector(17 downto 0); @@ -609,20 +607,13 @@ architecture behaviour of fpu is -- Construct a DP floating-point result from components function pack_dp(negative: std_ulogic; class: fp_number_class; exp: signed(EXP_BITS-1 downto 0); - mantissa: std_ulogic_vector; single_prec: std_ulogic; quieten_nan: std_ulogic; - negate: std_ulogic; is_subtract: std_ulogic; round_mode: std_ulogic_vector) + mantissa: std_ulogic_vector; single_prec: std_ulogic; quieten_nan: std_ulogic) return std_ulogic_vector is variable dp_result : std_ulogic_vector(63 downto 0); - variable sign : std_ulogic; begin dp_result := (others => '0'); - sign := negative; case class is when ZERO => - if is_subtract = '1' then - -- set result sign depending on rounding mode - sign := round_mode(0) and round_mode(1); - end if; when FINITE => if mantissa(UNIT_BIT) = '1' then -- normalized number @@ -642,7 +633,7 @@ architecture behaviour of fpu is dp_result(28 downto 0) := mantissa(SP_LSB - 1 downto DP_LSB); end if; end case; - dp_result(63) := sign xor negate; + dp_result(63) := negative; return dp_result; end; @@ -860,6 +851,7 @@ begin variable opcbits : std_ulogic_vector(4 downto 0); variable int_result : std_ulogic; variable illegal : std_ulogic; + variable rsign : std_ulogic; begin v := r; v.complete := '0'; @@ -2590,7 +2582,6 @@ begin arith_done := '1'; when NAN_RESULT => - v.negate := '0'; if (r.use_a = '1' and r.a.class = NAN and r.a.mantissa(QNAN_BIT) = '0') or (r.use_b = '1' and r.b.class = NAN and r.b.mantissa(QNAN_BIT) = '0') or (r.use_c = '1' and r.c.class = NAN and r.c.mantissa(QNAN_BIT) = '0') then @@ -3158,14 +3149,14 @@ begin end case; + rsign := v.result_sign; if zero_divide = '1' then v.fpscr(FPSCR_ZX) := '1'; end if; if qnan_result = '1' then invalid := '1'; v.result_class := NAN; - v.result_sign := '0'; - v.negate := '0'; + rsign := '0'; misc_sel <= "0001"; opsel_r <= RES_MISC; arith_done := '1'; @@ -3181,6 +3172,12 @@ begin v.writing_fpr := '1'; v.update_fprf := '1'; end if; + if v.is_subtract = '1' and v.result_class = ZERO then + rsign := r.round_mode(0) and r.round_mode(1); + end if; + if v.negate = '1' and v.result_class /= NAN then + rsign := not rsign; + end if; v.instr_done := '1'; update_fx := '1'; end if; @@ -3516,7 +3513,7 @@ begin end if; if r.update_fprf = '1' then - v.fpscr(FPSCR_C downto FPSCR_FU) := result_flags(r.result_sign, r.result_class, + v.fpscr(FPSCR_C downto FPSCR_FU) := result_flags(r.res_sign, r.result_class, r.r(UNIT_BIT) and not r.denorm); end if; @@ -3541,9 +3538,7 @@ begin v.int_result := int_result; v.illegal := illegal; v.nsnan_result := v.quieten_nan; - v.res_negate := v.negate; - v.res_subtract := v.is_subtract; - v.res_rmode := r.round_mode; + v.res_sign := rsign; if r.integer_op = '1' then v.cr_mask := num_to_fxm(0); elsif r.is_cmp = '0' then @@ -3574,9 +3569,8 @@ begin if r.int_result = '1' then fp_result <= r.r; else - fp_result <= pack_dp(r.result_sign, r.result_class, r.result_exp, r.r, - r.sp_result, r.nsnan_result, - r.res_negate, r.res_subtract, r.res_rmode); + fp_result <= pack_dp(r.res_sign, r.result_class, r.result_exp, r.r, + r.sp_result, r.nsnan_result); end if; rin <= v;