From 3268ef717cfbc38290b0b49be22cb6679e378fb9 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 21 Mar 2025 21:41:39 +1100 Subject: [PATCH] FPU: Make opsel_a a function of just the state This adds some extra states and transitions so that opsel_a becomes a function only of the current state. Signed-off-by: Paul Mackerras --- fpu.vhdl | 89 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/fpu.vhdl b/fpu.vhdl index 4ef2d14..5648012 100644 --- a/fpu.vhdl +++ b/fpu.vhdl @@ -57,7 +57,7 @@ architecture behaviour of fpu is DO_FSEL, DO_IDIVMOD, FRI_1, - ADD_1, ADD_SHIFT, ADD_2, ADD_3, + ADD_1, ADD_SHIFT, ADD_2, ADD_2B, ADD_3, CMP_1, CMP_2, MULT_1, FMADD_0, FMADD_1, FMADD_2, FMADD_3, @@ -73,7 +73,7 @@ architecture behaviour of fpu is INT_FINAL, INT_CHECK, INT_OFLOW, FINISH, NORMALIZE, ROUND_UFLOW, ROUND_OFLOW, - ROUNDING, ROUNDING_2, ROUNDING_3, + ROUNDING, ROUND_INC, ROUNDING_2, ROUNDING_3, DENORM, RENORM_A, RENORM_B, RENORM_C, RENORM_1, RENORM_2, @@ -87,7 +87,8 @@ architecture behaviour of fpu is IDIV_EXT_TBH4, IDIV_EXT_TBH5, IDIV_EXTDIV, IDIV_EXTDIV1, IDIV_EXTDIV2, IDIV_EXTDIV3, IDIV_EXTDIV4, IDIV_EXTDIV5, IDIV_EXTDIV6, - IDIV_MODADJ, IDIV_MODSUB, IDIV_DIVADJ, IDIV_OVFCHK, IDIV_DONE, IDIV_ZERO); + IDIV_MODADJ, IDIV_MODADJ_NEG, IDIV_MODSUB, + IDIV_DIVADJ, IDIV_OVFCHK, IDIV_DONE, IDIV_ZERO); type decode32 is array(0 to 31) of state_t; type decode8 is array(0 to 7) of state_t; @@ -1027,7 +1028,6 @@ begin variable mulexp : signed(EXP_BITS-1 downto 0); variable maddend : std_ulogic_vector(127 downto 0); variable sum : std_ulogic_vector(63 downto 0); - variable round_inc : std_ulogic_vector(63 downto 0); variable mult_mask : std_ulogic; variable sign_bit : std_ulogic; variable rexp_in1 : signed(EXP_BITS-1 downto 0); @@ -1678,7 +1678,7 @@ begin rs_sel2 <= RSH2_A; v.add_bsmall := '0'; if r.a.exponent = r.b.exponent then - v.state := ADD_2; + v.state := ADD_2B; elsif r.a.exponent < r.b.exponent then v.longmask := '0'; v.state := ADD_SHIFT; @@ -1841,14 +1841,24 @@ begin v.x := s_nz; set_x := '1'; v.longmask := r.single_prec; - v.state := ADD_2; - - when ADD_2 => if r.add_bsmall = '1' then - opsel_a <= AIN_A; + v.state := ADD_2; else - opsel_a <= AIN_B; + v.state := ADD_2B; end if; + + when ADD_2 => + opsel_a <= AIN_A; + opsel_b <= BIN_ADDSUBR; + opsel_c <= CIN_SUBEXT; + set_r := '1'; + -- set shift to -1 + rs_con2 <= RSCON2_1; + rs_neg2 <= '1'; + v.state := ADD_3; + + when ADD_2B => + opsel_a <= AIN_B; opsel_b <= BIN_ADDSUBR; opsel_c <= CIN_SUBEXT; set_r := '1'; @@ -2484,20 +2494,14 @@ begin v.fpscr(FPSCR_FR downto FPSCR_FI) := round; if round(1) = '1' then -- increment the LSB for the precision - opsel_a <= AIN_RND; - -- set shift to -1 - rs_con2 <= RSCON2_1; - rs_neg2 <= '1'; - v.state := ROUNDING_2; + v.state := ROUND_INC; + elsif r.r(UNIT_BIT) = '0' then + -- result after masking could be zero, or could be a + -- denormalized result that needs to be renormalized + rs_norm <= '1'; + v.state := ROUNDING_3; else - if r.r(UNIT_BIT) = '0' then - -- result after masking could be zero, or could be a - -- denormalized result that needs to be renormalized - rs_norm <= '1'; - v.state := ROUNDING_3; - else - arith_done := '1'; - end if; + arith_done := '1'; end if; if round(0) = '1' then v.fpscr(FPSCR_XX) := '1'; @@ -2506,6 +2510,14 @@ begin end if; end if; + when ROUND_INC => + set_r := '1'; + opsel_a <= AIN_RND; + -- set shift to -1 + rs_con2 <= RSCON2_1; + rs_neg2 <= '1'; + v.state := ROUNDING_2; + when ROUNDING_2 => -- Check for overflow during rounding -- r.shift = -1 @@ -2804,12 +2816,10 @@ begin msel_1 <= MUL1_Y; msel_2 <= MUL2_P; v.inc_quot := not pcmpc_lt and not r.divmod; - if r.divmod = '0' then - -- get B into R for IDIV_DIVADJ state - opsel_a <= AIN_B; - opsel_b <= BIN_ZERO; - set_r := '1'; - end if; + -- if dividing, get B into R for IDIV_DIVADJ state + opsel_a <= AIN_B; + opsel_b <= BIN_ZERO; + set_r := not r.divmod; -- set shift to UNIT_BIT (== 56) rs_con2 <= RSCON2_UNIT; if pcmpc_lt = '1' or pcmpc_eq = '1' then @@ -2872,11 +2882,11 @@ begin v.inc_quot := not pcmpc_lt and not r.divmod; -- set shift to UNIT_BIT (== 56) rs_con2 <= RSCON2_UNIT; + -- if dividing, get B into R for IDIV_DIVADJ state + opsel_a <= AIN_B; + opsel_b <= BIN_ZERO; + set_r := not r.divmod; if r.divmod = '0' then - -- get B into R for IDIV_DIVADJ state - opsel_a <= AIN_B; - opsel_b <= BIN_ZERO; - set_r := '1'; v.state := IDIV_DIVADJ; elsif pcmpc_eq = '1' then v.state := IDIV_ZERO; @@ -3026,8 +3036,15 @@ begin elsif r.result_sign = '0' then v.state := IDIV_DONE; else - v.state := IDIV_DIVADJ; + v.state := IDIV_MODADJ_NEG; end if; + when IDIV_MODADJ_NEG => + -- result (so far) is in R + -- set carry to increment quotient if needed + -- and also negate R since the answer is negative + opsel_b <= BIN_MINUSR; + set_r := '1'; + v.state := IDIV_OVFCHK; when IDIV_MODSUB => -- Subtract divisor from remainder opsel_a <= AIN_C; @@ -3043,12 +3060,10 @@ begin -- result (so far) is in R -- set carry to increment quotient if needed -- and also negate R if the answer is negative + opsel_a <= AIN_RND_B32; opsel_b <= BIN_RSIGNR; opsel_c <= CIN_RNDQ; set_r := '1'; - if r.divmod = '0' then - opsel_a <= AIN_RND_B32; - end if; if r.is_signed = '0' then v.state := IDIV_DONE; else