Skip to content
Snippets Groups Projects
Commit 8d120bde authored by Eric Kooistra's avatar Eric Kooistra
Browse files

Use SIGNED instead of NATURAL for c_half. Now use s_round() to implement...

Use SIGNED instead of NATURAL for c_half. Now use s_round() to implement u_round() to have cleaner code.
parent 6e612423
No related branches found
No related tags found
1 merge request!168Add support for round half to even via g_round_even. Not yet fully tested....
......@@ -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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment