diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index a74053a4cd479ae002693585e009314e5857ceda..4b0b149032e7dc8e22f4cf8ba7ad3f818cdd1dfd 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -467,8 +467,6 @@ PACKAGE common_pkg IS FUNCTION s_round( vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR; -- remove n LSBits from vec by rounding away from 0, so result has width vec'LENGTH-n, and clip to avoid wrap FUNCTION s_round( vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR; -- remove n LSBits from vec by rounding away from 0, so result has width vec'LENGTH-n FUNCTION s_round( vec : STD_LOGIC_VECTOR; n : NATURAL; clip, even : BOOLEAN) RETURN STD_LOGIC_VECTOR; -- idem but round half to even for signed - FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR; -- idem but round up to +infinity (s_round_up = u_round) - FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR; -- idem but round up to +infinity (s_round_up = u_round) FUNCTION u_round( vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR; -- idem round up for unsigned values FUNCTION u_round( vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR; -- idem round up for unsigned values FUNCTION u_round( vec : STD_LOGIC_VECTOR; n : NATURAL; clip, even : BOOLEAN) RETURN STD_LOGIC_VECTOR; -- idem but round half to even for unsigned @@ -2320,12 +2318,12 @@ PACKAGE BODY common_pkg IS ------------------------------------------------------------------------------------------------- -- -- From https://en.wikipedia.org/wiki/Rounding it follows that there are three main - -- catergories for rounding to integer: + -- categories for rounding to integer: -- -- 1) Direct rounding to integer : - -- . down : y = floor(x), + -- . down : y = floor(x), is truncate of LSbits -- . up : y = ceil(x), - -- . towards zero : y = truncate(x) + -- . towards zero : y = truncate(x) = int(x), is truncate of fraction, is keep the integer part -- = sgn(x) floor(|x|) = floor(x) when x >= 0, else ceil(x) -- . away from zero : y = sgn(x) ceil(|x|) = ceil(x) when x >= 0, else floor(x) -- 2) Rounding to nearest integer : @@ -2334,14 +2332,14 @@ PACKAGE BODY common_pkg IS -- . half towards zero : y = sgn(x) ceil(|x| - 0.5) = ceil(x - 0.5) when x >= 0, else floor(x + 0.5) -- . half away from zero : y = sgn(x) floor(|x| + 0.5) = floor(x + 0.5) when x >= 0, else ceil(x - 0.5) -- . round half to even : rounds to the nearest even integer when fraction = 0.5 else use - -- either floor(x + 0.5) or ceil(x - 0.5), because they are quivalent then. This avoid + -- either floor(x + 0.5) or ceil(x - 0.5), because they are equivalent then. This avoid -- DC bias and bias towards or away from zero. -- -- 3) Randomized rounding to an integer : round to nearest when fraction != 0.5, round up or -- down when fraction = 0.5. This avoid DC bias and bias towards or away from zero: -- . alternate tie breaking : alternately select round up or round down when fraction = 0.5 -- else either floor(x + 0.5) or ceil(x - 0.5), because they are equivalent then. - -- . random tie breaking : idem as alternate, but use random selection. + -- . random tie breaking : idem as alternate tie breaking, but use random selection. -- . stochastic rounding : round up or down with a probability that depends on proximity. -- This avoids DC bias when the input is not random, e.g. when the input has a constant -- fraction > 0. For DSP with ADC related data with sufficient dynamic range this does @@ -2354,21 +2352,23 @@ PACKAGE BODY common_pkg IS -- a function for round using tie breaking requires an external state to manage the -- selection. -- - -- * u_round() and s_round_up() use half up. This introduces +DC bias when fraction 0.5 - -- occurs. - -- * s_round() uses half away from zero, similar as by round() in Matlab, Python, TCL. - -- This avoid DC bias, but does introduce bias away from zero, which can show as a up - -- bias in power values because (negative)**2 > 0 and (positive)**2 > 0. + -- * Half up introduces +DC bias when fraction 0.5 occurs. + -- * Half away from zero is also used by round() in VHDL math_real, Matlab, Python, TCL. + -- * Half away avoids DC bias, but does introduce bias away from zero, which can show + -- as a up bias in power values because (negative)**2 > 0 and (positive)**2 > 0. -- - -- Functions s_round(), s_round_up() and u_round(): + -- Functions s_round() and u_round(): -- + -- . u_round() supports half up (= half away) and half even. + -- . s_round() supports half away and half even. -- . The returned output width is input width - n. -- . If n=0 then the return value is the same as the input value so only -- wires (NOP, no operation). -- . Both have the same implementation but different c_max and c_clip values. -- . Round up for unsigned so +2.5 becomes 3 - -- . Round away from zero for signed so round up for positive and round down for negative, so +2.5 becomes 3 and -2.5 becomes -3. - -- . Round away from zero is also used by round() in Matlab, Python, TCL + -- . Round away from zero for signed so round up for positive and round down for negative, + -- so +2.5 becomes 3 and -2.5 becomes -3. + -- . Round away from zero is also used by round() in VHDL math_real, Matlab, Python, TCL -- . Rounding up implies adding 0.5 and then truncation, use clip = TRUE to -- clip the potential overflow due to adding 0.5 to +max. -- . For negative values overflow due to rounding can not occur, because c_half-1 >= 0 for n>0 @@ -2388,14 +2388,15 @@ PACKAGE BODY common_pkg IS -- if fraction = 0.5 and v_out = odd: -- v_out -= 1 -- 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_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_out : SIGNED(c_out_w-1 DOWNTO 0); + 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_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_out : SIGNED(c_out_w-1 DOWNTO 0); BEGIN v_in := SIGNED(vec); IF n > 0 THEN @@ -2407,6 +2408,12 @@ PACKAGE BODY common_pkg IS 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)); + 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 @@ -2414,7 +2421,7 @@ PACKAGE BODY common_pkg IS RETURN STD_LOGIC_VECTOR(v_out); END; - FUNCTION s_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip, even : BOOLEAN) RETURN STD_LOGIC_VECTOR IS + FUNCTION s_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR IS BEGIN RETURN s_round(vec, n, clip, FALSE); -- no round half to even END; @@ -2424,17 +2431,6 @@ PACKAGE BODY common_pkg IS RETURN s_round(vec, n, FALSE); -- no round clip END; - -- An alternative is to always round up, also for negative numbers (i.e. s_round_up = u_round). - FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR IS - BEGIN - RETURN u_round(vec, n, clip); - END; - - FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR IS - BEGIN - RETURN u_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) FUNCTION u_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip, even : BOOLEAN) RETURN STD_LOGIC_VECTOR IS -- # Round half to even algorithm: