From 41988e3b5ffdef3973ae42e1b0d1400dc26ff58d Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 10 Dec 2025 08:37:02 +1100 Subject: [PATCH] FPU: Fix comparison of remainder in square root code The square root procedure needs to compare B - R^2 with 2R + 1 to decide whether to increment the square root estimate R by 1. It currently does this by putting 2R + 1 in B and using the pcmpb_lt and pcmpb_eq signals. This is not correct because the comparisons that generate those signals have a 2-bit shift embedded into them. Instead, put 2R + 1 into C and use pcmpc_lt/eq, which don't have the 2-bit shift. Signed-off-by: Paul Mackerras --- fpu.vhdl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fpu.vhdl b/fpu.vhdl index 5227406..b97e768 100644 --- a/fpu.vhdl +++ b/fpu.vhdl @@ -1303,6 +1303,9 @@ begin end if; -- Compare P with zero and with B + -- This has a 2-bit shift in it (p(59..4) compared to b(57..2)) + -- because it's used in the FP division code to determine whether + -- to increment the quotient at bit 2 (DP_RBIT). px_nz := or (r.p(UNIT_BIT + 1 downto 4)); pcmpb_eq := '0'; if r.p(59 downto 4) = r.b.mantissa(UNIT_BIT + 1 downto DP_RBIT) then @@ -1314,6 +1317,9 @@ begin elsif unsigned(r.p(59 downto 4)) < unsigned(r.b.mantissa(UNIT_BIT + 1 downto DP_RBIT)) then pcmpb_lt := '1'; end if; + -- Compare P with zero and with C + -- This is used in the square root and integer division code + -- to decide whether to increment the result by 1 pcmpc_eq := '0'; if r.p = r.c.mantissa then pcmpc_eq := '1'; @@ -2271,29 +2277,29 @@ begin when SQRT_11 => -- compute P = A - R * R (remainder) - -- also put 2 * R + 1 into B for comparison with P + -- also put 2 * R + 1 into C for comparison with P msel_1 <= MUL1_R; msel_2 <= MUL2_R; msel_add <= MULADD_A; msel_inv <= '1'; f_to_multiply.valid <= r.first; shiftin := '1'; - set_b := r.first; + set_c := r.first; if multiply_to_f.valid = '1' then v.state := SQRT_12; end if; when SQRT_12 => - -- test if remainder is 0 or >= B = 2*R + 1 + -- test if remainder is 0 or >= C = 2*R + 1 set_r := '0'; opsel_c <= CIN_INC; - if pcmpb_lt = '1' then + if pcmpc_lt = '1' then -- square root is correct, set X if remainder non-zero v.x := r.p(UNIT_BIT + 2) or px_nz; else -- square root needs to be incremented by 1 set_r := '1'; - v.x := not pcmpb_eq; + v.x := not pcmpc_eq; end if; v.state := FINISH;