diff --git a/libraries/base/common/hdllib.cfg b/libraries/base/common/hdllib.cfg index 6a8deef3b0251bc4dc8d5a8401c9b93e470c8ff9..22c74c8f3c500407dc23967c1eb4e18e1388e999 100644 --- a/libraries/base/common/hdllib.cfg +++ b/libraries/base/common/hdllib.cfg @@ -45,6 +45,7 @@ synth_files = src/vhdl/common_ddio_in.vhd src/vhdl/common_ddio_out.vhd + src/vhdl/common_create_strobes_from_valid.vhd src/vhdl/common_wideband_data_scope.vhd src/vhdl/common_iobuf_in.vhd #$UNB/Firmware/modules/common/src/vhdl/common_iobuf_in_a_stratix4.vhd @@ -195,7 +196,8 @@ test_bench_files = tb/vhdl/tb_common_to_sreal.vhd tb/vhdl/tb_delta_cycle_demo.vhd tb/vhdl/tb_mms_common_variable_delay.vhd - + tb/vhdl/tb_common_create_strobes_from_valid.vhd + tb/vhdl/tb_tb_resize.vhd tb/vhdl/tb_tb_round.vhd tb/vhdl/tb_tb_common_add_sub.vhd @@ -209,6 +211,7 @@ test_bench_files = tb/vhdl/tb_tb_common_rl.vhd tb/vhdl/tb_tb_common_rl_register.vhd tb/vhdl/tb_tb_common_transpose.vhd + tb/vhdl/tb_tb_common_create_strobes_from_valid.vhd regression_test_vhdl = tb/vhdl/tb_common_fifo_rd.vhd @@ -236,7 +239,8 @@ regression_test_vhdl = tb/vhdl/tb_tb_common_rl.vhd tb/vhdl/tb_tb_common_rl_register.vhd tb/vhdl/tb_tb_common_transpose.vhd - + tb/vhdl/tb_tb_common_create_strobes_from_valid.vhd + [modelsim_project_file] modelsim_copy_files = data data diff --git a/libraries/base/common/src/vhdl/common_create_strobes_from_valid.vhd b/libraries/base/common/src/vhdl/common_create_strobes_from_valid.vhd new file mode 100644 index 0000000000000000000000000000000000000000..2474c75da3e6754b73419668b1d3d4e04bec78d4 --- /dev/null +++ b/libraries/base/common/src/vhdl/common_create_strobes_from_valid.vhd @@ -0,0 +1,160 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2022 +-- 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: Eric Kooistra +-- Purpose: Create sync interval by counting input valids +-- Description: +-- +-- The first out_sync is created at the first inval after rst release. The +-- subsequent out_sync are created every m in_val, at the start of a block. +-- +-- n = g_nof_clk_per_block +-- m = g_nof_clk_per_sync +-- _____________________________________________________ +-- in_val __| +-- +-- blk_cnt | 0 | 1 | 2 | 3 | 4 | +-- +-- val_cnt |0 |n |n*2 |n*3 - m | |0 +-- _ _ _ +-- out_sync __| |_____________________| |_______________________| |_ +-- _____________________________________________________ +-- out_val __| +-- _ _ _ _ _ _ +-- out_sop __| |_______| |_______| |_______| |_______| |_______| |_ +-- _ _ _ _ _ +-- out_eop __________| |_______| |_______| |_______| |_______| |___ +-- +-- Remark: +-- . Use VHDL coding template from: +-- https://support.astron.nl/confluence/display/SBe/VHDL+design+patterns+for+RTL+coding +-- . The out_sop and out_eop are created as well, for reference. +-- . The out_sync1 for LOFAR1 style is only avaiable if g_pipeline = TRUE, +-- because the pipeline is needed to let the out_sync1 preceed the +-- out_sop and other strobes. + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; +USE work.common_pkg.ALL; + +ENTITY common_create_strobes_from_valid IS + GENERIC ( + g_pipeline : BOOLEAN := TRUE; + g_nof_clk_per_sync : NATURAL := 200*10**6; + g_nof_clk_per_block : NATURAL := 1024 + ); + PORT ( + rst : IN STD_LOGIC := '0'; + clk : IN STD_LOGIC; + in_val : IN STD_LOGIC; + out_val : OUT STD_LOGIC; + out_sop : OUT STD_LOGIC; + out_eop : OUT STD_LOGIC; + out_sync : OUT STD_LOGIC; -- DP style: sync at sop + out_sync1 : OUT STD_LOGIC -- LOFAR1 style: sync before sop + ); +END common_create_strobes_from_valid; + + +ARCHITECTURE rtl OF common_create_strobes_from_valid IS + + TYPE t_state IS RECORD -- function state registers + val_cnt : NATURAL RANGE 0 TO g_nof_clk_per_sync-1; + blk_cnt : NATURAL RANGE 0 TO g_nof_clk_per_block-1; + END RECORD; + + TYPE t_outputs IS RECORD -- copy of entity outputs + out_val : STD_LOGIC; + out_sop : STD_LOGIC; + out_eop : STD_LOGIC; + out_sync : STD_LOGIC; + END RECORD; + + CONSTANT c_state_rst : t_state := (val_cnt => 0, blk_cnt => 0); + CONSTANT c_outputs_rst : t_outputs := ('0', '0', '0', '0'); + + SIGNAL q : t_state := c_state_rst; -- stored state with latency one + SIGNAL d : t_state := c_state_rst; -- zero latency state + + SIGNAL o : t_outputs := c_outputs_rst; -- zero latency outputs + SIGNAL p : t_outputs := c_outputs_rst; -- pipelined outputs + +BEGIN + + -- p_state + q <= d WHEN rising_edge(clk); + + p_comb : PROCESS(rst, q, in_val) + VARIABLE v : t_state; + BEGIN + -- Default + v := q; + o.out_val <= in_val; + o.out_sop <= '0'; + o.out_eop <= '0'; + o.out_sync <= '0'; + + -- Function + IF in_val = '1' THEN + -- maintain in_val counters + IF q.val_cnt >= g_nof_clk_per_sync-1 THEN + v.val_cnt := 0; + ELSE + v.val_cnt := v.val_cnt + 1; + END IF; + IF q.blk_cnt >= g_nof_clk_per_block-1 THEN + v.blk_cnt := 0; + ELSE + v.blk_cnt := v.blk_cnt + 1; + END IF; + -- create out_sop at start of block + IF q.blk_cnt = 0 THEN + o.out_sop <= '1'; + END IF; + -- create out_eop at end of block + IF q.blk_cnt = g_nof_clk_per_block-1 THEN + o.out_eop <= '1'; + END IF; + -- create out_sync at start of first block of sync interval + IF q.blk_cnt = 0 AND q.val_cnt < g_nof_clk_per_block THEN + o.out_sync <= '1'; + END IF; + END IF; + + -- Reset + IF rst = '1' THEN + v := c_state_rst; + END IF; + + -- Result + d <= v; + + END PROCESS; + + -- Output + p <= o WHEN rising_edge(clk); + + out_val <= o.out_val WHEN g_pipeline = FALSE ELSE p.out_val; + out_sop <= o.out_sop WHEN g_pipeline = FALSE ELSE p.out_sop; + out_eop <= o.out_eop WHEN g_pipeline = FALSE ELSE p.out_eop; + out_sync <= o.out_sync WHEN g_pipeline = FALSE ELSE p.out_sync; + + out_sync1 <= '0' WHEN g_pipeline = FALSE ELSE o.out_sync; + +END rtl; diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index 791cfb39682d6e1fd94b0469e18c4d6582ae0e1d..6153caf04a8c55f7cfdb2b77674b7407161229a4 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -2177,9 +2177,8 @@ PACKAGE BODY common_pkg IS END; FUNCTION INCR_SVEC(vec : STD_LOGIC_VECTOR; dec : INTEGER) RETURN STD_LOGIC_VECTOR IS - VARIABLE v_dec : INTEGER; BEGIN - RETURN STD_LOGIC_VECTOR(SIGNED(vec) + v_dec); -- uses function "+" (L : SIGNED, R : INTEGER) + RETURN STD_LOGIC_VECTOR(SIGNED(vec) + dec); -- uses function "+" (L : SIGNED, R : INTEGER) END; FUNCTION INCR_SVEC(vec : STD_LOGIC_VECTOR; dec : SIGNED) RETURN STD_LOGIC_VECTOR IS diff --git a/libraries/base/common/tb/vhdl/tb_common_create_strobes_from_valid.vhd b/libraries/base/common/tb/vhdl/tb_common_create_strobes_from_valid.vhd new file mode 100644 index 0000000000000000000000000000000000000000..c29143fc533451d52612349f67b33e05fd4d2626 --- /dev/null +++ b/libraries/base/common/tb/vhdl/tb_common_create_strobes_from_valid.vhd @@ -0,0 +1,142 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2022 +-- 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: Eric Kooistra +-- Purpose: Self checking and self-stopping tb for common_create_strobes_from_valid.vhd +-- Usage: +-- > as 3 +-- > run -a + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; +USE work.common_pkg.ALL; +USE work.tb_common_pkg.ALL; + +ENTITY tb_common_create_strobes_from_valid IS + GENERIC ( + g_pipeline : BOOLEAN := FALSE; + g_in_val_gaps : BOOLEAN := TRUE; + g_nof_clk_per_sync : NATURAL := 10; + g_nof_clk_per_block : NATURAL := 5 + ); +END tb_common_create_strobes_from_valid; + +ARCHITECTURE tb OF tb_common_create_strobes_from_valid IS + + CONSTANT clk_period : TIME := 10 ns; + + CONSTANT c_nof_block_per_sync_max : NATURAL := ceil_div(g_nof_clk_per_sync, g_nof_clk_per_block); + CONSTANT c_nof_block_per_sync_min : NATURAL := g_nof_clk_per_sync / g_nof_clk_per_block; + CONSTANT c_fractional : BOOLEAN := c_nof_block_per_sync_min /= c_nof_block_per_sync_max; + + CONSTANT c_nof_sync : NATURAL := sel_a_b(c_fractional, g_nof_clk_per_block, 1) * 3; + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL rst : STD_LOGIC := '1'; + SIGNAL clk : STD_LOGIC := '0'; + SIGNAL in_val : STD_LOGIC := '0'; + SIGNAL out_val : STD_LOGIC; + SIGNAL out_sop : STD_LOGIC; + SIGNAL out_eop : STD_LOGIC; + SIGNAL out_sync : STD_LOGIC; + SIGNAL out_val_cnt : NATURAL := 0; + +BEGIN + + clk <= NOT clk OR tb_end AFTER clk_period/2; + + p_in_stimuli : PROCESS + BEGIN + rst <= '1'; + proc_common_wait_some_cycles(clk, 10); + rst <= '0'; + proc_common_wait_some_cycles(clk, 10); + FOR I IN 0 TO c_nof_sync-1 LOOP + FOR J IN 0 TO c_nof_block_per_sync_max-1 LOOP + FOR K IN 0 TO g_nof_clk_per_block-1 LOOP + IF g_in_val_gaps AND K = 0 THEN + in_val <= '0'; -- insert a one cycle gap + proc_common_wait_some_cycles(clk, 1); + END IF; + in_val <= '1'; + proc_common_wait_some_cycles(clk, 1); + END LOOP; + END LOOP; + END LOOP; + proc_common_wait_some_cycles(clk, g_nof_clk_per_sync*2); + tb_end <= '1'; + WAIT; + END PROCESS; + + out_val_cnt <= out_val_cnt + 1 WHEN rising_edge(clk) AND out_val = '1'; + + p_verify : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + IF out_val = '1' THEN + -- Verify out_eop + IF out_val_cnt MOD g_nof_clk_per_block = g_nof_clk_per_block-1 THEN + ASSERT out_eop = '1' REPORT "Missing out_eop." SEVERITY ERROR; + ELSE + ASSERT out_eop = '0' REPORT "Unexpected out_eop." SEVERITY ERROR; + END IF; + + -- Verify out_sop + IF out_val_cnt MOD g_nof_clk_per_block = 0 THEN + ASSERT out_sop = '1' REPORT "Missing out_sop." SEVERITY ERROR; + ELSE + ASSERT out_sop = '0' REPORT "Unexpected out_sop." SEVERITY ERROR; + END IF; + + -- Verify out_sync + IF out_val_cnt MOD g_nof_clk_per_block = 0 THEN + IF out_val_cnt MOD g_nof_clk_per_sync <= g_nof_clk_per_block-1 THEN + ASSERT out_sync = '1' REPORT "Missing out_sync." SEVERITY ERROR; + ELSE + ASSERT out_sync = '0' REPORT "Unexpected out_sync." SEVERITY ERROR; + END IF; + ELSE + ASSERT out_sync = '0' REPORT "Unexpected out_sync." SEVERITY ERROR; + END IF; + ELSE + -- Illegal strobe when out_val = '0' + ASSERT out_eop = '0' REPORT "Illegal out_eop." SEVERITY ERROR; + ASSERT out_sop = '0' REPORT "Illegal out_sop." SEVERITY ERROR; + ASSERT out_sync = '0' REPORT "Illegal out_sync." SEVERITY ERROR; + END IF; + END IF; + END PROCESS; + + u_in_sync : ENTITY work.common_create_strobes_from_valid + GENERIC MAP ( + g_pipeline => g_pipeline, + g_nof_clk_per_sync => g_nof_clk_per_sync, + g_nof_clk_per_block => g_nof_clk_per_block + ) + PORT MAP ( + rst => rst, + clk => clk, + in_val => in_val, + out_val => out_val, + out_sop => out_sop, + out_eop => out_eop, + out_sync => out_sync + ); + +END tb; diff --git a/libraries/base/common/tb/vhdl/tb_tb_common_create_strobes_from_valid.vhd b/libraries/base/common/tb/vhdl/tb_tb_common_create_strobes_from_valid.vhd new file mode 100644 index 0000000000000000000000000000000000000000..78ddd2e363d5b6171087eacc372db0a557b6a2ce --- /dev/null +++ b/libraries/base/common/tb/vhdl/tb_tb_common_create_strobes_from_valid.vhd @@ -0,0 +1,48 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2022 +-- 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: Eric Kooistra +-- Purpose: Multi tb for common_create_strobes_from_valid.vhd +-- Usage: +-- > as 3 +-- > run -a + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; + +ENTITY tb_tb_common_create_strobes_from_valid IS +END tb_tb_common_create_strobes_from_valid; + +ARCHITECTURE tb OF tb_tb_common_create_strobes_from_valid IS + SIGNAL tb_end : STD_LOGIC := '0'; -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end' +BEGIN + + -- g_pipeline : BOOLEAN := FALSE; + -- g_in_val_gaps : BOOLEAN := FALSE; + -- g_nof_clk_per_sync : NATURAL := 17; + -- g_nof_clk_per_block : NATURAL := 7 + + u_integer_interval : ENTITY work.tb_common_create_strobes_from_valid GENERIC MAP (FALSE, FALSE, 10, 5); + u_integer_interval_with_gaps : ENTITY work.tb_common_create_strobes_from_valid GENERIC MAP (FALSE, TRUE, 10, 5); + u_integer_interval_with_gaps_pipe : ENTITY work.tb_common_create_strobes_from_valid GENERIC MAP ( TRUE, TRUE, 10, 5); + u_fractional_interval : ENTITY work.tb_common_create_strobes_from_valid GENERIC MAP (FALSE, FALSE, 17, 7); + u_fractional_interval_with_gaps : ENTITY work.tb_common_create_strobes_from_valid GENERIC MAP (FALSE, TRUE, 17, 7); + u_fractional_interval_with_gaps_pipe : ENTITY work.tb_common_create_strobes_from_valid GENERIC MAP ( TRUE, TRUE, 17, 7); + +END tb; diff --git a/libraries/dsp/fft/hdllib.cfg b/libraries/dsp/fft/hdllib.cfg index dc054d8a66804073a951df9d113fbfe6e8300ef7..a75778467bbdea6b49354de1f431115a5b5dd9d8 100644 --- a/libraries/dsp/fft/hdllib.cfg +++ b/libraries/dsp/fft/hdllib.cfg @@ -6,7 +6,10 @@ hdl_lib_technology = synth_files = src/vhdl/fft_pkg.vhd - src/vhdl/fft_sepa.vhd + src/vhdl/fft_lfsr.vhd + src/vhdl/fft_switch.vhd + src/vhdl/fft_unswitch.vhd + src/vhdl/fft_sepa.vhd src/vhdl/fft_reorder_sepa_pipe.vhd src/vhdl/fft_sepa_wide.vhd src/vhdl/fft_r2_bf_par.vhd @@ -19,7 +22,8 @@ synth_files = test_bench_files = tb/vhdl/tb_fft_pkg.vhd tb/vhdl/tb_fft_functions.vhd - tb/vhdl/tb_fft_sepa.vhd + tb/vhdl/tb_fft_switch.vhd + tb/vhdl/tb_fft_sepa.vhd tb/vhdl/tb_fft_reorder_sepa_pipe.vhd tb/vhdl/tb_fft_r2_bf_par.vhd tb/vhdl/tb_fft_r2_pipe.vhd @@ -33,6 +37,7 @@ test_bench_files = tb/vhdl/tb_tb_fft_r2_wide.vhd regression_test_vhdl = + tb/vhdl/tb_fft_switch.vhd tb/vhdl/tb_tb_fft_r2_pipe.vhd tb/vhdl/tb_tb_fft_r2_par.vhd tb/vhdl/tb_tb_fft_r2_wide.vhd diff --git a/libraries/dsp/fft/src/vhdl/fft_lfsr.vhd b/libraries/dsp/fft/src/vhdl/fft_lfsr.vhd new file mode 100644 index 0000000000000000000000000000000000000000..56f5b5e453f9a2e009a05c2418f6890ee6aa22e3 --- /dev/null +++ b/libraries/dsp/fft/src/vhdl/fft_lfsr.vhd @@ -0,0 +1,92 @@ +------------------------------------------------------------------------------- +-- +-- 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: ported by E. Kooistra, original 2004 by W. Lubberhuizen / W. Poeisz +-- Purpose: Scramble quantization noise crosstalk between two real inputs +-- Description: Ported from LOFAR1, see readme_lofar1.txt +-- Remark: Copy from applications/lofar1/RSP/pft2/src/vhdl/pft_lfsr.vhd + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; + +ENTITY fft_lfsr IS + PORT ( + in_en : IN STD_LOGIC; + out_bit1 : OUT STD_LOGIC; + out_bit2 : OUT STD_LOGIC; + clk : IN STD_LOGIC; + rst : IN STD_LOGIC + ); +END fft_lfsr; + + +ARCHITECTURE rtl OF fft_lfsr IS + + -- uses preferred pair of pritive trinomials + -- x^41 + x^20 + 1 and x^41 + x^3 + 1 + -- see XAPP217 + + CONSTANT c_max : NATURAL := 41; + CONSTANT c1 : NATURAL := 20; + CONSTANT c2 : NATURAL := 3; + + SIGNAL s1 : STD_LOGIC_VECTOR(c_max-1 DOWNTO 0); + SIGNAL nxt_s1 : STD_LOGIC_VECTOR(c_max-1 DOWNTO 0); + + SIGNAL s2 : STD_LOGIC_VECTOR(c_max-1 DOWNTO 0); + SIGNAL nxt_s2 : STD_LOGIC_VECTOR(c_max-1 DOWNTO 0); + + +BEGIN + p_reg : PROCESS(rst,clk) + BEGIN + IF rst='1' THEN + s1 <= "01000101011101110101001011111000101100001"; + s2 <= "11011001000101001011011001110101100101100"; + ELSIF rising_edge(clk) THEN + s1 <= nxt_s1; + s2 <= nxt_s2; + END IF; + END PROCESS; + + out_bit1 <= s1(s1'HIGH); + out_bit2 <= s2(s2'HIGH); + + p_seed : PROCESS(in_en,s1,s2) + BEGIN + nxt_s1 <= s1; + nxt_s2 <= s2; + IF in_en='1' THEN + -- shift + nxt_s1(c_max-1 DOWNTO 1) <= s1(c_max-2 DOWNTO 0); + nxt_s2(c_max-1 DOWNTO 1) <= s2(c_max-2 DOWNTO 0); + + -- feedback 1 + nxt_s1(0) <= s1(c_max-1); + nxt_s2(0) <= s2(c_max-1); + + -- feedback 2 + nxt_s1(c1) <= s1(c_max-1) xor s1(c1-1); + nxt_s2(c2) <= s2(c_max-1) xor s2(c2-1); + END IF; + END PROCESS; + +end rtl; + diff --git a/libraries/dsp/fft/src/vhdl/fft_r2_pipe.vhd b/libraries/dsp/fft/src/vhdl/fft_r2_pipe.vhd index 317095e9c7782275905d7452814afd6cc8567373..c9ed7f72c558ac588935c3670265363a810f4b5f 100644 --- a/libraries/dsp/fft/src/vhdl/fft_r2_pipe.vhd +++ b/libraries/dsp/fft/src/vhdl/fft_r2_pipe.vhd @@ -39,15 +39,22 @@ -- an alternating way: A(0), B(0), A(1), B(1).... etc -- -- --- Remarks: When g_fft.nof_chan is used the spectrums at the output will be interleaved --- per spectrum and NOT per sample. So in case g_fft.nof_chan = 1 there will be --- two multiplexed channels at the input (c0t0 means channel 0, timestamp 0) : +-- Remarks: +-- . When g_fft.use_separate = TRUE, then the two real inputs are pseudo randomly +-- multiplied by +1 or -1 every block of input samples in fft_switch. At the +-- FFT output this is undone by fft_unswitch. In this way any crosstalk due +-- to quantization noise between the two real inputs gets scrambled and thus +-- averages to zero when integrated over multiple blocks. +-- +-- . When g_fft.nof_chan is used the spectrums at the output will be interleaved +-- per spectrum and NOT per sample. So in case g_fft.nof_chan = 1 there will be +-- two multiplexed channels at the input (c0t0 means channel 0, timestamp 0) : -- --- c0t0 c1t0s c0t1 c1t1 c0t2 c1t2 ... c0t15 c1t15 +-- c0t0 c1t0s c0t1 c1t1 c0t2 c1t2 ... c0t15 c1t15 -- --- At the output will find: +-- At the output will find: -- --- c0f0 c0f1 c0f2 ... c0f15 c1f0 c1f1 c1f2 ... c1f15 (c0f0 means channel 0, frequency bin 0) +-- c0f0 c0f1 c0f2 ... c0f15 c1f0 c1f1 c1f2 ... c1f15 (c0f0 means channel 0, frequency bin 0) -- -- @@ -81,18 +88,30 @@ architecture str of fft_r2_pipe is constant c_pipeline_remove_lsb : natural := 0; + constant c_switch_en : boolean := g_fft.use_separate; -- default do apply switch/unswitch per real input to mitigate quantization crosstalk + constant c_switch_sz_w : natural := ceil_log2(g_fft.nof_points) + g_fft.nof_chan; + constant c_switch_dat_w : natural := g_fft.in_dat_w + 1; -- add 1 extra bit to fit negation of most negative value per real input switch function + constant c_unswitch_dat_w : natural := g_fft.out_dat_w; -- no need for extra bit, because most negative value cannot occur in output constant c_nof_stages : natural := ceil_log2(g_fft.nof_points); constant c_stage_offset : natural := true_log2(g_fft.wb_factor); -- Stage offset is required for twiddle generation in wideband fft constant c_in_scale_w : natural := g_fft.stage_dat_w - g_fft.in_dat_w - sel_a_b(g_fft.guard_enable, g_fft.guard_w, 0); constant c_out_scale_w : integer := g_fft.stage_dat_w - g_fft.out_dat_w - g_fft.out_gain_w; -- Estimate number of LSBs to throw throw away when > 0 or insert when < 0 constant c_raw_dat_extra_w : natural := sel_a_b(g_fft.use_separate, g_sepa_extra_w, 0); constant c_raw_dat_w : natural := g_fft.stage_dat_w + c_raw_dat_extra_w; - + -- number the stage instances from c_nof_stages:1 -- . the data input for the first stage has index c_nof_stages -- . the data output of the last stage has index 0 type t_data_arr is array(c_nof_stages downto 0) of std_logic_vector(g_fft.stage_dat_w-1 downto 0); + signal in_dat_re : std_logic_vector(c_switch_dat_w-1 downto 0); + signal in_dat_im : std_logic_vector(c_switch_dat_w-1 downto 0); + signal in_dat_val : std_logic; + + signal switch_re : std_logic_vector(c_switch_dat_w-1 downto 0); + signal switch_im : std_logic_vector(c_switch_dat_w-1 downto 0); + signal switch_val : std_logic; + signal data_re : t_data_arr; signal data_im : t_data_arr; signal last_re : std_logic_vector(c_raw_dat_w-1 downto 0); @@ -105,13 +124,43 @@ architecture str of fft_r2_pipe is signal raw_out_im : std_logic_vector(c_raw_dat_w-1 downto 0); signal raw_out_val : std_logic; + signal quant_re : std_logic_vector(g_fft.out_dat_w-1 downto 0); + signal quant_im : std_logic_vector(g_fft.out_dat_w-1 downto 0); + signal quant_val : std_logic; + begin + ------------------------------------------------------------------------------ + -- Mitigate quantization noise crosstalk between two real inputs by negating + -- the inputs per lock in a random pattern, when g_fft.use_separate = TRUE. + ------------------------------------------------------------------------------ + -- Inputs - data_re( c_nof_stages) <= scale_and_resize_svec(in_re, c_in_scale_w, g_fft.stage_dat_w); - data_im( c_nof_stages) <= scale_and_resize_svec(in_im, c_in_scale_w, g_fft.stage_dat_w); - data_val(c_nof_stages) <= in_val; - + in_dat_re <= RESIZE_SVEC(in_re, c_switch_dat_w); + in_dat_im <= RESIZE_SVEC(in_im, c_switch_dat_w); + in_dat_val <= in_val; + + u_switch : ENTITY work.fft_switch + GENERIC MAP ( + g_switch_en => c_switch_en, + g_fft_sz_w => c_switch_sz_w, + g_dat_w => c_switch_dat_w + ) + PORT MAP ( + in_re => in_dat_re, + in_im => in_dat_im, + in_val => in_dat_val, + out_re => switch_re, + out_im => switch_im, + out_val => switch_val, + clk => clk, + rst => rst + ); + + data_re( c_nof_stages) <= scale_and_resize_svec(switch_re, c_in_scale_w, g_fft.stage_dat_w); + data_im( c_nof_stages) <= scale_and_resize_svec(switch_im, c_in_scale_w, g_fft.stage_dat_w); + data_val(c_nof_stages) <= switch_val; + ------------------------------------------------------------------------------ -- pipelined FFT stages ------------------------------------------------------------------------------ @@ -155,12 +204,11 @@ begin in_re => data_re(1), in_im => data_im(1), in_val => data_val(1), - out_re => last_re, - out_im => last_im, + out_re => last_re, -- = data_re(0), but may instead have c_raw_dat_w bits + out_im => last_im, -- = data_im(0), but may instead have c_raw_dat_w bits out_val => data_val(0) ); - ------------------------------------------------------------------------------ -- Optional output reorder and separation ------------------------------------------------------------------------------ @@ -190,7 +238,7 @@ begin end generate; - no_reorder_no_generate : if(g_fft.use_separate=false and g_fft.use_reorder=false) generate + no_reorder_no_seperate : if(g_fft.use_separate=false and g_fft.use_reorder=false) generate raw_out_re <= last_re; raw_out_im <= last_im; raw_out_val <= data_val(0); @@ -215,7 +263,7 @@ begin port map ( clk => clk, in_dat => raw_out_re, - out_dat => out_re, + out_dat => quant_re, out_ovr => open ); @@ -235,7 +283,7 @@ begin port map ( clk => clk, in_dat => raw_out_im, - out_dat => out_im, + out_dat => quant_im, out_ovr => open ); @@ -248,8 +296,26 @@ begin rst => rst, clk => clk, in_dat => raw_out_val, - out_dat => out_val + out_dat => quant_val ); + -- Undo input random negation of u_switch at output when g_fft.use_separate = TRUE + u_unswitch : ENTITY work.fft_unswitch + GENERIC MAP ( + g_switch_en => c_switch_en, + g_fft_sz_w => c_switch_sz_w, + g_dat_w => c_unswitch_dat_w + ) + PORT MAP ( + in_re => quant_re, + in_im => quant_im, + in_val => quant_val, + out_re => out_re, + out_im => out_im, + out_val => out_val, + clk => clk, + rst => rst + ); + end str; diff --git a/libraries/dsp/fft/src/vhdl/fft_switch.vhd b/libraries/dsp/fft/src/vhdl/fft_switch.vhd new file mode 100644 index 0000000000000000000000000000000000000000..cad57405ab3b2c03ed84834171fab082b2ed6b51 --- /dev/null +++ b/libraries/dsp/fft/src/vhdl/fft_switch.vhd @@ -0,0 +1,168 @@ +------------------------------------------------------------------------------- +-- +-- 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: ported by E. Kooistra, original 2004 by W. Lubberhuizen / W. Poeisz +-- Purpose: Scramble quantization noise crosstalk between two real inputs +-- Description: +-- . Ported from LOFAR1, see readme_lofar1.txt +-- . The fft_switch multiplies the samples from two real inputs A and B in a +-- block by +1 or -1. The fft_unswitch undoes this by multiplying the FFT +-- output again by +1 and -1. The fft_unswitch takes account of that the FFT +-- has time mutliplexed the two spectra of the two inputs. +-- . The input switching is pseudo random base on a LFSR (linear feedback +-- shift register) sequence. The fft_switch and fft_unswitch start at the +-- first in_val = '1' and then continue 'forever' until a next power cycle +-- by rst ='1'. +-- Remark: +-- . Copy from applications/lofar1/RSP/pft2/src/vhdl/pft_switch.vhd +-- . Removed in_sync, because the in_val are guaranteed to arrive in blocks of +-- c_nof_clk_per_block samples, forever after rst release. +-- The purpose of the in_sync is to recover from an fractional input block, +-- but that cannot occur. The other parts of the FFT also rely on this block +-- processing, without need for in_sync to recover from fractional blocks. +-- The application that uses the FFT must guarantee to only pass on complete +-- blocks of c_nof_clk_per_block samples to the FFT. +-- . The two real inputs each use another LFSR sequence, like for LOFAR1. +-- For the crosstalk mitigation purpose scrambling only one input would be +-- enough, but scrambling both inputs is fine too. + +LIBRARY IEEE, common_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; + +ENTITY fft_switch IS + GENERIC ( + g_switch_en : BOOLEAN := FALSE; + g_fft_sz_w : NATURAL; + g_dat_w : NATURAL + ); + PORT ( + in_re : IN STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0); -- real input A + in_im : IN STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0); -- real input B + in_val : IN STD_LOGIC; + out_re : OUT STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0); + out_im : OUT STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0); + out_val : OUT STD_LOGIC; + clk : IN STD_LOGIC; + rst : IN STD_LOGIC + ); +END fft_switch; + + +ARCHITECTURE rtl OF fft_switch IS + + CONSTANT c_nof_clk_per_block : NATURAL := 2**g_fft_sz_w; + + SIGNAL in_sop : STD_LOGIC; + SIGNAL in_eop : STD_LOGIC; + + SIGNAL cnt : STD_LOGIC_VECTOR(g_fft_sz_w DOWNTO 0) := (OTHERS => '0'); + SIGNAL nxt_cnt : STD_LOGIC_VECTOR(cnt'RANGE); + + SIGNAL lfsr_bit1 : STD_LOGIC; + SIGNAL lfsr_bit2 : STD_LOGIC; + SIGNAL lfsr_en : STD_LOGIC; + + SIGNAL nxt_out_re : STD_LOGIC_VECTOR(in_re'RANGE); + SIGNAL nxt_out_im : STD_LOGIC_VECTOR(in_im'RANGE); + +BEGIN + + -- Create input strobes to view data blocks for debugging + u_in_strobes : ENTITY common_lib.common_create_strobes_from_valid + GENERIC MAP ( + g_pipeline => FALSE, + g_nof_clk_per_sync => c_nof_clk_per_block * 16, -- void value, sync is not used + g_nof_clk_per_block => c_nof_clk_per_block + ) + PORT MAP ( + rst => rst, + clk => clk, + in_val => in_val, + out_val => OPEN, -- out_val = in_val, because g_pipeline = FALSE + out_sop => in_sop, + out_eop => in_eop, + out_sync => OPEN + ); + + no_switch : IF g_switch_en = FALSE GENERATE + -- wire inputs to outputs + out_re <= in_re; + out_im <= in_im; + out_val <= in_val; + END GENERATE; + + gen_switch : IF g_switch_en = TRUE GENERATE + p_reg : PROCESS (rst, clk) + BEGIN + IF rst = '1' THEN + cnt <= (OTHERS => '0'); + out_val <= '0'; + out_re <= (OTHERS => '0'); + out_im <= (OTHERS => '0'); + ELSIF rising_edge(clk) THEN + cnt <= nxt_cnt; + out_val <= in_val; + out_re <= nxt_out_re; + out_im <= nxt_out_im; + END IF; + END PROCESS; + + p_counter: PROCESS(cnt, in_val) + BEGIN + nxt_cnt <= cnt; + IF in_val = '1' THEN + nxt_cnt <= INCR_UVEC(cnt, 1); + END IF; + END PROCESS; + + p_lfsr_ctrl: PROCESS(cnt, in_val) + BEGIN + if TO_SINT(cnt) = -1 AND in_val = '1' THEN + lfsr_en <= '1'; + ELSE + lfsr_en <= '0'; + END IF; + END PROCESS; + + p_out: PROCESS(in_re, in_im, cnt, lfsr_bit1, lfsr_bit2) + BEGIN + nxt_out_re <= in_re; + nxt_out_im <= in_im; + + IF lfsr_bit1 = cnt(cnt'HIGH) THEN + nxt_out_re <= NEGATE_SVEC(in_re, g_dat_w); -- negate block of input A samples + END IF; + IF lfsr_bit2 = cnt(cnt'HIGH) THEN + nxt_out_im <= NEGATE_SVEC(in_im, g_dat_w); -- negate block of input B samples + END IF; + END PROCESS; + + u_fft_lfsr: ENTITY work.fft_lfsr + PORT MAP ( + clk => clk, + rst => rst, + in_en => lfsr_en, + out_bit1 => lfsr_bit1, + out_bit2 => lfsr_bit2 + ); + END GENERATE; + +END rtl; diff --git a/libraries/dsp/fft/src/vhdl/fft_unswitch.vhd b/libraries/dsp/fft/src/vhdl/fft_unswitch.vhd new file mode 100644 index 0000000000000000000000000000000000000000..6d9f561a15e7ca184f70c418810bd85f9b1ac6d6 --- /dev/null +++ b/libraries/dsp/fft/src/vhdl/fft_unswitch.vhd @@ -0,0 +1,159 @@ +------------------------------------------------------------------------------- +-- +-- 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: ported by E. Kooistra, original 2004 by W. Lubberhuizen / W. Poeisz +-- Purpose: Scramble quantization noise crosstalk between two real inputs +-- Description: +-- . Ported from LOFAR1, see readme_lofar1.txt +-- . See fft_switch.vhd +-- Remark: +-- . Copy from applications/lofar1/RSP/pft2/src/vhdl/pft_unswitch.vhd +-- . Removed in_sync, because the in_val are guaranteed to arrive in blocks of +-- c_nof_clk_per_block samples, forever after rst release. + +LIBRARY IEEE, common_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; + +ENTITY fft_unswitch IS + GENERIC ( + g_switch_en : BOOLEAN := FALSE; + g_fft_sz_w : NATURAL; + g_dat_w : NATURAL + ); + PORT ( + in_re : IN STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0); + in_im : IN STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0); + in_val : IN STD_LOGIC; + out_re : OUT STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0); + out_im : OUT STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0); + out_val : OUT STD_LOGIC; + clk : IN STD_LOGIC; + rst : IN STD_LOGIC + ); +END fft_unswitch; + +ARCHITECTURE rtl OF fft_unswitch IS + + CONSTANT c_nof_clk_per_block : NATURAL := 2**g_fft_sz_w; + + SIGNAL in_sop : STD_LOGIC; + SIGNAL in_eop : STD_LOGIC; + + SIGNAL cnt : STD_LOGIC_VECTOR(g_fft_sz_w DOWNTO 0) := (OTHERS => '0'); + SIGNAL nxt_cnt : STD_LOGIC_VECTOR(cnt'RANGE); + + SIGNAL lfsr_bit1 : STD_LOGIC; + SIGNAL lfsr_bit2 : STD_LOGIC; + + SIGNAL lfsr_en : STD_LOGIC; + + SIGNAL nxt_out_re : STD_LOGIC_VECTOR(in_re'RANGE); + SIGNAL nxt_out_im : STD_LOGIC_VECTOR(in_im'RANGE); + +BEGIN + + -- Create input strobes to view data blocks for debugging + u_in_strobes : ENTITY common_lib.common_create_strobes_from_valid + GENERIC MAP ( + g_pipeline => FALSE, + g_nof_clk_per_sync => c_nof_clk_per_block * 16, -- void value, sync is not used + g_nof_clk_per_block => c_nof_clk_per_block + ) + PORT MAP ( + rst => rst, + clk => clk, + in_val => in_val, + out_val => OPEN, -- out_val = in_val, because g_pipeline = FALSE + out_sop => in_sop, + out_eop => in_eop, + out_sync => OPEN + ); + + no_switch : IF g_switch_en = FALSE GENERATE + -- wire inputs to outputs + out_re <= in_re; + out_im <= in_im; + out_val <= in_val; + END GENERATE; + + gen_switch : IF g_switch_en = TRUE GENERATE + p_reg : PROCESS (rst, clk) + BEGIN + IF rst = '1' THEN + cnt <= (OTHERS => '0'); + out_val <= '0'; + out_re <= (OTHERS => '0'); + out_im <= (OTHERS => '0'); + ELSIF rising_edge(clk) THEN + cnt <= nxt_cnt; + out_val <= in_val; + out_re <= nxt_out_re; + out_im <= nxt_out_im; + END IF; + END PROCESS; + + p_counter: PROCESS(cnt, in_val) + BEGIN + nxt_cnt <= cnt; + IF in_val = '1' THEN + nxt_cnt <= INCR_UVEC(cnt, 1); + END IF; + END PROCESS; + + p_lfsr_ctrl: PROCESS(cnt, in_val) + BEGIN + if TO_SINT(cnt) = -1 AND in_val = '1' THEN + lfsr_en <= '1'; + ELSE + lfsr_en <= '0'; + END IF; + END PROCESS; + + p_out: PROCESS(in_re, in_im, cnt, lfsr_bit1, lfsr_bit2) + BEGIN + nxt_out_re <= in_re; + nxt_out_im <= in_im; + + -- multiplexed spectrum for input A at index 0, B at index 1 + IF cnt(0) = '0' THEN + IF cnt(cnt'HIGH) = lfsr_bit1 THEN -- negate spectrum to undo negate of block of real input A + nxt_out_re <= NEGATE_SVEC(in_re, g_dat_w); + nxt_out_im <= NEGATE_SVEC(in_im, g_dat_w); + END IF; + ELSE + IF cnt(cnt'HIGH) = lfsr_bit2 THEN -- negate spectrum to undo negate of block of real input B + nxt_out_re <= NEGATE_SVEC(in_re, g_dat_w); + nxt_out_im <= NEGATE_SVEC(in_im, g_dat_w); + END IF; + END IF; + END PROCESS; + + u_fft_lfsr: ENTITY work.fft_lfsr + PORT MAP ( + clk => clk, + rst => rst, + in_en => lfsr_en, + out_bit1 => lfsr_bit1, + out_bit2 => lfsr_bit2 + ); + END GENERATE; + +END rtl; diff --git a/libraries/dsp/fft/tb/vhdl/tb_fft_switch.vhd b/libraries/dsp/fft/tb/vhdl/tb_fft_switch.vhd new file mode 100644 index 0000000000000000000000000000000000000000..08d04ee3e9bae1ffaea762e535be0d38d97395f6 --- /dev/null +++ b/libraries/dsp/fft/tb/vhdl/tb_fft_switch.vhd @@ -0,0 +1,311 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2022 +-- 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: Tb for fft_switch.vhd + fft_unswitch.vhd +-- Description: +-- +-- p_in_val --> u_fft_switch --> mux --> u_fft_unswitch --> demux --> p_verify +-- +-- . p_in_val creates blocks of in_val, with or without g_in_val_gaps +-- . in_a and in_b are offset counter data that increment at in_val +-- . fft_switch uses an lfsr per input to randomly negate or keep the input +-- . mux models that the FFT complex output is multiplexes a, b in time +-- . fft_unswitch use the same lfsr as fft_switch to undo the random negate +-- on the multiplexed a, b output +-- . demux demultiplexes the output so that it can be compared to the delayed +-- input +-- . p_verify checks that the output is equal to the delayed input. +-- +-- Remark: +-- . The fft_switch and fft_unswitch only use in_val, the other strobes sop, +-- eop and sync are only for tb debugging purposes to recognize the in_val +-- data blocks of c_nof_clk_per_block samples in the Wave window. +-- . The g_increment_at_val determines whether the in_re, in_im increment at +-- every sample (at in_val), or at every block of samples (at in_eop). +-- Default use g_increment_at_val = TRUE. Increment at eop is for debugging +-- purposes. +-- +-- Usage: +-- > as 5 +-- > run -a +-- # view a,b and re,im signals in radix decimal + +LIBRARY IEEE, common_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; + +ENTITY tb_fft_switch IS + GENERIC ( + g_switch_en : BOOLEAN := TRUE; + g_in_val_gaps : BOOLEAN := TRUE; + g_increment_at_val : BOOLEAN := TRUE; + g_fft_size_w : NATURAL := 3; + g_nof_clk_per_sync : NATURAL := 32; + g_nof_sync : NATURAL := 2 + ); +END tb_fft_switch; + +ARCHITECTURE tb OF tb_fft_switch IS + + CONSTANT clk_period : TIME := 10 ns; + + CONSTANT c_dat_w : NATURAL := 16; + + CONSTANT c_nof_clk_per_block : NATURAL := 2**g_fft_size_w; + CONSTANT c_nof_block_per_sync_max : NATURAL := ceil_div(g_nof_clk_per_sync, c_nof_clk_per_block); + CONSTANT c_nof_block_per_sync_min : NATURAL := g_nof_clk_per_sync / c_nof_clk_per_block; + + CONSTANT c_dly : NATURAL := 4; -- pipeling in fft_switch, mux, fft_unswitch and demux + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL rst : STD_LOGIC := '1'; + SIGNAL clk : STD_LOGIC := '0'; + + -- Use fixed input A, B values + SIGNAL in_a : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0) := TO_SVEC(3, c_dat_w); + SIGNAL in_b : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0) := TO_SVEC(13, c_dat_w); + SIGNAL in_val : STD_LOGIC := '0'; + SIGNAL in_sop : STD_LOGIC := '0'; + SIGNAL in_eop : STD_LOGIC := '0'; + SIGNAL in_sync : STD_LOGIC := '0'; + + SIGNAL switch_a : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL switch_b : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL switch_val : STD_LOGIC; + SIGNAL prev1_switch_a : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL prev1_switch_b : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL prev2_switch_a : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL prev2_switch_b : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + + SIGNAL mux_toggle : STD_LOGIC := '0'; + SIGNAL mux_re : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0) := (OTHERS => '0'); + SIGNAL mux_im : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0) := (OTHERS => '0'); + SIGNAL mux_val : STD_LOGIC := '0'; + + SIGNAL unswitch_re : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL unswitch_im : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL unswitch_val : STD_LOGIC := '0'; + SIGNAL prev1_unswitch_re : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL prev1_unswitch_im : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL prev2_unswitch_re : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL prev2_unswitch_im : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + + SIGNAL out_toggle : STD_LOGIC := '0'; + SIGNAL out_a : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL out_b : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL out_val : STD_LOGIC; + SIGNAL out_sop : STD_LOGIC := '0'; + SIGNAL out_eop : STD_LOGIC := '0'; + SIGNAL out_sync : STD_LOGIC := '0'; + + SIGNAL dly_val : STD_LOGIC_VECTOR(0 TO c_dly) := (OTHERS => '0'); + SIGNAL dly_a : t_integer_arr(0 TO c_dly) := (OTHERS => 0); + SIGNAL dly_b : t_integer_arr(0 TO c_dly) := (OTHERS => 0); + SIGNAL exp_val : STD_LOGIC := '0'; + SIGNAL exp_a : INTEGER; + SIGNAL exp_b : INTEGER; + + SIGNAL verify_en : STD_LOGIC := '0'; + +BEGIN + + clk <= NOT clk OR tb_end AFTER clk_period/2; + + p_in_val : PROCESS + BEGIN + rst <= '1'; + in_val <= '0'; + proc_common_wait_some_cycles(clk, 10); + rst <= '0'; + proc_common_wait_some_cycles(clk, 10); + FOR I IN 0 TO g_nof_sync-1 LOOP + FOR J IN 0 TO c_nof_block_per_sync_max-1 LOOP + FOR K IN 0 TO c_nof_clk_per_block-1 LOOP + IF g_in_val_gaps AND K = 0 THEN + in_val <= '0'; -- insert a one cycle gap + proc_common_wait_some_cycles(clk, 1); + END IF; + in_val <= '1'; + proc_common_wait_some_cycles(clk, 1); + END LOOP; + END LOOP; + END LOOP; + proc_common_wait_some_cycles(clk, g_nof_clk_per_sync*g_nof_sync); + tb_end <= '1'; + WAIT; + END PROCESS; + + -- Create in strobes for debugging + u_in_strobes : ENTITY common_lib.common_create_strobes_from_valid + GENERIC MAP ( + g_pipeline => FALSE, + g_nof_clk_per_sync => g_nof_clk_per_sync, + g_nof_clk_per_block => c_nof_clk_per_block + ) + PORT MAP ( + rst => rst, + clk => clk, + in_val => in_val, + out_val => OPEN, -- out_val = in_val, because g_pipeline = FALSE + out_sop => in_sop, + out_eop => in_eop, + out_sync => in_sync + ); + + + gen_increment_at_val : IF g_increment_at_val = TRUE GENERATE + in_a <= INCR_SVEC(in_a, 1) WHEN rising_edge(clk) AND in_val = '1'; + in_b <= INCR_SVEC(in_b, 1) WHEN rising_edge(clk) AND in_val = '1'; + END GENERATE; + gen_increment_at_eop : IF g_increment_at_val = FALSE GENERATE + in_a <= INCR_SVEC(in_a, 1) WHEN rising_edge(clk) AND in_eop = '1'; + in_b <= INCR_SVEC(in_b, 1) WHEN rising_edge(clk) AND in_eop = '1'; + END GENERATE; + + + u_fft_switch : ENTITY work.fft_switch + GENERIC MAP ( + g_switch_en => g_switch_en, + g_fft_sz_w => g_fft_size_w, + g_dat_w => c_dat_w + ) + PORT MAP ( + in_re => in_a, + in_im => in_b, + in_val => in_val, + out_re => switch_a, + out_im => switch_b, + out_val => switch_val, + clk => clk, + rst => rst + ); + + -- Model A, B multiplexing part of FFT + -- 0 1 2 .. N-1 + -- switch_a: a0 a1 a2 .. aN-1 + -- switch_b: b0 b1 b2 .. bN-1 + -- prev1_switch_a: a0 a1 .. aN-1 + -- prev1_switch_b: b0 b1 .. bN-1 + -- prev2_switch_a: a0 .. aN-1 + -- prev2_switch_b: b0 .. bN-1 + -- mux_toggle: 0 1 0 1 0 .. 1 0 + -- 0 1 2 3 .. N-2 N-1 + -- mux_re: a0 b0 a2 b2 .. aN-2 bN-2 + -- mux_im: a1 b1 a3 b3 .. aN-1 bN-1 + + prev1_switch_a <= switch_a WHEN rising_edge(clk) AND switch_val = '1'; + prev1_switch_b <= switch_b WHEN rising_edge(clk) AND switch_val = '1'; + prev2_switch_a <= prev1_switch_a WHEN rising_edge(clk) AND switch_val = '1'; + prev2_switch_b <= prev1_switch_b WHEN rising_edge(clk) AND switch_val = '1'; + + mux_toggle <= NOT mux_toggle WHEN rising_edge(clk) AND switch_val = '1'; + + mux_re <= prev1_switch_a WHEN mux_toggle = '1' ELSE prev2_switch_b; -- a0, b0, .. + mux_im <= switch_a WHEN mux_toggle = '1' ELSE prev1_switch_b; -- a1, b1, .. + mux_val <= switch_val WHEN rising_edge(clk); + + + u_fft_unswitch : ENTITY work.fft_unswitch + GENERIC MAP ( + g_switch_en => g_switch_en, + g_fft_sz_w => g_fft_size_w, + g_dat_w => c_dat_w + ) + PORT MAP ( + in_re => mux_re, + in_im => mux_im, + in_val => mux_val, + out_re => unswitch_re, + out_im => unswitch_im, + out_val => unswitch_val, + clk => clk, + rst => rst + ); + + -- Demultiplex output to ease verification + -- 0 1 2 3 .. N-2 N-1 + -- unswitch_re: a0 b0 a2 b2 .. aN-2 bN-2 + -- unswitch_im: a1 b1 a3 b3 .. aN-1 bN-1 + -- prev1_unswitch_re: a0 b0 a2 b2 .. aN-2 bN-2 + -- prev1_unswitch_im: a1 b1 a3 b3 .. aN-1 bN-1 + -- prev2_unswitch_re: a0 b0 a2 b2 .. aN-2 bN-2 + -- prev2_unswitch_im: a1 b1 a3 b3 .. aN-1 bN-1 + -- out_toggle: 0 1 0 1 0 + -- 0 1 .. N-1 + -- out_a: a0 a1 ..aN-1 + -- out_b: b0 b1 ..bN-1 + + prev1_unswitch_re <= unswitch_re WHEN rising_edge(clk) AND unswitch_val = '1'; + prev1_unswitch_im <= unswitch_im WHEN rising_edge(clk) AND unswitch_val = '1'; + prev2_unswitch_re <= prev1_unswitch_re WHEN rising_edge(clk) AND unswitch_val = '1'; + prev2_unswitch_im <= prev1_unswitch_im WHEN rising_edge(clk) AND unswitch_val = '1'; + + out_toggle <= NOT out_toggle WHEN rising_edge(clk) AND unswitch_val = '1'; + + out_a <= prev1_unswitch_re WHEN out_toggle = '1' ELSE prev2_unswitch_im; -- a0, a1, .. + out_b <= unswitch_re WHEN out_toggle = '1' ELSE prev1_unswitch_im; -- b0, b1, .. + out_val <= unswitch_val WHEN rising_edge(clk); + + -- Create out strobes for debugging + u_out_strobes : ENTITY common_lib.common_create_strobes_from_valid + GENERIC MAP ( + g_pipeline => FALSE, + g_nof_clk_per_sync => g_nof_clk_per_sync, + g_nof_clk_per_block => c_nof_clk_per_block + ) + PORT MAP ( + rst => rst, + clk => clk, + in_val => out_val, + out_val => OPEN, -- out_val = in_val, because g_pipeline = FALSE + out_sop => out_sop, + out_eop => out_eop, + out_sync => out_sync + ); + + -- Account for pipeling in fft_switch, mux, fft_unswitch and demux + dly_val(0) <= in_val; + dly_a(0) <= TO_SINT(in_a); + dly_b(0) <= TO_SINT(in_b); + dly_val(1 TO c_dly) <= dly_val(0 TO c_dly-1) WHEN rising_edge(clk); + dly_a(1 TO c_dly) <= dly_a(0 TO c_dly-1) WHEN rising_edge(clk); + dly_b(1 TO c_dly) <= dly_b(0 TO c_dly-1) WHEN rising_edge(clk); + exp_val <= dly_val(c_dly); + exp_a <= dly_a(c_dly); + exp_b <= dly_b(c_dly); + + verify_en <= '1' WHEN exp_val = '1'; + + p_verify : PROCESS(clk) + BEGIN + IF rising_edge(clk) THEN + IF verify_en = '1' THEN + IF exp_val = '1' THEN + ASSERT TO_SINT(out_a) = exp_a REPORT "Wrong out_re" SEVERITY ERROR; + ASSERT TO_SINT(out_b) = exp_b REPORT "Wrong out_im" SEVERITY ERROR; + END IF; + ASSERT out_val = exp_val REPORT "Wrong out_val" SEVERITY ERROR; + END IF; + END IF; + END PROCESS; + +END tb; diff --git a/libraries/dsp/fft/tb/vhdl/tb_tb_fft_r2_pipe.vhd b/libraries/dsp/fft/tb/vhdl/tb_tb_fft_r2_pipe.vhd index 3efd0d0917221ec7c194f8916c34f543a0f8a585..ba5514038feb271e308a479e1a4e1959f447af46 100644 --- a/libraries/dsp/fft/tb/vhdl/tb_tb_fft_r2_pipe.vhd +++ b/libraries/dsp/fft/tb/vhdl/tb_tb_fft_r2_pipe.vhd @@ -53,6 +53,7 @@ ARCHITECTURE tb OF tb_tb_fft_r2_pipe IS -- Real input CONSTANT c_impulse_chirp : string := "data/run_pfft_m_impulse_chirp_8b_128points_16b.dat"; -- 25600 lines CONSTANT c_sinusoid_chirp : string := "data/run_pfft_m_sinusoid_chirp_8b_128points_16b.dat"; -- 25600 lines + CONSTANT c_sinusoid : string := "data/run_pfft_m_sinusoid_8b_128points_16b.dat"; -- 640 lines CONSTANT c_noise : string := "data/run_pfft_m_noise_8b_128points_16b.dat"; -- 1280 lines CONSTANT c_dc_agwn : string := "data/run_pfft_m_dc_agwn_8b_128points_16b.dat"; -- 1280 lines -- Complex input @@ -104,9 +105,10 @@ BEGIN u_act_two_real_chirp : ENTITY work.tb_fft_r2_pipe GENERIC MAP (c_fft_two_real, c_diff_margin, c_sinusoid_chirp, 25600, c_impulse_chirp, 25600, c_unused, 0, 25600, FALSE); u_act_two_real_a0 : ENTITY work.tb_fft_r2_pipe GENERIC MAP (c_fft_two_real, c_diff_margin, c_zero, 25600, c_impulse_chirp, 25600, c_unused, 0, 5120, FALSE); u_act_two_real_b0 : ENTITY work.tb_fft_r2_pipe GENERIC MAP (c_fft_two_real, c_diff_margin, c_sinusoid_chirp, 25600, c_zero, 25600, c_unused, 0, 5120, FALSE); + u_act_two_real_sinus : ENTITY work.tb_fft_r2_pipe GENERIC MAP (c_fft_two_real, c_diff_margin, c_sinusoid, 640, c_zero, 640, c_unused, 0, 640, FALSE); u_rnd_two_real_noise : ENTITY work.tb_fft_r2_pipe GENERIC MAP (c_fft_two_real, c_diff_margin, c_noise, 1280, c_dc_agwn, 1280, c_unused, 0, 1280, TRUE); u_rnd_two_real_channels : ENTITY work.tb_fft_r2_pipe GENERIC MAP (c_fft_two_real_more_channels, c_diff_margin, c_noise, 1280, c_dc_agwn, 1280, c_unused, 0, 1280, TRUE); - + -- Complex input data u_act_complex_chirp : ENTITY work.tb_fft_r2_pipe GENERIC MAP (c_fft_complex, c_diff_margin, c_unused, 0, c_unused, 0, c_phasor_chirp, 12800, 12800, FALSE); u_act_complex_channels : ENTITY work.tb_fft_r2_pipe GENERIC MAP (c_fft_complex_more_channels, c_diff_margin, c_unused, 0, c_unused, 0, c_phasor_chirp, 12800, 1280, FALSE);