diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index 750cd5e6bebe818c7667deffa69778c34d2f8426..e81085087bca4e5a67920bea7e00f08decda7f2d 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -372,6 +372,9 @@ PACKAGE common_pkg IS FUNCTION RESIZE_UVEC_32(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- = RESIZE_UVEC() with w=32 for t_slv_32_arr slv elements FUNCTION RESIZE_SVEC_32(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; -- = RESIZE_SVEC() with w=32 for t_slv_32_arr slv elements + + -- Negate vec, but avoid overflow by forcing -min to +max. Use w <= vec'LENGTH. + FUNCTION NEGATE_SVEC(vec : STD_LOGIC_VECTOR; w : INTEGER) RETURN STD_LOGIC_VECTOR; FUNCTION INCR_UVEC(vec : STD_LOGIC_VECTOR; dec : INTEGER) RETURN STD_LOGIC_VECTOR; FUNCTION INCR_UVEC(vec : STD_LOGIC_VECTOR; dec : UNSIGNED) RETURN STD_LOGIC_VECTOR; @@ -1754,6 +1757,22 @@ PACKAGE BODY common_pkg IS RETURN RESIZE_SVEC(vec, 32); END; + -- Negate vec, but avoid overflow by forcing -min to +max. Use w <= vec'LENGTH. + FUNCTION NEGATE_SVEC(vec : STD_LOGIC_VECTOR; w : INTEGER) RETURN STD_LOGIC_VECTOR IS + CONSTANT c_max : INTEGER := 2**(w-1)-1; + CONSTANT c_min : INTEGER := -2**(w-1); + CONSTANT c_vec_w : NATURAL := vec'LENGTH; + VARIABLE v_vec : STD_LOGIC_VECTOR(c_vec_w-1 DOWNTO 0) := vec; -- independent of vec'RANGE + VARIABLE v_val : STD_LOGIC_VECTOR(w-1 DOWNTO 0); + BEGIN + v_val := v_vec(w-1 DOWNTO 0); -- operate on width w and resize to c_vec_w for return + IF SIGNED(v_val) = c_min THEN + RETURN STD_LOGIC_VECTOR(TO_SIGNED(c_max, c_vec_w)); -- most negative becomes most positive + ELSE + RETURN STD_LOGIC_VECTOR(RESIZE_NUM(-SIGNED(v_val), c_vec_w)); -- negate by multiplying by -1 + END IF; + END; + FUNCTION INCR_UVEC(vec : STD_LOGIC_VECTOR; dec : INTEGER) RETURN STD_LOGIC_VECTOR IS VARIABLE v_dec : INTEGER; BEGIN diff --git a/libraries/dsp/si/hdllib.cfg b/libraries/dsp/si/hdllib.cfg new file mode 100755 index 0000000000000000000000000000000000000000..b9c5b293a1aec62f6d333dd08119ea202fc3ca15 --- /dev/null +++ b/libraries/dsp/si/hdllib.cfg @@ -0,0 +1,21 @@ +hdl_lib_name = si +hdl_library_clause_name = si_lib +hdl_lib_uses_synth = common dp +hdl_lib_uses_sim = +hdl_lib_technology = + +synth_files = + src/vhdl/si.vhd + +test_bench_files = + tb/vhdl/tb_si.vhd + +regression_test_vhdl = + tb/vhdl/tb_si.vhd + + +[modelsim_project_file] + + +[quartus_project_file] + diff --git a/libraries/dsp/si/src/vhdl/si.vhd b/libraries/dsp/si/src/vhdl/si.vhd new file mode 100755 index 0000000000000000000000000000000000000000..14d61189ce30513a56070e74e5c0680a2387f789 --- /dev/null +++ b/libraries/dsp/si/src/vhdl/si.vhd @@ -0,0 +1,108 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2020 +-- 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 +-- Purpose: Spectral inversion. +-- Description: +-- In the even Nyquist zones the sampled spectrum gets flipped in frequency. +-- This flip can be compensated for by a spectral inversion (SI). When +-- enabled the SI multiplies the input samples by (-1)**n = +1, -1, ..., +-- where n = 0 is the first sample in the FFT block. For more information +-- see section 4.19 in LOFAR_ASTRON_SDD_018_RSP_Firmware_DD.pdf. +-- Remark: +-- . Ported from LOFAR1 rsp. Then rewrote code to use t_dp_sosi. +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; + +ENTITY si IS + GENERIC ( + g_pipeline : NATURAL := 1; -- 0 for wires, 1 for output pipeline + g_dat_w : NATURAL := 18 + ); + PORT ( + in_sosi : IN t_dp_sosi; + out_sosi : OUT t_dp_sosi; + si_en : IN STD_LOGIC; + clk : IN STD_LOGIC; + rst : IN STD_LOGIC + ); +END si; + +ARCHITECTURE rtl OF si IS + + SIGNAL plus : STD_LOGIC; + SIGNAL plus_reg : STD_LOGIC; + SIGNAL si_plus : STD_LOGIC; + SIGNAL si_sosi : t_dp_sosi; + +BEGIN + + p_reg : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + plus_reg <= '1'; + ELSIF rising_edge(clk) THEN + plus_reg <= plus; + END IF; + END PROCESS; + + -- Control -1**n to start with +1 at sop and then toggle at every valid + p_si_control : PROCESS (plus_reg, in_sosi) + BEGIN + plus <= plus_reg; + IF in_sosi.sop = '1' THEN + plus <= '1'; + ELSIF in_sosi.valid = '1' THEN + plus <= NOT plus_reg; + END IF; + END PROCESS; + + -- Use SI when enabled, else pass on input + si_plus <= plus WHEN si_en = '1' ELSE '1'; + + si_data : PROCESS (si_plus, in_sosi) + BEGIN + si_sosi <= in_sosi; + IF si_plus = '0' THEN + si_sosi.data <= NEGATE_SVEC(in_sosi.data, g_dat_w); + si_sosi.re <= NEGATE_SVEC(in_sosi.re, g_dat_w); + si_sosi.im <= NEGATE_SVEC(in_sosi.im, g_dat_w); + END IF; + END PROCESS; + + -- Output + u_pipeline : ENTITY dp_lib.dp_pipeline + GENERIC MAP ( + g_pipeline => g_pipeline + ) + PORT MAP ( + rst => rst, + clk => clk, + snk_in => si_sosi, + src_out => out_sosi + ); + +END rtl; diff --git a/libraries/dsp/si/tb/vhdl/tb_si.vhd b/libraries/dsp/si/tb/vhdl/tb_si.vhd new file mode 100755 index 0000000000000000000000000000000000000000..462b6b6a8a1b9a671e1b3ed8852ea70431f384d8 --- /dev/null +++ b/libraries/dsp/si/tb/vhdl/tb_si.vhd @@ -0,0 +1,183 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2020 +-- 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 +-- Purpose: Spectral inversion. +-- Description: +-- Test bench for si.vhd. +-- Remark: +-- . Ported from LOFAR1 rsp. Made the tb self-stopping and self-checking. + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; + +ENTITY tb_si IS +END tb_si; + +ARCHITECTURE tb OF tb_si IS + + CONSTANT c_clk_period : TIME := 10 ns; + + CONSTANT c_dat_w : NATURAL := 5; + CONSTANT c_max : INTEGER := 2**(c_dat_w-1)-1; + CONSTANT c_min : INTEGER := -2**(c_dat_w-1); + CONSTANT c_block_size : NATURAL := 9; + + SIGNAL in_sosi : t_dp_sosi; + SIGNAL out_sosi : t_dp_sosi; + + SIGNAL in_dat : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0) := (OTHERS => '0'); + SIGNAL in_val : STD_LOGIC; + SIGNAL in_sop : STD_LOGIC; + SIGNAL in_sync : STD_LOGIC; + SIGNAL out_dat : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL out_val : STD_LOGIC; + SIGNAL out_sop : STD_LOGIC; + SIGNAL out_sync : STD_LOGIC; + SIGNAL si_en : STD_LOGIC; + SIGNAL clk : STD_LOGIC := '1'; + SIGNAL rst : STD_LOGIC; + SIGNAL tb_end : STD_LOGIC := '0'; + + SIGNAL toggle : STD_LOGIC; + SIGNAL clip_even : STD_LOGIC; + SIGNAL clip_odd : STD_LOGIC; + +BEGIN + + rst <= '1', '0' AFTER c_clk_period; + clk <= NOT(clk) OR tb_end AFTER c_clk_period/2; + + u_si : ENTITY work.si + GENERIC MAP ( + g_pipeline => 0, + g_dat_w => c_dat_w + ) + PORT MAP( + in_sosi => in_sosi, + out_sosi => out_sosi, + si_en => si_en, + clk => clk, + rst => rst + ); + + -- wires + in_sosi.sync <= in_sync; + in_sosi.sop <= in_sop; + in_sosi.valid <= in_val; + in_sosi.data <= RESIZE_DP_SDATA(in_dat); + in_sosi.re <= TO_DP_DSP_DATA(0); + in_sosi.im <= TO_DP_DSP_DATA(0); + + out_sync <= out_sosi.sync; + out_sop <= out_sosi.sop; + out_val <= out_sosi.valid; + out_dat <= out_sosi.data(c_dat_w-1 DOWNTO 0); + + -- Create in_dat with equal value per pair + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + in_dat <= (OTHERS => '0'); + toggle <= '0'; + ELSIF rising_edge(clk) THEN + IF in_val='1' THEN + IF toggle='1' THEN + in_dat <= INCR_UVEC(in_dat, 1); + END IF; + toggle <= NOT toggle; + END IF; + END IF; + END PROCESS; + + p_stimuli : PROCESS + BEGIN + si_en <= '1'; + in_sop <= '0'; + in_sync <= '0'; + in_val <= '0'; + WAIT FOR 10*c_clk_period; + + -- pulse in_sync, to check that it is passed on + -- pulse sop and continue with valid data + in_sync <= '1'; + in_sop <= '1'; + in_val <= '1'; + WAIT FOR c_clk_period; + in_sync <= '0'; + in_sop <= '0'; + WAIT FOR c_block_size*c_clk_period; + + -- insert some valid low cycles + in_val <= '0'; + WAIT FOR 3*c_clk_period; + in_val <= '1'; + + -- some more blocks + FOR I IN 0 TO 15 LOOP + in_sop <= '1'; + WAIT FOR c_clk_period; + in_sop <= '0'; + WAIT FOR c_block_size*c_clk_period; + END LOOP; + + tb_end <= '1'; + WAIT; + END PROCESS; + + p_verify : PROCESS + VARIABLE v_even : INTEGER; + VARIABLE v_odd : INTEGER; + VARIABLE v_clip_even : STD_LOGIC; + VARIABLE v_clip_odd : STD_LOGIC; + BEGIN + -- verify per pair + WAIT FOR c_clk_period; + proc_common_wait_until_high(clk, out_val); + v_even := TO_SINT(out_dat); + WAIT FOR c_clk_period; + proc_common_wait_until_high(clk, out_val); + v_odd := TO_SINT(out_dat); + -- identify clip wrap of -c_min to +c_max + IF v_even = c_max AND v_odd = c_min THEN v_clip_even := '1'; ELSE v_clip_even := '0'; END IF; + IF v_even = c_min AND v_odd = c_max THEN v_clip_odd := '1'; ELSE v_clip_odd := '0'; END IF; + clip_even <= v_clip_even; -- show in wave window (only cycle late) + clip_odd <= v_clip_odd; -- show in wave window (only cycle late) + -- compare pair + IF tb_end = '0' THEN + IF v_even /= -v_odd THEN + IF NOT (v_clip_even = '1') THEN + IF NOT (v_clip_odd = '1') THEN + REPORT "Wrong negate value" SEVERITY ERROR; + END IF; + END IF; + END IF; + ELSE + WAIT; + END IF; + END PROCESS; + +END tb; +