diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index 63a769e7f4577b9a077a98b8cc3da1e6d18a9a02..58e8b511359f7c00e7788c636eff2713d2edf18c 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -2385,24 +2385,23 @@ PACKAGE BODY common_pkg IS -- # floor(vec + 0.5) -4 -3 -2 -1 1 2 3 4 -- # round even -4 -2 -2 0 0 2 2 4 -- # round even clip -4 -2 -2 0 0 2 2 3, clip to c_clip = 3 when c_out_w = 3 - -- v_out = floor(vec + 0.5) - -- if fraction = 0.5 and v_out = odd: - -- v_out -= 1 - -- Use SIGNED to avoid NATURAL (32 bit range) overflow error + -- Use SIGNED instead of NATURAL for c_half to avoid INTEGER (32 bit range) overflow error CONSTANT c_in_w : NATURAL := vec'LENGTH; CONSTANT c_out_w : NATURAL := vec'LENGTH - n; - CONSTANT c_half : NATURAL := 2**(n-1); - CONSTANT c_max : INTEGER := 2**(c_in_w-1)-1 - c_half; - CONSTANT c_clip : INTEGER := 2**(c_out_w-1)-1; - CONSTANT s_clip : SIGNED(c_out_w-1 DOWNTO 0) := TO_SIGNED(c_clip, c_out_w); + CONSTANT c_one : SIGNED(c_in_w-1 DOWNTO 0) := TO_SIGNED(1, c_in_w); + CONSTANT c_half : SIGNED(c_in_w-1 DOWNTO 0) := SHIFT_LEFT(c_one, n-1); -- = 2**(n-1) + CONSTANT c_max : SIGNED(c_in_w-1 DOWNTO 0) := SIGNED('0' & c_slv1(c_in_w-2 DOWNTO 0)) - c_half; -- = 2**(c_in_w-1)-1 - c_half + -- When c_out_w = 1, then c_clip = 0, because a 1 bit signed value is -1 or 0 + CONSTANT c_clip : SIGNED(c_out_w-1 DOWNTO 0) := SIGNED('0' & c_slv1(c_out_w-2 DOWNTO 0)); -- = 2**(c_out_w-1)-1 VARIABLE v_in : SIGNED(c_in_w-1 DOWNTO 0); - VARIABLE v_fraction : UNSIGNED(n-1 DOWNTO 0); + CONSTANT u_half : UNSIGNED(n-1 DOWNTO 0) := UNSIGNED(STD_LOGIC_VECTOR(c_half(n-1 DOWNTO 0))); -- convert to UNSIGNED to compare with u_fraction + VARIABLE u_fraction : UNSIGNED(n-1 DOWNTO 0); VARIABLE v_out : SIGNED(c_out_w-1 DOWNTO 0); BEGIN v_in := SIGNED(vec); IF n > 0 THEN IF clip = TRUE AND v_in > c_max THEN - v_out := s_clip; -- Round clip to maximum positive to avoid wrap to negative + v_out := c_clip; -- Round clip to maximum positive to avoid wrap to negative ELSE IF even = FALSE THEN -- Round half away @@ -2414,8 +2413,8 @@ PACKAGE BODY common_pkg IS ELSE -- Round half to even v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half, n), c_out_w); -- Round to nearest using floor(vec + 0.5) - v_fraction := UNSIGNED(vec(n-1 DOWNTO 0)); - IF v_fraction = c_half AND v_out(0) = '1' THEN -- Round half to even, so when odd subtract 1 + u_fraction := UNSIGNED(vec(n-1 DOWNTO 0)); + IF u_fraction = u_half AND v_out(0) = '1' THEN -- Round half to even, so when odd subtract 1 v_out := v_out - 1; -- to make v_out even END IF; END IF; @@ -2436,50 +2435,17 @@ PACKAGE BODY common_pkg IS RETURN s_round(vec, n, FALSE); -- no round clip END; - -- Unsigned numbers are round up (almost same as s_round, but without the else on negative vec) + -- for unsigned round half away and round half up are equivalent FUNCTION u_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip, even : BOOLEAN) RETURN STD_LOGIC_VECTOR IS - -- # Round half up when even = FALSE, else round half to even. - -- # Round half to even algorithm: - -- # vec: 0.5 1.5 2.5 3.5 4.5 5.5 6.5 7.5 - -- # floor(vec + 0.5) 1 2 3 4 5 6 7 8 - -- # round even 0 2 2 4 4 6 6 8 - -- # round even clip 0 2 2 4 4 6 6 7, clip to c_clip = 7 when c_out_w = 3 - -- v_out = floor(vec + 0.5) - -- if fraction = 0.5 and v_out = odd: - -- v_out -= 1 - -- - -- Use UNSIGNED to avoid NATURAL (32 bit range) overflow error CONSTANT c_in_w : NATURAL := vec'LENGTH; CONSTANT c_out_w : NATURAL := vec'LENGTH - n; - CONSTANT c_half : NATURAL := 2**(n-1); - CONSTANT c_max : NATURAL := 2**c_in_w-1 - c_half; - CONSTANT c_clip : NATURAL := 2**c_out_w-1; - CONSTANT u_clip : UNSIGNED(c_out_w-1 DOWNTO 0) := TO_UNSIGNED(c_clip, c_out_w); - VARIABLE v_in : UNSIGNED(c_in_w-1 DOWNTO 0); - VARIABLE v_fraction : UNSIGNED(n-1 DOWNTO 0); - VARIABLE v_out : UNSIGNED(c_out_w-1 DOWNTO 0); - BEGIN - v_in := UNSIGNED(vec); - IF n > 0 THEN - IF clip = TRUE AND v_in > c_max THEN - v_out := u_clip; -- Round clip to +max to avoid wrap to 0 - ELSE - IF even = FALSE THEN - -- Round half up - v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half, n), c_out_w); -- Round half up using floor(vec + 0.5) - ELSE - -- Round half to even - v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half, n), c_out_w); -- Round to nearest using floor(vec + 0.5) - v_fraction := UNSIGNED(vec(n-1 DOWNTO 0)); - IF v_fraction = c_half AND v_out(0) = '1' THEN -- Round half to even, so when odd subtract 1 - v_out := v_out - 1; -- to make v_out even - END IF; - END IF; - END IF; - ELSE - v_out := RESIZE_NUM(v_in, c_out_w); -- NOP - END IF; - RETURN STD_LOGIC_VECTOR(v_out); + VARIABLE in_vec : STD_LOGIC_VECTOR(c_in_w DOWNTO 0); + VARIABLE out_vec : STD_LOGIC_VECTOR(c_out_w DOWNTO 0); + BEGIN + -- Convert unsigned to positive signed and back to be able to reuse s_round() + in_vec := '0' & vec; + out_vec := s_round(in_vec, n, clip, even); + RETURN out_vec(c_out_w-1 DOWNTO 0); END; FUNCTION u_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR IS