Skip to content
Snippets Groups Projects
Commit 56825826 authored by Reinier van der Walle's avatar Reinier van der Walle
Browse files

Merge branch 'L2SDP-303' into 'master'

Resolve L2SDP-303

Closes L2SDP-303

See merge request desp/hdl!165
parents 95b730bc c425bf7d
No related branches found
No related tags found
1 merge request!165Resolve L2SDP-303
......@@ -196,6 +196,7 @@ test_bench_files =
tb/vhdl/tb_delta_cycle_demo.vhd
tb/vhdl/tb_mms_common_variable_delay.vhd
tb/vhdl/tb_tb_resize.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
......@@ -218,7 +219,7 @@ regression_test_vhdl =
tb/vhdl/tb_common_shiftreg.vhd
tb/vhdl/tb_common_transpose_symbol.vhd
tb/vhdl/tb_common_variable_delay.vhd
tb/vhdl/tb_resize.vhd
tb/vhdl/tb_tb_resize.vhd
#tb/vhdl/tb_round.vhd -- has no self verification yet
tb/vhdl/tb_requantize.vhd
tb/vhdl/tb_common_to_sreal.vhd
......
......@@ -383,12 +383,38 @@ PACKAGE common_pkg IS
FUNCTION TO_UREAL(uvec : STD_LOGIC_VECTOR; resolution_w : INTEGER) RETURN REAL; -- convert unsigned fixed point slv of any length, and with resolution of 2**resolution_w, to REAL
FUNCTION TO_SREAL(svec : STD_LOGIC_VECTOR; resolution_w : INTEGER) RETURN REAL; -- convert signed fixed point slv of any length, and with resolution of 2**resolution_w, to REAL
-- The RESIZE for SIGNED in IEEE.NUMERIC_STD extends the sign bit or it keeps the sign bit and LS part. This
-- RESIZE_NUM() original description:
-- The RESIZE for SIGNED in IEEE.NUMERIC_STD extends the sign bit or it keeps the sign bit and LS part. This
-- behaviour of preserving the sign bit is less suitable for DSP and not necessary in general. A more
-- appropriate approach is to ignore the MSbit sign and just keep the LS part. For too large values this
-- means that the result gets wrapped, but that is fine for default behaviour, because that is also what
-- happens for RESIZE of UNSIGNED. Therefor this is what the RESIZE_NUM for SIGNED and the RESIZE_SVEC do
-- and better not use RESIZE for SIGNED anymore.
--
-- RESIZE_NUM() updated description (27 oct 2021):
-- The RESIZE() from IEEE.NUMERIC_STD keeps the sign bit and the w-1 LSbits, this results in a signal that
-- keeps the sign, but wraps between - and 0 for negative input, and that wraps between + and 0 for
-- positive input.
-- The RESIZE_NUM() from in this common_pkg.vhd simply keeps the w LSbits, so it wraps between - and +.
-- Hence RESIZE_NUM() works the same for SIGNED as for UNSIGNED. For an adder that sums multiple inputs,
-- it can be better to wrap over the entire -, + range, like RESIZE_NUM() does, because if the final adder
-- sum again fits in w bits, then any wrapping effects for intermediate sums will cancel in the end sum.
-- If the number of bit w is sufficient to have no resize overflow, then RESIZE() = RESIZE_NUM(). In an
-- application overflow should be avoided anyway, so then using either RESIZE() or RESIZE_NUM() is fine.
-- When w keeps or increases the data width then the values do not change (of course). When w reduces the
-- data width then overflow can occur in an application, so then use:
-- * RESIZE() to preserve the sign,
-- * RESIZE_NUM() to wrap similar for SIGNED as for UNSIGNED,
-- * common_resize.vhd to clip the overflow (and use symmetrical -, + clipping to avoid introducing DC bias).
-- The resize functions and component are verified by tb_tb_resize.vhd.
-- Conclusion:
-- 1) Keep original RESIZE_NUM(), so resize by selecting the w LSbits for both SIGNED and UNSIGNED.
-- 2) For applications without overflow RESIZE() = RESIZE_NUM() = common_resize.
-- 3) For applications with overflow choose to use RESIZE_NUM(), because it wraps similar for SIGNED as
-- for UNSIGNED (because both keep the w LSbits), instead of behaving differently for SIGNED like
-- RESIZE() does (keeping the MSbit and the w-1 LSbits). The wrapping of RESIZE_NUM() preserves the
-- capability of recovering from intermediate overflow in a summator, which can be beneficial for e.g.
-- a beamformer.
FUNCTION RESIZE_NUM( u : UNSIGNED; w : NATURAL) RETURN UNSIGNED; -- left extend with '0' or keep LS part (same as RESIZE for UNSIGNED)
FUNCTION RESIZE_NUM( s : SIGNED; w : NATURAL) RETURN SIGNED; -- extend sign bit or keep LS part
FUNCTION RESIZE_UVEC(sl : STD_LOGIC; w : NATURAL) RETURN STD_LOGIC_VECTOR; -- left extend with '0' into slv
......@@ -1975,8 +2001,8 @@ PACKAGE BODY common_pkg IS
FUNCTION RESIZE_NUM(u : UNSIGNED; w : NATURAL) RETURN UNSIGNED IS
BEGIN
-- left extend with '0' or keep LS part (same as RESIZE for UNSIGNED)
RETURN RESIZE(u, w);
-- left extend with '0' or remove MSbits and keep LS part (= u[w-1:0])
RETURN RESIZE(u, w); -- same as RESIZE for UNSIGNED
END;
FUNCTION RESIZE_NUM(s : SIGNED; w : NATURAL) RETURN SIGNED IS
......@@ -1985,7 +2011,12 @@ PACKAGE BODY common_pkg IS
IF w>s'LENGTH THEN
RETURN RESIZE(s, w); -- extend sign bit
ELSE
RETURN SIGNED(RESIZE(UNSIGNED(s), w)); -- keep LSbits (= vec[w-1:0])
-- RESIZE() wraps between -, 0 for negative and 0, + for positive, so it keeps the sign and w-1 LSbits
-- RESIZE_NUM() removes MSbits, so it wraps from + to - and from - to +, and it keeps the w LSbits
-- remove MSbits and keep LS part (= s[w-1:0])
-- use RESIZE(UNSIGNED()) rather than s[w-1:0] to be independent of RANGE of s
RETURN SIGNED(RESIZE(UNSIGNED(s), w));
END IF;
END;
......
......@@ -24,16 +24,28 @@ USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
USE work.common_pkg.ALL;
-- Purpose: Test bench for common_resize.vhd
-- Purpose: Test bench for common_resize.vhd and for RESIZE_NUM() from
-- common_pkg.vhd
-- Usage:
-- > do wave_resize.do
-- > run 500 ns
-- . Verify clipping using c_clip=TRUE and c_clip_symmetric=TRUE or FALSE
-- . Verify wrapping using c_clip=FALSE
-- or use:
-- > as 5
-- and manually set the signed data output signals to radix decimal
-- > run -a
-- . Verify clipping using g_clip=TRUE and g_clip_symmetric=TRUE or FALSE
-- . Verify wrapping using g_clip=FALSE
-- . Observe reg_dat with respect to out_sdat, out_sovr for signed
-- . Observe reg_dat with respect to out_udat, out_uovr for unsigned
ENTITY tb_resize IS
GENERIC (
-- Supported for RESIZE_NUM() and common_resize.vhd
g_in_dat_w : NATURAL := 5;
g_out_dat_w : NATURAL := 3;
-- Only supported for common_resize.vhd
g_clip : BOOLEAN := TRUE;
g_clip_symmetric : BOOLEAN := TRUE
);
END tb_resize;
ARCHITECTURE tb OF tb_resize IS
......@@ -43,28 +55,38 @@ ARCHITECTURE tb OF tb_resize IS
CONSTANT c_pipeline_input : NATURAL := 0;
CONSTANT c_pipeline_output : NATURAL := 1;
CONSTANT c_pipeline : NATURAL := c_pipeline_input + c_pipeline_output;
CONSTANT c_clip : BOOLEAN := TRUE;
CONSTANT c_clip_symmetric : BOOLEAN := TRUE;
CONSTANT c_in_dat_w : NATURAL := 5;
CONSTANT c_out_dat_w : NATURAL := 3;
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);
SIGNAL out_sdat : 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_sovr : STD_LOGIC;
SIGNAL out_uovr : STD_LOGIC;
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL clk : STD_LOGIC := '1';
SIGNAL rst : STD_LOGIC;
CONSTANT c_init : STD_LOGIC_VECTOR(in_dat'RANGE) := (OTHERS=>'0');
-- Test 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 reg_sdat : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0);
SIGNAL lowrange_sdat : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); -- keep LSbits or sign extend
SIGNAL resize_num_sdat : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); -- using RESIZE_NUM() from common_pkg.vhd
SIGNAL resize_sdat : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); -- using RESIZE() from IEEE.NUMERIC_STD
SIGNAL out_sdat : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); -- using common_resize.vhd
SIGNAL out_sovr : STD_LOGIC; -- overflow for out_sdat
-- Unsigned output data, view as radix unsigned in Wave window
SIGNAL reg_udat : STD_LOGIC_VECTOR(g_in_dat_w-1 DOWNTO 0);
SIGNAL lowrange_udat : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); -- keep LSbits
SIGNAL resize_num_udat : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); -- using RESIZE_NUM() from common_pkg.vhd
SIGNAL resize_udat : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); -- using RESIZE() from IEEE.NUMERIC_STD
SIGNAL out_udat : STD_LOGIC_VECTOR(g_out_dat_w-1 DOWNTO 0); -- using common_resize.vhd
SIGNAL out_uovr : STD_LOGIC; -- overflow for out_udat
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL clk : STD_LOGIC := '1';
SIGNAL rst : STD_LOGIC;
CONSTANT c_init : STD_LOGIC_VECTOR(in_dat'RANGE) := (OTHERS=>'0');
CONSTANT g_clip_umax : NATURAL := 2**g_out_dat_w - 1;
CONSTANT g_clip_smax : NATURAL := 2**(g_out_dat_w - 1) - 1;
CONSTANT g_clip_smin : INTEGER := -2**(g_out_dat_w - 1);
BEGIN
-- Stimuli
......@@ -99,35 +121,54 @@ BEGIN
END IF;
END PROCESS;
-- Delay input as much as DUT output
in_vec <= in_val & in_dat;
u_pipe : ENTITY work.common_pipeline
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
)
PORT MAP (
clk => clk,
in_dat => in_vec,
out_dat => reg_vec
);
reg_val <= reg_vec(c_in_dat_w);
reg_dat <= reg_vec(c_in_dat_w-1 DOWNTO 0);
-- Delay input as much as DUT output, assume c_pipeline = 1
reg_val <= in_val WHEN rising_edge(clk);
reg_dat <= in_dat WHEN rising_edge(clk);
reg_sdat <= reg_dat;
reg_udat <= reg_dat;
gen_extend_lowrange : IF g_out_dat_w >= g_in_dat_w GENERATE
p_extend_lowrange : PROCESS(clk)
BEGIN
IF rising_edge(clk) THEN
-- Extend MSbits for "SIGNED" and "UNSIGNED"
lowrange_sdat <= (OTHERS => in_dat(g_in_dat_w-1)); -- Extend MSbit for "SIGNED"
lowrange_udat <= (OTHERS => '0'); -- Extend '0' for "UNSIGNED"
lowrange_sdat(g_in_dat_w-1 DOWNTO 0) <= in_dat;
lowrange_udat(g_in_dat_w-1 DOWNTO 0) <= in_dat;
END IF;
END PROCESS;
END GENERATE;
gen_reduce_lowrange : IF g_out_dat_w < g_in_dat_w GENERATE
p_reduce_lowrange : PROCESS(clk)
BEGIN
IF rising_edge(clk) THEN
-- Remove MSbits for "SIGNED" and "UNSIGNED"
lowrange_sdat <= in_dat(g_out_dat_w-1 DOWNTO 0);
lowrange_udat <= in_dat(g_out_dat_w-1 DOWNTO 0);
END IF;
END PROCESS;
END GENERATE;
-- IEEE RESIZE() for "SIGNED" and "UNSIGNED"
resize_sdat <= STD_LOGIC_VECTOR(RESIZE( SIGNED(in_dat), g_out_dat_w)) WHEN rising_edge(clk);
resize_udat <= STD_LOGIC_VECTOR(RESIZE(UNSIGNED(in_dat), g_out_dat_w)) WHEN rising_edge(clk);
-- RESIZE_NUM() from common_pkg.vhd for "SIGNED" and "UNSIGNED"
resize_num_sdat <= STD_LOGIC_VECTOR(RESIZE_NUM( SIGNED(in_dat), g_out_dat_w)) WHEN rising_edge(clk);
resize_num_udat <= STD_LOGIC_VECTOR(RESIZE_NUM(UNSIGNED(in_dat), g_out_dat_w)) WHEN rising_edge(clk);
-- DUT for "SIGNED"
u_s_resize : ENTITY work.common_resize
GENERIC MAP (
g_representation => "SIGNED",
g_clip => c_clip,
g_clip_symmetric => c_clip_symmetric,
g_clip => g_clip,
g_clip_symmetric => g_clip_symmetric,
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,
......@@ -140,12 +181,12 @@ BEGIN
u_u_resize : ENTITY work.common_resize
GENERIC MAP (
g_representation => "UNSIGNED",
g_clip => c_clip,
g_clip_symmetric => c_clip_symmetric,
g_clip => g_clip,
g_clip_symmetric => g_clip_symmetric,
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,
......@@ -155,16 +196,49 @@ BEGIN
);
-- Verification
p_verify_increase : PROCESS
p_verify : PROCESS
BEGIN
WAIT UNTIL rising_edge(clk);
IF reg_val = '1' AND c_in_dat_w<=c_out_dat_w THEN
IF SIGNED(out_sdat) /= SIGNED(reg_dat) THEN
REPORT "Mismatch between signed resize to larger vector" SEVERITY ERROR;
END IF;
IF UNSIGNED(out_udat) /= UNSIGNED(reg_dat) THEN
REPORT "Mismatch between unsigned resize to larger vector" SEVERITY ERROR;
IF reg_val = '1' THEN
IF g_in_dat_w<=g_out_dat_w THEN
-- For extended width expected value is same as input value
ASSERT SIGNED( lowrange_sdat) = SIGNED(reg_dat) REPORT "Wrong extended lowrange_sdat" SEVERITY ERROR;
ASSERT UNSIGNED( lowrange_udat) = UNSIGNED(reg_dat) REPORT "Wrong extended lowrange_udat" SEVERITY ERROR;
ASSERT SIGNED( resize_sdat) = SIGNED(reg_dat) REPORT "Wrong extended resize_sdat" SEVERITY ERROR;
ASSERT UNSIGNED( resize_udat) = UNSIGNED(reg_dat) REPORT "Wrong extended resize_udat" SEVERITY ERROR;
ASSERT SIGNED(resize_num_sdat) = SIGNED(reg_dat) REPORT "Wrong extended resize_num_sdat" SEVERITY ERROR;
ASSERT UNSIGNED(resize_num_udat) = UNSIGNED(reg_dat) REPORT "Wrong extended resize_num_udat" SEVERITY ERROR;
ASSERT SIGNED( out_sdat) = SIGNED(reg_dat) REPORT "Wrong extended out_sdat" SEVERITY ERROR;
ASSERT UNSIGNED( out_udat) = UNSIGNED(reg_dat) REPORT "Wrong extended out_udat" SEVERITY ERROR;
ELSE
-- For reduced width compare unsigned with lowrange
ASSERT UNSIGNED( resize_udat) = UNSIGNED(lowrange_udat) REPORT "Wrong wrapped resize_udat" SEVERITY ERROR;
ASSERT UNSIGNED(resize_num_udat) = UNSIGNED(lowrange_udat) REPORT "Wrong wrapped resize_num_udat" SEVERITY ERROR;
ASSERT UNSIGNED( out_udat) = UNSIGNED(lowrange_udat) OR
UNSIGNED( out_udat) = g_clip_umax REPORT "Wrong clipped out_udat" SEVERITY ERROR;
-- For reduced width compare signed with lowrange
-- . no need to verify RESIZE(), because it is part of IEEE.NUMERIC_STD
-- . verify RESIZE_NUM() below for all g_out_dat_w
-- . verify common_resize here
IF g_clip THEN
IF g_clip_symmetric THEN
ASSERT (SIGNED(out_sdat) = SIGNED(lowrange_sdat) OR SIGNED(out_sdat) = -g_clip_smax OR SIGNED(out_sdat) = g_clip_smax) AND
SIGNED(out_sdat) /= g_clip_smin AND
SIGNED(out_sdat) /= -g_clip_smin REPORT "Wrong clipped symmetrical out_sdat" SEVERITY ERROR;
ELSE
ASSERT (SIGNED(out_sdat) = SIGNED(lowrange_sdat) OR SIGNED(out_sdat) = g_clip_smin OR SIGNED(out_sdat) = g_clip_smax)
REPORT "Wrong clipped out_sdat" SEVERITY ERROR;
END IF;
ELSE
ASSERT SIGNED(out_sdat) = SIGNED(lowrange_sdat) REPORT "Wrong wrapped out_sdat" SEVERITY ERROR;
END IF;
END IF;
-- RESIZE_NUM() in common_pkg.vhd is always equivalent to lowrange
ASSERT SIGNED(resize_num_sdat) = SIGNED(lowrange_sdat) REPORT "Wrong resize_num_sdat /= lowrange_sdat" SEVERITY ERROR;
ASSERT UNSIGNED(resize_num_udat) = UNSIGNED(lowrange_udat) REPORT "Wrong resize_num_udat /= lowrange_udat" SEVERITY ERROR;
END IF;
END PROCESS;
......
-- --------------------------------------------------------------------------
-- 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, 26 okt 2021
-- Purpose: Multi tb for common_resize.vhd and RESIZE_NUM() in common_pkg.vhd
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
ENTITY tb_tb_resize IS
END tb_tb_resize;
ARCHITECTURE tb OF tb_tb_resize 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_resize.vhd
-- g_in_dat_w : NATURAL := 5;
-- g_out_dat_w : NATURAL := 3;
-- -- Only supported for common_resize.vhd
-- g_clip : BOOLEAN := TRUE;
-- g_clip_symmetric : BOOLEAN := TRUE;
u_wrap_extend_w : ENTITY work.tb_resize GENERIC MAP (3, 5, FALSE, FALSE);
u_wrap_keep_w : ENTITY work.tb_resize GENERIC MAP (5, 5, FALSE, FALSE);
u_wrap_reduce_w : ENTITY work.tb_resize GENERIC MAP (5, 3, FALSE, FALSE);
u_clip_extend_w : ENTITY work.tb_resize GENERIC MAP (3, 5, TRUE, FALSE);
u_clip_keep_w : ENTITY work.tb_resize GENERIC MAP (5, 5, TRUE, FALSE);
u_clip_reduce_w : ENTITY work.tb_resize GENERIC MAP (5, 3, TRUE, FALSE);
u_clip_sym_extend_w : ENTITY work.tb_resize GENERIC MAP (3, 5, TRUE, TRUE);
u_clip_sym_keep_w : ENTITY work.tb_resize GENERIC MAP (5, 5, TRUE, TRUE);
u_clip_sym_reduce_w : ENTITY work.tb_resize GENERIC MAP (5, 3, TRUE, TRUE);
END tb;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment