diff --git a/libraries/base/common/hdllib.cfg b/libraries/base/common/hdllib.cfg index 507266e0ecca3abf8a42762d4ac1ba85ce4a7258..6a8deef3b0251bc4dc8d5a8401c9b93e470c8ff9 100644 --- a/libraries/base/common/hdllib.cfg +++ b/libraries/base/common/hdllib.cfg @@ -197,6 +197,7 @@ test_bench_files = tb/vhdl/tb_mms_common_variable_delay.vhd tb/vhdl/tb_tb_resize.vhd + tb/vhdl/tb_tb_round.vhd tb/vhdl/tb_tb_common_add_sub.vhd tb/vhdl/tb_tb_common_adder_tree.vhd tb/vhdl/tb_tb_common_fanout_tree.vhd @@ -220,7 +221,7 @@ regression_test_vhdl = tb/vhdl/tb_common_transpose_symbol.vhd tb/vhdl/tb_common_variable_delay.vhd tb/vhdl/tb_tb_resize.vhd - #tb/vhdl/tb_round.vhd -- has no self verification yet + tb/vhdl/tb_tb_round.vhd tb/vhdl/tb_requantize.vhd tb/vhdl/tb_common_to_sreal.vhd tb/vhdl/tb_common_pulse_delay.vhd diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index cf03d8a7b2723ad19b51505269d471b2eb01b8c4..58e8b511359f7c00e7788c636eff2713d2edf18c 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -464,12 +464,12 @@ PACKAGE common_pkg IS FUNCTION truncate_or_resize_uvec( vec : STD_LOGIC_VECTOR; b : BOOLEAN; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- when b=TRUE then truncate to width w, else resize to width w FUNCTION truncate_or_resize_svec( vec : STD_LOGIC_VECTOR; b : BOOLEAN; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- idem for signed values - 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_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 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 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 FUNCTION u_to_s(u : NATURAL; w : NATURAL) RETURN INTEGER; -- interpret w bit unsigned u as w bit signed, and remove any MSbits FUNCTION s_to_u(s : INTEGER; w : NATURAL) RETURN NATURAL; -- interpret w bit signed s as w bit unsigned, and remove any MSbits @@ -2313,16 +2313,62 @@ PACKAGE BODY common_pkg IS RETURN v_res; END; - - -- Functions s_round, s_round_up and u_round: + ------------------------------------------------------------------------------------------------- + -- Rounding schemes: + ------------------------------------------------------------------------------------------------- + -- + -- From https://en.wikipedia.org/wiki/Rounding it follows that there are three main + -- categories for rounding to integer: + -- + -- 1) Direct rounding to integer : + -- . down : y = floor(x), is truncate of LSbits + -- . up : y = ceil(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 : + -- . half down : y = ceil(x - 0.5), + -- . half up : y = floor(x + 0.5), + -- . 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 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 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 + -- not occur. + -- + -- The advantage of round half to even, over round with tie breaking is, that for tie + -- breaking an external signal is needed to hold the alternate or random selector state, + -- whereas for round half to even the current input signal itself determines the + -- selection. Hence round half to even can be fully implemented in a function, whereas + -- a function for round using tie breaking requires an external state to manage the + -- selection. + -- + -- * 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() 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 @@ -2332,73 +2378,79 @@ PACKAGE BODY common_pkg IS -- maximum product is -8*-8=+64 <= 127-8, so wrapping due to rounding -- overflow will never occur. - FUNCTION s_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR 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_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); + 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 + -- # 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 + -- 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_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); + 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 := c_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 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 + -- 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) + 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; END IF; ELSE - v_out := RESIZE_NUM(v_in, c_out_w); -- NOP + v_out := RESIZE_NUM(v_in, c_out_w); -- NOP END IF; RETURN STD_LOGIC_VECTOR(v_out); END; - FUNCTION s_round(vec : STD_LOGIC_VECTOR; n : NATURAL) 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, FALSE); -- no round clip + RETURN s_round(vec, n, clip, FALSE); -- no round half to even 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 + + FUNCTION s_round(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR IS BEGIN - RETURN u_round(vec, n, clip); + RETURN s_round(vec, n, FALSE); -- no round clip END; - FUNCTION s_round_up(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR IS + -- 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 + CONSTANT c_in_w : NATURAL := vec'LENGTH; + CONSTANT c_out_w : NATURAL := vec'LENGTH - n; + VARIABLE in_vec : STD_LOGIC_VECTOR(c_in_w DOWNTO 0); + VARIABLE out_vec : STD_LOGIC_VECTOR(c_out_w DOWNTO 0); BEGIN - RETURN u_round(vec, n, FALSE); -- no round clip + -- 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; - - -- 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 : BOOLEAN ) RETURN STD_LOGIC_VECTOR 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_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); - VARIABLE v_out : UNSIGNED(c_out_w-1 DOWNTO 0); + + FUNCTION u_round(vec : STD_LOGIC_VECTOR; n : NATURAL; clip : BOOLEAN) RETURN STD_LOGIC_VECTOR IS BEGIN - v_in := UNSIGNED(vec); - IF n > 0 THEN - 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 up - END IF; - ELSE - v_out := RESIZE_NUM(v_in, c_out_w); -- NOP - END IF; - RETURN STD_LOGIC_VECTOR(v_out); + RETURN u_round(vec, n, clip, FALSE); -- no round half to even END; FUNCTION u_round(vec : STD_LOGIC_VECTOR; n : NATURAL) RETURN STD_LOGIC_VECTOR IS diff --git a/libraries/base/common/src/vhdl/common_requantize.vhd b/libraries/base/common/src/vhdl/common_requantize.vhd index 06f6836d6792ddd01a48feaa9607f51346dcfa22..cd704a27386792c8c272453232a1d58e996f5ce3 100644 --- a/libraries/base/common/src/vhdl/common_requantize.vhd +++ b/libraries/base/common/src/vhdl/common_requantize.vhd @@ -57,9 +57,10 @@ ENTITY common_requantize IS g_lsb_w : INTEGER := 4; -- when > 0, number of LSbits to remove from in_dat -- when < 0, number of LSBits to insert as a gain before resize to out_dat'LENGTH -- when 0 then no effect - g_lsb_round : BOOLEAN := TRUE; -- when true ROUND else TRUNCATE the input LSbits - g_lsb_round_clip : BOOLEAN := FALSE; -- when true round clip to +max to avoid wrapping to output -min (signed) or 0 (unsigned) due to rounding - g_msb_clip : BOOLEAN := TRUE; -- when true CLIP else WRAP the input MSbits + g_lsb_round : BOOLEAN := TRUE; -- when TRUE round else truncate the input LSbits + g_lsb_round_clip : BOOLEAN := FALSE; -- when TRUE round clip to +max to avoid wrapping to output -min (signed) or 0 (unsigned) due to rounding + g_lsb_round_even : BOOLEAN := FALSE; -- when TRUE round to even, else round away from zero + g_msb_clip : BOOLEAN := TRUE; -- when TRUE CLIP else WRAP the input MSbits g_msb_clip_symmetric : BOOLEAN := FALSE; -- when TRUE clip signed symmetric to +c_smax and -c_smax, else to +c_smax and c_smin_symm -- for wrapping when g_msb_clip=FALSE the g_msb_clip_symmetric is ignored, so signed wrapping is done asymmetric g_gain_w : NATURAL := 0; -- do not use, must be 0, use negative g_lsb_w instead @@ -101,6 +102,7 @@ BEGIN g_representation => g_representation, g_round => g_lsb_round, g_round_clip => g_lsb_round_clip, + g_round_even => g_lsb_round_even, g_pipeline_input => 0, g_pipeline_output => g_pipeline_remove_lsb, g_in_dat_w => g_in_dat_w, diff --git a/libraries/base/common/src/vhdl/common_round.vhd b/libraries/base/common/src/vhdl/common_round.vhd index 60e57c440b271976ebefd5241621c91c279ea651..65ccff8133905b359036ed56b3a98c50060579c1 100644 --- a/libraries/base/common/src/vhdl/common_round.vhd +++ b/libraries/base/common/src/vhdl/common_round.vhd @@ -36,12 +36,14 @@ ENTITY common_round IS -- -- If the input comes from a product and is rounded to the input width then g_round_clip can safely be FALSE, because e.g. for unsigned -- 4b*4b=8b->4b the maximum product is 15*15=225 <= 255-8, so wrapping will never occur. + -- When g_round = FALSE then truncate (= remove) the LSbits and then g_round_clip and g_round_even are dont care. -- GENERIC ( g_representation : STRING := "SIGNED"; -- SIGNED (round +-0.5 away from zero to +- infinity) or UNSIGNED rounding (round 0.5 up to + inifinity) g_round : BOOLEAN := TRUE; -- when TRUE round the input, else truncate the input g_round_clip : BOOLEAN := FALSE; -- when TRUE clip rounded input >= +max to avoid wrapping to output -min (signed) or 0 (unsigned) + g_round_even : BOOLEAN := FALSE; -- when TRUE round to even, else round away from zero g_pipeline_input : NATURAL := 0; -- >= 0 g_pipeline_output : NATURAL := 1; -- >= 0, use g_pipeline_input=0 and g_pipeline_output=0 for combinatorial output g_in_dat_w : NATURAL := 36; @@ -90,10 +92,10 @@ BEGIN -- Decrease to out_dat width by c_remove_w number of LSbits -- . rounding gen_s : IF c_remove_w>0 AND g_round=TRUE AND g_representation="SIGNED" GENERATE - res_dat <= s_round(reg_dat, c_remove_w, g_round_clip); + res_dat <= s_round(reg_dat, c_remove_w, g_round_clip, g_round_even); END GENERATE; gen_u : IF c_remove_w>0 AND g_round=TRUE AND g_representation="UNSIGNED" GENERATE - res_dat <= u_round(reg_dat, c_remove_w, g_round_clip); + res_dat <= u_round(reg_dat, c_remove_w, g_round_clip, g_round_even); END GENERATE; -- . truncating gen_t : IF c_remove_w>0 AND g_round=FALSE GENERATE diff --git a/libraries/base/common/tb/vhdl/tb_resize.vhd b/libraries/base/common/tb/vhdl/tb_resize.vhd index cc7a8044e92be04fddd7b4c433025a100b1026d9..d791044e3125e5c4f13239186837d98f4092c43a 100644 --- a/libraries/base/common/tb/vhdl/tb_resize.vhd +++ b/libraries/base/common/tb/vhdl/tb_resize.vhd @@ -19,11 +19,7 @@ -- ------------------------------------------------------------------------------- -LIBRARY IEEE; -USE IEEE.STD_LOGIC_1164.ALL; -USE IEEE.NUMERIC_STD.ALL; -USE work.common_pkg.ALL; - +-- Author: E. Kooistra 2009, updated in 2021 -- Purpose: Test bench for common_resize.vhd and for RESIZE_NUM() from -- common_pkg.vhd -- Usage: @@ -37,6 +33,11 @@ USE work.common_pkg.ALL; -- . Observe reg_dat with respect to out_sdat, out_sovr for signed -- . Observe reg_dat with respect to out_udat, out_uovr for unsigned +LIBRARY IEEE; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; +USE work.common_pkg.ALL; + ENTITY tb_resize IS GENERIC ( -- Supported for RESIZE_NUM() and common_resize.vhd diff --git a/libraries/base/common/tb/vhdl/tb_round.vhd b/libraries/base/common/tb/vhdl/tb_round.vhd index 65ca3d57e96bb25b01a5303ac3716af10c03591a..68d5cd7b2694a5068fc50849b5d92604575db26d 100644 --- a/libraries/base/common/tb/vhdl/tb_round.vhd +++ b/libraries/base/common/tb/vhdl/tb_round.vhd @@ -19,186 +19,613 @@ -- ------------------------------------------------------------------------------- +-- Author: E. Kooistra 2009, updated in 2021 using common_round_tb.py +-- Purpose: Test bench for common_round.vhd +-- Description: +-- The signal names in this VHDL tb are the same as in the document [1] and +-- in the Python tb [2]. +-- The verification uses expected results that were obtained and copied +-- from [2]. The tb_tb_round.vhd tries different settings for w and r. +-- +-- References: +-- [1] https://support.astron.nl/confluence/display/L2M/L4+SDPFW+Decision%3A+Number+representation%2C+resizing+and+rounding +-- [2] common_round_tb.py +-- Usage: +-- > as 5 +-- and manually set the signed data output signals to radix decimal +-- > run -a + LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.NUMERIC_STD.ALL; USE work.common_pkg.ALL; --- Purpose: Test bench for common_round.vhd --- Usage: --- > do wave_round.do --- > run 500 ns --- . Observe reg_dat with respect to out_sdat_no_rc for signed round without clipping of rounding overflow --- . Observe reg_dat with respect to out_sdat_with_rc for signed round with clipping of rounding overflow --- . Observe reg_dat with respect to out_udat for unsigned round --- . Observe reg_dat with respect to out_tdat for truncation - ENTITY tb_round IS + GENERIC ( + g_in_dat_w : NATURAL := 4; -- w = g_in_dat_w, in [2] + g_out_dat_w : NATURAL := 1 -- r = g_in_dat_w - g_out_dat_w, in [2] + ); END tb_round; ARCHITECTURE tb OF tb_round IS CONSTANT clk_period : TIME := 10 ns; - CONSTANT c_pipeline_input : NATURAL := 0; - CONSTANT c_pipeline_output : NATURAL := 0; - CONSTANT c_pipeline : NATURAL := c_pipeline_input + c_pipeline_output; - - --CONSTANT c_round_clip : BOOLEAN := TRUE; - CONSTANT c_round_clip : BOOLEAN := FALSE; - CONSTANT c_in_dat_w : NATURAL := 5; - CONSTANT c_out_dat_w : NATURAL := 3; - CONSTANT c_round_w : NATURAL := c_in_dat_w - c_out_dat_w; + CONSTANT c_pipeline_input : NATURAL := 0; + CONSTANT c_pipeline_output : NATURAL := 0; + CONSTANT c_pipeline : NATURAL := c_pipeline_input + c_pipeline_output; - SIGNAL in_val : STD_LOGIC; - SIGNAL in_dat : STD_LOGIC_VECTOR(c_in_dat_w-1 DOWNTO 0); - SIGNAL in_vec : STD_LOGIC_VECTOR(c_in_dat_w DOWNTO 0); - SIGNAL reg_vec : STD_LOGIC_VECTOR(c_in_dat_w DOWNTO 0); - SIGNAL reg_val : STD_LOGIC; - SIGNAL reg_dat : STD_LOGIC_VECTOR(c_in_dat_w-1 DOWNTO 0); + CONSTANT c_round_w : INTEGER := g_in_dat_w - g_out_dat_w; - SIGNAL out_sdat_no_rc : STD_LOGIC_VECTOR(c_out_dat_w-1 DOWNTO 0); - SIGNAL out_sdat_with_rc : STD_LOGIC_VECTOR(c_out_dat_w-1 DOWNTO 0); - SIGNAL out_udat : STD_LOGIC_VECTOR(c_out_dat_w-1 DOWNTO 0); - SIGNAL out_tdat : STD_LOGIC_VECTOR(c_out_dat_w-1 DOWNTO 0); -- truncate - - SIGNAL tb_end : STD_LOGIC := '0'; - SIGNAL clk : STD_LOGIC := '1'; - SIGNAL rst : STD_LOGIC := '1'; + -- Expected rounded results from [2] for w = g_in_dat_w = 4 and r = c_round_w = 1 + CONSTANT c_exp_w4_r1_sreal_fixed_point : t_real_arr(0 TO 15) := (-4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5); + CONSTANT c_exp_w4_r1_signed_truncate : t_integer_arr(0 TO 15) := (-4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 3, 3); + CONSTANT c_exp_w4_r1_signed_round_half_away : t_integer_arr(0 TO 15) := (-4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, -4); + CONSTANT c_exp_w4_r1_signed_round_half_away_clip : t_integer_arr(0 TO 15) := (-4, -4, -3, -3, -2, -2, -1, -1, 0, 1, 1, 2, 2, 3, 3, 3); + CONSTANT c_exp_w4_r1_signed_round_half_even : t_integer_arr(0 TO 15) := (-4, -4, -3, -2, -2, -2, -1, 0, 0, 0, 1, 2, 2, 2, 3, -4); + CONSTANT c_exp_w4_r1_signed_round_half_even_clip : t_integer_arr(0 TO 15) := (-4, -4, -3, -2, -2, -2, -1, 0, 0, 0, 1, 2, 2, 2, 3, 3); + + CONSTANT c_exp_w4_r1_unsigned_fixed_point : t_real_arr(0 TO 15) := (0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5); + CONSTANT c_exp_w4_r1_unsigned_truncate : t_natural_arr(0 TO 15) := (0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7); + CONSTANT c_exp_w4_r1_unsigned_round_half_up : t_natural_arr(0 TO 15) := (0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 0); + CONSTANT c_exp_w4_r1_unsigned_round_half_up_clip : t_natural_arr(0 TO 15) := (0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7); + CONSTANT c_exp_w4_r1_unsigned_round_half_even : t_natural_arr(0 TO 15) := (0, 0, 1, 2, 2, 2, 3, 4, 4, 4, 5, 6, 6, 6, 7, 0); + CONSTANT c_exp_w4_r1_unsigned_round_half_even_clip : t_natural_arr(0 TO 15) := (0, 0, 1, 2, 2, 2, 3, 4, 4, 4, 5, 6, 6, 6, 7, 7); + + -- Expected rounded results from [2] for w = g_in_dat_w = 4 and r = c_round_w = 2 + CONSTANT c_exp_w4_r2_sreal_fixed_point : t_real_arr(0 TO 15) := (-2.0, -1.75, -1.5, -1.25, -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75); + CONSTANT c_exp_w4_r2_signed_truncate : t_integer_arr(0 TO 15) := (-2, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 0, 1, 1, 1, 1); + CONSTANT c_exp_w4_r2_signed_round_half_away : t_integer_arr(0 TO 15) := (-2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, -2, -2); + CONSTANT c_exp_w4_r2_signed_round_half_away_clip : t_integer_arr(0 TO 15) := (-2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 1, 1); + CONSTANT c_exp_w4_r2_signed_round_half_even : t_integer_arr(0 TO 15) := (-2, -2, -2, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, -2, -2); + CONSTANT c_exp_w4_r2_signed_round_half_even_clip : t_integer_arr(0 TO 15) := (-2, -2, -2, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1); + + CONSTANT c_exp_w4_r2_unsigned_fixed_point : t_real_arr(0 TO 15) := (0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75); + CONSTANT c_exp_w4_r2_unsigned_truncate : t_natural_arr(0 TO 15) := (0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3); + CONSTANT c_exp_w4_r2_unsigned_round_half_up : t_natural_arr(0 TO 15) := (0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 0, 0); + CONSTANT c_exp_w4_r2_unsigned_round_half_up_clip : t_natural_arr(0 TO 15) := (0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3); + CONSTANT c_exp_w4_r2_unsigned_round_half_even : t_natural_arr(0 TO 15) := (0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 0, 0); + CONSTANT c_exp_w4_r2_unsigned_round_half_even_clip : t_natural_arr(0 TO 15) := (0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3); + + -- Expected rounded results from [2] for w = g_in_dat_w = 4 and r = c_round_w = 3 + CONSTANT c_exp_w4_r3_sreal_fixed_point : t_real_arr(0 TO 15) := (-1.0, -0.875, -0.75, -0.625, -0.5, -0.375, -0.25, -0.125, 0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875); + CONSTANT c_exp_w4_r3_signed_truncate : t_integer_arr(0 TO 15) := (-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0); + CONSTANT c_exp_w4_r3_signed_round_half_away : t_integer_arr(0 TO 15) := (-1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1); + CONSTANT c_exp_w4_r3_signed_round_half_away_clip : t_integer_arr(0 TO 15) := (-1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + CONSTANT c_exp_w4_r3_signed_round_half_even : t_integer_arr(0 TO 15) := (-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1); + CONSTANT c_exp_w4_r3_signed_round_half_even_clip : t_integer_arr(0 TO 15) := (-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + CONSTANT c_exp_w4_r3_unsigned_fixed_point : t_real_arr(0 TO 15) := (0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625, 1.75, 1.875); + CONSTANT c_exp_w4_r3_unsigned_truncate : t_natural_arr(0 TO 15) := (0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); + CONSTANT c_exp_w4_r3_unsigned_round_half_up : t_natural_arr(0 TO 15) := (0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0); + CONSTANT c_exp_w4_r3_unsigned_round_half_up_clip : t_natural_arr(0 TO 15) := (0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + CONSTANT c_exp_w4_r3_unsigned_round_half_even : t_natural_arr(0 TO 15) := (0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0); + CONSTANT c_exp_w4_r3_unsigned_round_half_even_clip : t_natural_arr(0 TO 15) := (0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + -- Expected rounded results from [2] for w = g_in_dat_w = 5 and r = c_round_w = 2 + CONSTANT c_exp_w5_r2_sreal_fixed_point : t_real_arr(0 TO 31) := (-4.0, -3.75, -3.5, -3.25, -3.0, -2.75, -2.5, -2.25, -2.0, -1.75, -1.5, -1.25, -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75); + CONSTANT c_exp_w5_r2_signed_truncate : t_integer_arr(0 TO 31) := (-4, -4, -4, -4, -3, -3, -3, -3, -2, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3); + CONSTANT c_exp_w5_r2_signed_round_half_away : t_integer_arr(0 TO 31) := (-4, -4, -4, -3, -3, -3, -3, -2, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, -4, -4); + CONSTANT c_exp_w5_r2_signed_round_half_away_clip : t_integer_arr(0 TO 31) := (-4, -4, -4, -3, -3, -3, -3, -2, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3); + CONSTANT c_exp_w5_r2_signed_round_half_even : t_integer_arr(0 TO 31) := (-4, -4, -4, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, -4, -4); + CONSTANT c_exp_w5_r2_signed_round_half_even_clip : t_integer_arr(0 TO 31) := (-4, -4, -4, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3); + + CONSTANT c_exp_w5_r2_unsigned_fixed_point : t_real_arr(0 TO 31) := (0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.25, 4.5, 4.75, 5.0, 5.25, 5.5, 5.75, 6.0, 6.25, 6.5, 6.75, 7.0, 7.25, 7.5, 7.75); + CONSTANT c_exp_w5_r2_unsigned_truncate : t_natural_arr(0 TO 31) := (0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7); + CONSTANT c_exp_w5_r2_unsigned_round_half_up : t_natural_arr(0 TO 31) := (0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 0, 0); + CONSTANT c_exp_w5_r2_unsigned_round_half_up_clip : t_natural_arr(0 TO 31) := (0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7); + CONSTANT c_exp_w5_r2_unsigned_round_half_even : t_natural_arr(0 TO 31) := (0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 0, 0); + CONSTANT c_exp_w5_r2_unsigned_round_half_even_clip : t_natural_arr(0 TO 31) := (0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7); + + -- Expected rounded results from [2] for w = g_in_dat_w = 5 and r = c_round_w = 3 + CONSTANT c_exp_w5_r3_sreal_fixed_point : t_real_arr(0 TO 31) := (-2.0, -1.875, -1.75, -1.625, -1.5, -1.375, -1.25, -1.125, -1.0, -0.875, -0.75, -0.625, -0.5, -0.375, -0.25, -0.125, 0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625, 1.75, 1.875); + CONSTANT c_exp_w5_r3_signed_truncate : t_integer_arr(0 TO 31) := (-2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); + CONSTANT c_exp_w5_r3_signed_round_half_away : t_integer_arr(0 TO 31) := (-2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, -2, -2, -2, -2); + CONSTANT c_exp_w5_r3_signed_round_half_away_clip : t_integer_arr(0 TO 31) := (-2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + CONSTANT c_exp_w5_r3_signed_round_half_even : t_integer_arr(0 TO 31) := (-2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, -2, -2, -2, -2); + CONSTANT c_exp_w5_r3_signed_round_half_even_clip : t_integer_arr(0 TO 31) := (-2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + CONSTANT c_exp_w5_r3_unsigned_fixed_point : t_real_arr(0 TO 31) := (0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625, 1.75, 1.875, 2.0, 2.125, 2.25, 2.375, 2.5, 2.625, 2.75, 2.875, 3.0, 3.125, 3.25, 3.375, 3.5, 3.625, 3.75, 3.875); + CONSTANT c_exp_w5_r3_unsigned_truncate : t_natural_arr(0 TO 31) := (0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3); + CONSTANT c_exp_w5_r3_unsigned_round_half_up : t_natural_arr(0 TO 31) := (0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0); + CONSTANT c_exp_w5_r3_unsigned_round_half_up_clip : t_natural_arr(0 TO 31) := (0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3); + CONSTANT c_exp_w5_r3_unsigned_round_half_even : t_natural_arr(0 TO 31) := (0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0); + CONSTANT c_exp_w5_r3_unsigned_round_half_even_clip : t_natural_arr(0 TO 31) := (0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3); + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL clk : STD_LOGIC := '1'; + + -- Input data + SIGNAL in_val : STD_LOGIC; + SIGNAL in_dat : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + SIGNAL reg_val : STD_LOGIC; + SIGNAL reg_dat : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + + -- Signed output data + -- . view as radix decimal in Wave window + SIGNAL fs_signed_integer : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + SIGNAL fs_signed_truncate : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + SIGNAL fs_signed_round_half_away : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + SIGNAL fs_signed_round_half_away_clip : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + SIGNAL fs_signed_round_half_even : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + SIGNAL fs_signed_round_half_even_clip : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + + SIGNAL S_w4 : NATURAL; -- lookup index for signed + SIGNAL exp_w4_r1_signed_truncate : INTEGER; + SIGNAL exp_w4_r1_signed_round_half_away : INTEGER; + SIGNAL exp_w4_r1_signed_round_half_away_clip : INTEGER; + SIGNAL exp_w4_r1_signed_round_half_even : INTEGER; + SIGNAL exp_w4_r1_signed_round_half_even_clip : INTEGER; + + SIGNAL exp_w4_r2_signed_truncate : INTEGER; + SIGNAL exp_w4_r2_signed_round_half_away : INTEGER; + SIGNAL exp_w4_r2_signed_round_half_away_clip : INTEGER; + SIGNAL exp_w4_r2_signed_round_half_even : INTEGER; + SIGNAL exp_w4_r2_signed_round_half_even_clip : INTEGER; + + SIGNAL exp_w4_r3_signed_truncate : INTEGER; + SIGNAL exp_w4_r3_signed_round_half_away : INTEGER; + SIGNAL exp_w4_r3_signed_round_half_away_clip : INTEGER; + SIGNAL exp_w4_r3_signed_round_half_even : INTEGER; + SIGNAL exp_w4_r3_signed_round_half_even_clip : INTEGER; + + SIGNAL S_w5 : NATURAL; -- lookup index for signed + SIGNAL exp_w5_r2_signed_truncate : INTEGER; + SIGNAL exp_w5_r2_signed_round_half_away : INTEGER; + SIGNAL exp_w5_r2_signed_round_half_away_clip : INTEGER; + SIGNAL exp_w5_r2_signed_round_half_even : INTEGER; + SIGNAL exp_w5_r2_signed_round_half_even_clip : INTEGER; + + SIGNAL exp_w5_r3_signed_truncate : INTEGER; + SIGNAL exp_w5_r3_signed_round_half_away : INTEGER; + SIGNAL exp_w5_r3_signed_round_half_away_clip : INTEGER; + SIGNAL exp_w5_r3_signed_round_half_even : INTEGER; + SIGNAL exp_w5_r3_signed_round_half_even_clip : INTEGER; + + -- . show as real in Wave window + SIGNAL fs_sreal_fixed_point : REAL := 0.0; + SIGNAL fs_sreal_truncate : REAL := 0.0; + SIGNAL fs_sreal_round_half_away : REAL := 0.0; + SIGNAL fs_sreal_round_half_away_clip : REAL := 0.0; + SIGNAL fs_sreal_round_half_even : REAL := 0.0; + SIGNAL fs_sreal_round_half_even_clip : REAL := 0.0; + + -- Unsigned output data + -- . view as radix unsigned in Wave window + SIGNAL fs_unsigned_integer : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0); + SIGNAL fs_unsigned_truncate : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + SIGNAL fs_unsigned_round_half_up : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + SIGNAL fs_unsigned_round_half_up_clip : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + SIGNAL fs_unsigned_round_half_even : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + SIGNAL fs_unsigned_round_half_even_clip : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); + + SIGNAL U_w4 : NATURAL; -- lookup index for unsigned + SIGNAL exp_w4_r1_unsigned_truncate : INTEGER; + SIGNAL exp_w4_r1_unsigned_round_half_up : INTEGER; + SIGNAL exp_w4_r1_unsigned_round_half_up_clip : INTEGER; + SIGNAL exp_w4_r1_unsigned_round_half_even : INTEGER; + SIGNAL exp_w4_r1_unsigned_round_half_even_clip : INTEGER; + + SIGNAL exp_w4_r2_unsigned_truncate : INTEGER; + SIGNAL exp_w4_r2_unsigned_round_half_up : INTEGER; + SIGNAL exp_w4_r2_unsigned_round_half_up_clip : INTEGER; + SIGNAL exp_w4_r2_unsigned_round_half_even : INTEGER; + SIGNAL exp_w4_r2_unsigned_round_half_even_clip : INTEGER; + + SIGNAL exp_w4_r3_unsigned_truncate : INTEGER; + SIGNAL exp_w4_r3_unsigned_round_half_up : INTEGER; + SIGNAL exp_w4_r3_unsigned_round_half_up_clip : INTEGER; + SIGNAL exp_w4_r3_unsigned_round_half_even : INTEGER; + SIGNAL exp_w4_r3_unsigned_round_half_even_clip : INTEGER; + + SIGNAL U_w5 : NATURAL; -- lookup index for unsigned + SIGNAL exp_w5_r2_unsigned_truncate : INTEGER; + SIGNAL exp_w5_r2_unsigned_round_half_up : INTEGER; + SIGNAL exp_w5_r2_unsigned_round_half_up_clip : INTEGER; + SIGNAL exp_w5_r2_unsigned_round_half_even : INTEGER; + SIGNAL exp_w5_r2_unsigned_round_half_even_clip : INTEGER; + + SIGNAL exp_w5_r3_unsigned_truncate : INTEGER; + SIGNAL exp_w5_r3_unsigned_round_half_up : INTEGER; + SIGNAL exp_w5_r3_unsigned_round_half_up_clip : INTEGER; + SIGNAL exp_w5_r3_unsigned_round_half_even : INTEGER; + SIGNAL exp_w5_r3_unsigned_round_half_even_clip : INTEGER; + + -- . show as real in Wave window + SIGNAL fs_ureal_fixed_point : REAL := 0.0; + SIGNAL fs_ureal_truncate : REAL := 0.0; + SIGNAL fs_ureal_round_half_up : REAL := 0.0; + SIGNAL fs_ureal_round_half_up_clip : REAL := 0.0; + SIGNAL fs_ureal_round_half_even : REAL := 0.0; + SIGNAL fs_ureal_round_half_even_clip : REAL := 0.0; - CONSTANT c_init : STD_LOGIC_VECTOR(in_dat'RANGE) := (OTHERS=>'0'); - BEGIN -- Stimuli clk <= NOT clk OR tb_end AFTER clk_period/2; - rst <= '1', '0' AFTER 3*clk_period; - -- Testbench end - p_tb_end : PROCESS - VARIABLE v_dat : STD_LOGIC_VECTOR(in_dat'RANGE); + p_stimuli : PROCESS BEGIN - tb_end <= '0'; - WAIT UNTIL in_val='1'; - WAIT UNTIL rising_edge(clk); - v_dat := in_dat; -- keep first in_dat - WAIT UNTIL rising_edge(clk); - WAIT UNTIL v_dat=in_dat; -- wait until all incrementing in_dat values have been applied at least once - WAIT UNTIL rising_edge(clk); - WAIT UNTIL rising_edge(clk); + in_val <= '0'; + in_dat <= (OTHERS=>'0'); + FOR I IN 0 TO 3 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + in_val <= '1'; WAIT UNTIL rising_edge(clk); + FOR I IN 0 TO 2**g_in_dat_w - 1 LOOP + in_dat <= INCR_UVEC(in_dat, 1); + WAIT UNTIL rising_edge(clk); + END LOOP; + in_val <= '0'; + in_dat <= (OTHERS=>'0'); + FOR I IN 0 TO 3 LOOP WAIT UNTIL rising_edge(clk); END LOOP; tb_end <= '1'; WAIT; END PROCESS; + + -- Delay input as much as DUT output, assume c_pipeline = 0 + reg_val <= in_val; + reg_dat <= in_dat; - p_clk : PROCESS (rst, clk) - BEGIN - IF rst='1' THEN - in_val <= '0'; - in_dat <= c_init; - ELSIF rising_edge(clk) THEN - in_val <= '1'; - in_dat <= STD_LOGIC_VECTOR(SIGNED(in_dat)+1); - END IF; - END PROCESS; - - -- Delay input as much as DUT output - in_vec <= in_val & in_dat; - - u_pipe : ENTITY work.common_pipeline + fs_signed_integer <= reg_dat; + fs_unsigned_integer <= reg_dat; + + ----------------------------------------------------------------------------- + -- SIGNED DUTs + ----------------------------------------------------------------------------- + + s_truncate : ENTITY work.common_round GENERIC MAP ( - g_representation => "SIGNED", - g_pipeline => c_pipeline, - g_in_dat_w => c_in_dat_w+1, - g_out_dat_w => c_in_dat_w+1 + g_representation => "SIGNED", + g_round => FALSE, + g_round_clip => FALSE, + g_pipeline_input => c_pipeline_input, + g_pipeline_output => c_pipeline_output, + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w ) PORT MAP ( - clk => clk, - in_dat => in_vec, - out_dat => reg_vec + clk => clk, + in_dat => in_dat, + out_dat => fs_signed_truncate ); - - reg_val <= reg_vec(c_in_dat_w); - reg_dat <= reg_vec(c_in_dat_w-1 DOWNTO 0); - - -- DUT for "SIGNED" round without clipping of rounding overflow - u_s_round : ENTITY work.common_round + + s_round_half_away : ENTITY work.common_round GENERIC MAP ( g_representation => "SIGNED", g_round => TRUE, g_round_clip => FALSE, g_pipeline_input => c_pipeline_input, g_pipeline_output => c_pipeline_output, - g_in_dat_w => c_in_dat_w, - g_out_dat_w => c_out_dat_w + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w ) PORT MAP ( clk => clk, - clken => '1', in_dat => in_dat, - out_dat => out_sdat_no_rc + out_dat => fs_signed_round_half_away ); - -- DUT for "SIGNED" round with clipping of rounding overflow - u_s_round_rc : ENTITY work.common_round + s_round_half_away_clip : ENTITY work.common_round GENERIC MAP ( g_representation => "SIGNED", g_round => TRUE, g_round_clip => TRUE, g_pipeline_input => c_pipeline_input, g_pipeline_output => c_pipeline_output, - g_in_dat_w => c_in_dat_w, - g_out_dat_w => c_out_dat_w + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w ) PORT MAP ( clk => clk, - clken => '1', in_dat => in_dat, - out_dat => out_sdat_with_rc + out_dat => fs_signed_round_half_away_clip ); - -- DUT for "UNSIGNED" round - u_u_round : ENTITY work.common_round + s_round_half_even : ENTITY work.common_round GENERIC MAP ( - g_representation => "UNSIGNED", + g_representation => "SIGNED", g_round => TRUE, - g_round_clip => c_round_clip, + g_round_clip => FALSE, + g_round_even => TRUE, g_pipeline_input => c_pipeline_input, g_pipeline_output => c_pipeline_output, - g_in_dat_w => c_in_dat_w, - g_out_dat_w => c_out_dat_w + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w ) PORT MAP ( clk => clk, - clken => '1', in_dat => in_dat, - out_dat => out_udat + out_dat => fs_signed_round_half_even ); - + + s_round_half_even_clip : ENTITY work.common_round + GENERIC MAP ( + g_representation => "SIGNED", + g_round => TRUE, + g_round_clip => TRUE, + g_round_even => TRUE, + g_pipeline_input => c_pipeline_input, + g_pipeline_output => c_pipeline_output, + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w + ) + PORT MAP ( + clk => clk, + in_dat => in_dat, + out_dat => fs_signed_round_half_even_clip + ); + + + ----------------------------------------------------------------------------- + -- UNSIGNED DUTs + ----------------------------------------------------------------------------- + -- DUT for truncate u_truncate : ENTITY work.common_round GENERIC MAP ( g_representation => "UNSIGNED", g_round => FALSE, - g_round_clip => c_round_clip, + g_round_clip => FALSE, g_pipeline_input => c_pipeline_input, g_pipeline_output => c_pipeline_output, - g_in_dat_w => c_in_dat_w, - g_out_dat_w => c_out_dat_w + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w ) PORT MAP ( clk => clk, - clken => '1', in_dat => in_dat, - out_dat => out_tdat + out_dat => fs_unsigned_truncate + ); + + u_round_half_up : ENTITY work.common_round + GENERIC MAP ( + g_representation => "UNSIGNED", + g_round => TRUE, + g_round_clip => FALSE, + g_pipeline_input => c_pipeline_input, + g_pipeline_output => c_pipeline_output, + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w + ) + PORT MAP ( + clk => clk, + in_dat => in_dat, + out_dat => fs_unsigned_round_half_up ); - + u_round_half_up_clip : ENTITY work.common_round + GENERIC MAP ( + g_representation => "UNSIGNED", + g_round => TRUE, + g_round_clip => TRUE, + g_pipeline_input => c_pipeline_input, + g_pipeline_output => c_pipeline_output, + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w + ) + PORT MAP ( + clk => clk, + in_dat => in_dat, + out_dat => fs_unsigned_round_half_up_clip + ); + + u_round_half_even : ENTITY work.common_round + GENERIC MAP ( + g_representation => "UNSIGNED", + g_round => TRUE, + g_round_clip => FALSE, + g_round_even => TRUE, + g_pipeline_input => c_pipeline_input, + g_pipeline_output => c_pipeline_output, + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w + ) + PORT MAP ( + clk => clk, + in_dat => in_dat, + out_dat => fs_unsigned_round_half_even + ); + + u_round_half_even_clip : ENTITY work.common_round + GENERIC MAP ( + g_representation => "UNSIGNED", + g_round => TRUE, + g_round_clip => TRUE, + g_round_even => TRUE, + g_pipeline_input => c_pipeline_input, + g_pipeline_output => c_pipeline_output, + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w + ) + PORT MAP ( + clk => clk, + in_dat => in_dat, + out_dat => fs_unsigned_round_half_even_clip + ); + + -- Observe fixed point SLV values as REAL + -- . signed + fs_sreal_fixed_point <= TO_SREAL(fs_signed_integer, c_round_w); + fs_sreal_truncate <= TO_SREAL(fs_signed_truncate, 0); + fs_sreal_round_half_away <= TO_SREAL(fs_signed_round_half_away, 0); + fs_sreal_round_half_away_clip <= TO_SREAL(fs_signed_round_half_away_clip, 0); + fs_sreal_round_half_even <= TO_SREAL(fs_signed_round_half_even, 0); + fs_sreal_round_half_even_clip <= TO_SREAL(fs_signed_round_half_even_clip, 0); + + -- . unsigned + fs_ureal_fixed_point <= TO_UREAL(fs_unsigned_integer, c_round_w); + fs_ureal_truncate <= TO_UREAL(fs_unsigned_truncate, 0); + fs_ureal_round_half_up <= TO_UREAL(fs_unsigned_round_half_up, 0); + fs_ureal_round_half_up_clip <= TO_UREAL(fs_unsigned_round_half_up_clip, 0); + fs_ureal_round_half_even <= TO_UREAL(fs_unsigned_round_half_even, 0); + fs_ureal_round_half_even_clip <= TO_UREAL(fs_unsigned_round_half_even_clip, 0); + + -- Expected rounded values + -- . w = 4 + S_w4 <= (TO_UINT(in_dat) + 8) MOD 16; -- 2**4 = 16 + U_w4 <= TO_UINT(in_dat) MOD 16; + + -- . w = 4, r = 1 + exp_w4_r1_signed_truncate <= c_exp_w4_r1_signed_truncate(S_w4); + exp_w4_r1_signed_round_half_away <= c_exp_w4_r1_signed_round_half_away(S_w4); + exp_w4_r1_signed_round_half_away_clip <= c_exp_w4_r1_signed_round_half_away_clip(S_w4); + exp_w4_r1_signed_round_half_even <= c_exp_w4_r1_signed_round_half_even(S_w4); + exp_w4_r1_signed_round_half_even_clip <= c_exp_w4_r1_signed_round_half_even_clip(S_w4); + + exp_w4_r1_unsigned_truncate <= c_exp_w4_r1_unsigned_truncate(U_w4); + exp_w4_r1_unsigned_round_half_up <= c_exp_w4_r1_unsigned_round_half_up(U_w4); + exp_w4_r1_unsigned_round_half_up_clip <= c_exp_w4_r1_unsigned_round_half_up_clip(U_w4); + exp_w4_r1_unsigned_round_half_even <= c_exp_w4_r1_unsigned_round_half_even(U_w4); + exp_w4_r1_unsigned_round_half_even_clip <= c_exp_w4_r1_unsigned_round_half_even_clip(U_w4); + + -- . w = 4, r = 2 + exp_w4_r2_signed_truncate <= c_exp_w4_r2_signed_truncate(S_w4); + exp_w4_r2_signed_round_half_away <= c_exp_w4_r2_signed_round_half_away(S_w4); + exp_w4_r2_signed_round_half_away_clip <= c_exp_w4_r2_signed_round_half_away_clip(S_w4); + exp_w4_r2_signed_round_half_even <= c_exp_w4_r2_signed_round_half_even(S_w4); + exp_w4_r2_signed_round_half_even_clip <= c_exp_w4_r2_signed_round_half_even_clip(S_w4); + + exp_w4_r2_unsigned_truncate <= c_exp_w4_r2_unsigned_truncate(U_w4); + exp_w4_r2_unsigned_round_half_up <= c_exp_w4_r2_unsigned_round_half_up(U_w4); + exp_w4_r2_unsigned_round_half_up_clip <= c_exp_w4_r2_unsigned_round_half_up_clip(U_w4); + exp_w4_r2_unsigned_round_half_even <= c_exp_w4_r2_unsigned_round_half_even(U_w4); + exp_w4_r2_unsigned_round_half_even_clip <= c_exp_w4_r2_unsigned_round_half_even_clip(U_w4); + + -- . w = 4, r = 3 + exp_w4_r3_signed_truncate <= c_exp_w4_r3_signed_truncate(S_w4); + exp_w4_r3_signed_round_half_away <= c_exp_w4_r3_signed_round_half_away(S_w4); + exp_w4_r3_signed_round_half_away_clip <= c_exp_w4_r3_signed_round_half_away_clip(S_w4); + exp_w4_r3_signed_round_half_even <= c_exp_w4_r3_signed_round_half_even(S_w4); + exp_w4_r3_signed_round_half_even_clip <= c_exp_w4_r3_signed_round_half_even_clip(S_w4); + + exp_w4_r3_unsigned_truncate <= c_exp_w4_r3_unsigned_truncate(U_w4); + exp_w4_r3_unsigned_round_half_up <= c_exp_w4_r3_unsigned_round_half_up(U_w4); + exp_w4_r3_unsigned_round_half_up_clip <= c_exp_w4_r3_unsigned_round_half_up_clip(U_w4); + exp_w4_r3_unsigned_round_half_even <= c_exp_w4_r3_unsigned_round_half_even(U_w4); + exp_w4_r3_unsigned_round_half_even_clip <= c_exp_w4_r3_unsigned_round_half_even_clip(U_w4); + + -- . w = 5 + S_w5 <= (TO_UINT(in_dat) + 16) MOD 32; -- 2**5 = 32 + U_w5 <= TO_UINT(in_dat) MOD 32; + + -- . w = 5, r = 2 + exp_w5_r2_signed_truncate <= c_exp_w5_r2_signed_truncate(S_w5); + exp_w5_r2_signed_round_half_away <= c_exp_w5_r2_signed_round_half_away(S_w5); + exp_w5_r2_signed_round_half_away_clip <= c_exp_w5_r2_signed_round_half_away_clip(S_w5); + exp_w5_r2_signed_round_half_even <= c_exp_w5_r2_signed_round_half_even(S_w5); + exp_w5_r2_signed_round_half_even_clip <= c_exp_w5_r2_signed_round_half_even_clip(S_w5); + + exp_w5_r2_unsigned_truncate <= c_exp_w5_r2_unsigned_truncate(U_w5); + exp_w5_r2_unsigned_round_half_up <= c_exp_w5_r2_unsigned_round_half_up(U_w5); + exp_w5_r2_unsigned_round_half_up_clip <= c_exp_w5_r2_unsigned_round_half_up_clip(U_w5); + exp_w5_r2_unsigned_round_half_even <= c_exp_w5_r2_unsigned_round_half_even(U_w5); + exp_w5_r2_unsigned_round_half_even_clip <= c_exp_w5_r2_unsigned_round_half_even_clip(U_w5); + + -- . w = 5, r = 3 + exp_w5_r3_signed_truncate <= c_exp_w5_r3_signed_truncate(S_w5); + exp_w5_r3_signed_round_half_away <= c_exp_w5_r3_signed_round_half_away(S_w5); + exp_w5_r3_signed_round_half_away_clip <= c_exp_w5_r3_signed_round_half_away_clip(S_w5); + exp_w5_r3_signed_round_half_even <= c_exp_w5_r3_signed_round_half_even(S_w5); + exp_w5_r3_signed_round_half_even_clip <= c_exp_w5_r3_signed_round_half_even_clip(S_w5); + + exp_w5_r3_unsigned_truncate <= c_exp_w5_r3_unsigned_truncate(U_w5); + exp_w5_r3_unsigned_round_half_up <= c_exp_w5_r3_unsigned_round_half_up(U_w5); + exp_w5_r3_unsigned_round_half_up_clip <= c_exp_w5_r3_unsigned_round_half_up_clip(U_w5); + exp_w5_r3_unsigned_round_half_even <= c_exp_w5_r3_unsigned_round_half_even(U_w5); + exp_w5_r3_unsigned_round_half_even_clip <= c_exp_w5_r3_unsigned_round_half_even_clip(U_w5); + -- Verification p_verify : PROCESS BEGIN WAIT UNTIL rising_edge(clk); IF reg_val = '1' THEN + IF c_round_w <= 0 THEN + -- Without rounding the expected value is same as input value + -- . signed + ASSERT SIGNED(fs_signed_truncate ) = SIGNED(reg_dat) REPORT "Wrong wired fs_signed_truncate" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away ) = SIGNED(reg_dat) REPORT "Wrong wired fs_signed_round_half_away" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away_clip) = SIGNED(reg_dat) REPORT "Wrong wired fs_signed_round_half_away_clip" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even ) = SIGNED(reg_dat) REPORT "Wrong wired fs_signed_round_half_even" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even_clip) = SIGNED(reg_dat) REPORT "Wrong wired fs_signed_round_half_even_clip" SEVERITY ERROR; + -- . unsigned + ASSERT UNSIGNED(fs_unsigned_truncate ) = UNSIGNED(reg_dat) REPORT "Wrong wired fs_unsigned_truncate" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up ) = UNSIGNED(reg_dat) REPORT "Wrong wired fs_unsigned_round_half_up" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up_clip ) = UNSIGNED(reg_dat) REPORT "Wrong wired fs_unsigned_round_half_up_clip" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even ) = UNSIGNED(reg_dat) REPORT "Wrong wired fs_unsigned_round_half_even" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even_clip) = UNSIGNED(reg_dat) REPORT "Wrong wired fs_unsigned_round_half_even_clip" SEVERITY ERROR; + ELSE + -- With rounding then compare with expected list of values from common_round_tb.py + IF g_in_dat_w = 4 AND c_round_w = 1 THEN + -- . signed + ASSERT SIGNED(fs_signed_truncate ) = exp_w4_r1_signed_truncate REPORT "Wrong exp_w4_r1_signed_truncate" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away ) = exp_w4_r1_signed_round_half_away REPORT "Wrong exp_w4_r1_signed_round_half_away" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away_clip) = exp_w4_r1_signed_round_half_away_clip REPORT "Wrong exp_w4_r1_signed_round_half_away_clip" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even ) = exp_w4_r1_signed_round_half_even REPORT "Wrong exp_w4_r1_signed_round_half_even" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even_clip) = exp_w4_r1_signed_round_half_even_clip REPORT "Wrong exp_w4_r1_signed_round_half_even_clip" SEVERITY ERROR; + -- . unsigned + ASSERT UNSIGNED(fs_unsigned_truncate ) = exp_w4_r1_unsigned_truncate REPORT "Wrong exp_w4_r1_unsigned_truncate" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up ) = exp_w4_r1_unsigned_round_half_up REPORT "Wrong exp_w4_r1_unsigned_round_half_up" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up_clip ) = exp_w4_r1_unsigned_round_half_up_clip REPORT "Wrong exp_w4_r1_unsigned_round_half_up_clip" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even ) = exp_w4_r1_unsigned_round_half_even REPORT "Wrong exp_w4_r1_unsigned_round_half_even" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even_clip) = exp_w4_r1_unsigned_round_half_even_clip REPORT "Wrong exp_w4_r1_unsigned_round_half_even_clip" SEVERITY ERROR; + END IF; + IF g_in_dat_w = 4 AND c_round_w = 2 THEN + -- . signed + ASSERT SIGNED(fs_signed_truncate ) = exp_w4_r2_signed_truncate REPORT "Wrong exp_w4_r2_signed_truncate" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away ) = exp_w4_r2_signed_round_half_away REPORT "Wrong exp_w4_r2_signed_round_half_away" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away_clip) = exp_w4_r2_signed_round_half_away_clip REPORT "Wrong exp_w4_r2_signed_round_half_away_clip" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even ) = exp_w4_r2_signed_round_half_even REPORT "Wrong exp_w4_r2_signed_round_half_even" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even_clip) = exp_w4_r2_signed_round_half_even_clip REPORT "Wrong exp_w4_r2_signed_round_half_even_clip" SEVERITY ERROR; + -- . unsigned + ASSERT UNSIGNED(fs_unsigned_truncate ) = exp_w4_r2_unsigned_truncate REPORT "Wrong exp_w4_r2_unsigned_truncate" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up ) = exp_w4_r2_unsigned_round_half_up REPORT "Wrong exp_w4_r2_unsigned_round_half_up" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up_clip ) = exp_w4_r2_unsigned_round_half_up_clip REPORT "Wrong exp_w4_r2_unsigned_round_half_up_clip" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even ) = exp_w4_r2_unsigned_round_half_even REPORT "Wrong exp_w4_r2_unsigned_round_half_even" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even_clip) = exp_w4_r2_unsigned_round_half_even_clip REPORT "Wrong exp_w4_r2_unsigned_round_half_even_clip" SEVERITY ERROR; + END IF; + IF g_in_dat_w = 4 AND c_round_w = 3 THEN + -- . signed + ASSERT SIGNED(fs_signed_truncate ) = exp_w4_r3_signed_truncate REPORT "Wrong exp_w4_r3_signed_truncate" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away ) = exp_w4_r3_signed_round_half_away REPORT "Wrong exp_w4_r3_signed_round_half_away" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away_clip) = exp_w4_r3_signed_round_half_away_clip REPORT "Wrong exp_w4_r3_signed_round_half_away_clip" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even ) = exp_w4_r3_signed_round_half_even REPORT "Wrong exp_w4_r3_signed_round_half_even" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even_clip) = exp_w4_r3_signed_round_half_even_clip REPORT "Wrong exp_w4_r3_signed_round_half_even_clip" SEVERITY ERROR; + -- . unsigned + ASSERT UNSIGNED(fs_unsigned_truncate ) = exp_w4_r3_unsigned_truncate REPORT "Wrong exp_w4_r3_unsigned_truncate" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up ) = exp_w4_r3_unsigned_round_half_up REPORT "Wrong exp_w4_r3_unsigned_round_half_up" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up_clip ) = exp_w4_r3_unsigned_round_half_up_clip REPORT "Wrong exp_w4_r3_unsigned_round_half_up_clip" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even ) = exp_w4_r3_unsigned_round_half_even REPORT "Wrong exp_w4_r3_unsigned_round_half_even" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even_clip) = exp_w4_r3_unsigned_round_half_even_clip REPORT "Wrong exp_w4_r3_unsigned_round_half_even_clip" SEVERITY ERROR; + END IF; + IF g_in_dat_w = 5 AND c_round_w = 2 THEN + -- . signed + ASSERT SIGNED(fs_signed_truncate ) = exp_w5_r2_signed_truncate REPORT "Wrong exp_w5_r2_signed_truncate" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away ) = exp_w5_r2_signed_round_half_away REPORT "Wrong exp_w5_r2_signed_round_half_away" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away_clip) = exp_w5_r2_signed_round_half_away_clip REPORT "Wrong exp_w5_r2_signed_round_half_away_clip" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even ) = exp_w5_r2_signed_round_half_even REPORT "Wrong exp_w5_r2_signed_round_half_even" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even_clip) = exp_w5_r2_signed_round_half_even_clip REPORT "Wrong exp_w5_r2_signed_round_half_even_clip" SEVERITY ERROR; + -- . unsigned + ASSERT UNSIGNED(fs_unsigned_truncate ) = exp_w5_r2_unsigned_truncate REPORT "Wrong exp_w5_r2_unsigned_truncate" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up ) = exp_w5_r2_unsigned_round_half_up REPORT "Wrong exp_w5_r2_unsigned_round_half_up" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up_clip ) = exp_w5_r2_unsigned_round_half_up_clip REPORT "Wrong exp_w5_r2_unsigned_round_half_up_clip" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even ) = exp_w5_r2_unsigned_round_half_even REPORT "Wrong exp_w5_r2_unsigned_round_half_even" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even_clip) = exp_w5_r2_unsigned_round_half_even_clip REPORT "Wrong exp_w5_r2_unsigned_round_half_even_clip" SEVERITY ERROR; + END IF; + IF g_in_dat_w = 5 AND c_round_w = 3 THEN + -- . signed + ASSERT SIGNED(fs_signed_truncate ) = exp_w5_r3_signed_truncate REPORT "Wrong exp_w5_r3_signed_truncate" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away ) = exp_w5_r3_signed_round_half_away REPORT "Wrong exp_w5_r3_signed_round_half_away" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_away_clip) = exp_w5_r3_signed_round_half_away_clip REPORT "Wrong exp_w5_r3_signed_round_half_away_clip" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even ) = exp_w5_r3_signed_round_half_even REPORT "Wrong exp_w5_r3_signed_round_half_even" SEVERITY ERROR; + ASSERT SIGNED(fs_signed_round_half_even_clip) = exp_w5_r3_signed_round_half_even_clip REPORT "Wrong exp_w5_r3_signed_round_half_even_clip" SEVERITY ERROR; + -- . unsigned + ASSERT UNSIGNED(fs_unsigned_truncate ) = exp_w5_r3_unsigned_truncate REPORT "Wrong exp_w5_r3_unsigned_truncate" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up ) = exp_w5_r3_unsigned_round_half_up REPORT "Wrong exp_w5_r3_unsigned_round_half_up" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_up_clip ) = exp_w5_r3_unsigned_round_half_up_clip REPORT "Wrong exp_w5_r3_unsigned_round_half_up_clip" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even ) = exp_w5_r3_unsigned_round_half_even REPORT "Wrong exp_w5_r3_unsigned_round_half_even" SEVERITY ERROR; + ASSERT UNSIGNED(fs_unsigned_round_half_even_clip) = exp_w5_r3_unsigned_round_half_even_clip REPORT "Wrong exp_w5_r3_unsigned_round_half_even_clip" SEVERITY ERROR; + END IF; + END IF; END IF; END PROCESS; diff --git a/libraries/base/common/tb/vhdl/tb_tb_round.vhd b/libraries/base/common/tb/vhdl/tb_tb_round.vhd new file mode 100644 index 0000000000000000000000000000000000000000..40efae340b378080d4043ea10edc83e227440f95 --- /dev/null +++ b/libraries/base/common/tb/vhdl/tb_tb_round.vhd @@ -0,0 +1,42 @@ +-- -------------------------------------------------------------------------- +-- Copyright 2021 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- -------------------------------------------------------------------------- +-- +-- Author: E. Kooistra, 4 nov 2021 +-- Purpose: Multi tb for common_round.vhd and s_round(), u_round() in common_pkg.vhd + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; + +ENTITY tb_tb_round IS +END tb_tb_round; + +ARCHITECTURE tb OF tb_tb_round IS + SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' +BEGIN + -- -- Supported for RESIZE_NUM() and common_round.vhd + -- g_in_dat_w : NATURAL := 5; + -- g_out_dat_w : NATURAL := 3 + + u_extend : ENTITY work.tb_round GENERIC MAP (5, 6); + u_wires : ENTITY work.tb_round GENERIC MAP (5, 5); + u_round_w4_r1 : ENTITY work.tb_round GENERIC MAP (4, 3); -- -r = 4 - 3 = 1 + u_round_w4_r2 : ENTITY work.tb_round GENERIC MAP (4, 2); -- -r = 4 - 2 = 2 + u_round_w4_r3 : ENTITY work.tb_round GENERIC MAP (4, 1); -- -r = 4 - 1 = 3 + u_round_w5_r2 : ENTITY work.tb_round GENERIC MAP (5, 3); -- -r = 5 - 3 = 2 + u_round_w5_r3 : ENTITY work.tb_round GENERIC MAP (5, 2); -- -r = 5 - 2 = 3 +END tb; diff --git a/libraries/base/dp/src/vhdl/dp_requantize.vhd b/libraries/base/dp/src/vhdl/dp_requantize.vhd index 1ae2ca974821c6bc4319b9e74a5d6ff3fc5b3fc9..31d63241e128df435401ce5e9a9bba5a41adf732 100644 --- a/libraries/base/dp/src/vhdl/dp_requantize.vhd +++ b/libraries/base/dp/src/vhdl/dp_requantize.vhd @@ -34,14 +34,15 @@ USE common_lib.common_pkg.ALL; ENTITY dp_requantize IS GENERIC ( - g_complex : BOOLEAN := TRUE; -- when true, the re and im field are processed, when false, the data field is processed + g_complex : BOOLEAN := TRUE; -- when TRUE, the re and im field are processed, when false, the data field is processed g_representation : STRING := "SIGNED"; -- SIGNED (round +-0.5 away from zero to +- infinity) or UNSIGNED rounding (round 0.5 up to + inifinity) g_lsb_w : INTEGER := 4; -- when > 0, number of LSbits to remove from in_dat -- when < 0, number of LSBits to insert as a gain before resize to out_dat'LENGTH -- when 0 then no effect - g_lsb_round : BOOLEAN := TRUE; -- when true ROUND else TRUNCATE the input LSbits - g_lsb_round_clip : BOOLEAN := FALSE; -- when true round clip to +max to avoid wrapping to output -min (signed) or 0 (unsigned) due to rounding - g_msb_clip : BOOLEAN := TRUE; -- when true CLIP else WRAP the input MSbits + g_lsb_round : BOOLEAN := TRUE; -- when TRUE round else truncate the input LSbits + g_lsb_round_clip : BOOLEAN := FALSE; -- when TRUE round clip to +max to avoid wrapping to output -min (signed) or 0 (unsigned) due to rounding + g_lsb_round_even : BOOLEAN := FALSE; -- when TRUE round to even, else round away from zero + g_msb_clip : BOOLEAN := TRUE; -- when TRUE CLIP else WRAP the input MSbits g_msb_clip_symmetric : BOOLEAN := FALSE; -- when TRUE clip signed symmetric to +c_smax and -c_smax, else to +c_smax and c_smin_symm -- for wrapping when g_msb_clip=FALSE the g_msb_clip_symmetric is ignored, so signed wrapping is done asymmetric g_gain_w : NATURAL := 0; -- do not use, must be 0, use negative g_lsb_w instead @@ -88,7 +89,8 @@ BEGIN g_representation => g_representation, g_lsb_w => g_lsb_w, g_lsb_round => g_lsb_round, - g_lsb_round_clip => g_lsb_round_clip, + g_lsb_round_clip => g_lsb_round_clip, + g_lsb_round_even => g_lsb_round_even, g_msb_clip => g_msb_clip, g_msb_clip_symmetric => g_msb_clip_symmetric, g_pipeline_remove_lsb => g_pipeline_remove_lsb, @@ -113,7 +115,8 @@ BEGIN g_representation => g_representation, g_lsb_w => g_lsb_w, g_lsb_round => g_lsb_round, - g_lsb_round_clip => g_lsb_round_clip, + g_lsb_round_clip => g_lsb_round_clip, + g_lsb_round_even => g_lsb_round_even, g_msb_clip => g_msb_clip, g_msb_clip_symmetric => g_msb_clip_symmetric, g_pipeline_remove_lsb => g_pipeline_remove_lsb, @@ -133,7 +136,8 @@ BEGIN g_representation => g_representation, g_lsb_w => g_lsb_w, g_lsb_round => g_lsb_round, - g_lsb_round_clip => g_lsb_round_clip, + g_lsb_round_clip => g_lsb_round_clip, + g_lsb_round_even => g_lsb_round_even, g_msb_clip => g_msb_clip, g_msb_clip_symmetric => g_msb_clip_symmetric, g_pipeline_remove_lsb => g_pipeline_remove_lsb,