diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index 4b0b149032e7dc8e22f4cf8ba7ad3f818cdd1dfd..c6ca6f3c92c1c8661015917c304a81c7e0fe2461 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -2379,6 +2379,7 @@ PACKAGE BODY common_pkg IS -- overflow will never occur. FUNCTION s_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip, even : BOOLEAN) RETURN STD_LOGIC_VECTOR IS + -- # Round half away from zero when even = FALSE, else round half to even. -- # Round half to even algorithm: -- # vec: -3.5 -2.5 -1.5 -0.5 0.5 1.5 2.5 3.5 -- # floor(vec + 0.5) -4 -3 -2 -1 1 2 3 4 @@ -2390,12 +2391,11 @@ PACKAGE BODY common_pkg IS -- Use SIGNED 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_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_half : NATURAL := 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 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 : SIGNED(n-1 DOWNTO 0); + VARIABLE v_fraction : UNSIGNED(n-1 DOWNTO 0); VARIABLE v_out : SIGNED(c_out_w-1 DOWNTO 0); BEGIN v_in := SIGNED(vec); @@ -2403,13 +2403,17 @@ PACKAGE BODY common_pkg IS IF clip = TRUE AND v_in > c_max THEN v_out := c_clip; -- Round clip to maximum positive to avoid wrap to negative ELSE - IF vec(vec'HIGH)='0' THEN - v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half + 0, n), c_out_w); -- Round up for positive + IF even = FALSE THEN + -- Round half away + IF vec(vec'HIGH)='0' THEN + v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half + 0, n), c_out_w); -- Round half up for positive + ELSE + v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half - 1, n), c_out_w); -- Round half down for negative + END IF; ELSE - v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half - 1, n), c_out_w); -- Round down for negative - END IF; - IF even = TRUE THEN - v_fraction := SIGNED(vec(n-1 DOWNTO 0)); + -- 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; @@ -2433,6 +2437,7 @@ PACKAGE BODY common_pkg IS -- Unsigned numbers are round up (almost same as s_round, but without the else on negative vec) 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 @@ -2445,8 +2450,7 @@ PACKAGE BODY common_pkg IS -- 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_one : UNSIGNED(c_in_w-1 DOWNTO 0) := TO_UNSIGNED(1, c_in_w); - CONSTANT c_half : UNSIGNED(c_in_w-1 DOWNTO 0) := SHIFT_LEFT(c_one, n-1); -- = 2**(n-1) + CONSTANT c_half : NATURAL := 2**(n-1); CONSTANT c_max : UNSIGNED(c_in_w-1 DOWNTO 0) := UNSIGNED(c_slv1(c_in_w-1 DOWNTO 0)) - c_half; -- = 2**c_in_w-1 - c_half CONSTANT c_clip : UNSIGNED(c_out_w-1 DOWNTO 0) := UNSIGNED(c_slv1(c_out_w-1 DOWNTO 0)); -- = 2**c_out_w-1 VARIABLE v_in : UNSIGNED(c_in_w-1 DOWNTO 0); @@ -2458,8 +2462,12 @@ PACKAGE BODY common_pkg IS IF clip = TRUE AND v_in > c_max THEN v_out := c_clip; -- Round clip to +max to avoid wrap to 0 ELSE - v_out := RESIZE_NUM(SHIFT_RIGHT(v_in + c_half, n), c_out_w); -- Round half up using floor(vec + 0.5) - IF even = TRUE THEN + 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