diff --git a/libraries/dsp/fft/tb/vhdl/tb_fft_switch.vhd b/libraries/dsp/fft/tb/vhdl/tb_fft_switch.vhd index 2332263212913553026c2c49eeb3ee4dc18c98b2..41d10820b0c34e23e35892a93d75906a384eaa1a 100644 --- a/libraries/dsp/fft/tb/vhdl/tb_fft_switch.vhd +++ b/libraries/dsp/fft/tb/vhdl/tb_fft_switch.vhd @@ -21,22 +21,45 @@ -- 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, dp_lib; +LIBRARY IEEE, common_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_fft_switch IS GENERIC ( - g_in_val_gaps : BOOLEAN := FALSE; + g_in_val_gaps : BOOLEAN := TRUE; + g_increment_at_val : BOOLEAN := TRUE; g_fft_size_w : NATURAL := 3; - g_nof_clk_per_sync : NATURAL := 80; - g_nof_sync : NATURAL := 10 + g_nof_clk_per_sync : NATURAL := 32; + g_nof_sync : NATURAL := 2 ); END tb_fft_switch; @@ -50,47 +73,65 @@ ARCHITECTURE tb OF tb_fft_switch IS 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; - SIGNAL tb_end : STD_LOGIC := '0'; - SIGNAL rst : STD_LOGIC := '1'; - SIGNAL clk : STD_LOGIC := '0'; - - -- Fixed input A, B values - SIGNAL in_val : STD_LOGIC := '0'; - 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(4, c_dat_w); - SIGNAL in_sosi : t_dp_sosi := c_dp_sosi_rst; - - 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_en : STD_LOGIC := '1'; - SIGNAL switch_sosi : t_dp_sosi; - - SIGNAL mux_toggle : STD_LOGIC := '0'; - SIGNAL mux_re : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); - SIGNAL mux_im : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); - SIGNAL mux_sosi : t_dp_sosi; - - SIGNAL fft_re : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); - SIGNAL fft_im : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); - SIGNAL fft_sosi : t_dp_sosi; - - 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_sosi : t_dp_sosi; - - 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_sosi : t_dp_sosi; - - SIGNAL dly_sosi : t_dp_sosi; - SIGNAL exp_sosi : t_dp_sosi; + 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_en : STD_LOGIC := '1'; + 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); + SIGNAL mux_im : STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); + SIGNAL mux_val : STD_LOGIC; + + SIGNAL unswitch_en : STD_LOGIC := '1'; + 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; + 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_stimuli : PROCESS + p_in_val : PROCESS BEGIN rst <= '1'; in_val <= '0'; @@ -114,10 +155,10 @@ BEGIN WAIT; END PROCESS; - -- Create sync for fft_switch - u_sync_switch : ENTITY common_lib.common_create_sync_from_valid + -- Create in strobes for debugging + u_in_strobes : ENTITY common_lib.common_create_strobes_from_valid GENERIC MAP ( - g_pipeline => TRUE, + g_pipeline => FALSE, g_nof_clk_per_sync => g_nof_clk_per_sync, g_nof_clk_per_block => c_nof_clk_per_block ) @@ -125,62 +166,64 @@ BEGIN rst => rst, clk => clk, in_val => in_val, - out_val => in_sosi.valid, - out_sop => in_sosi.sop, -- for monitoring only - out_eop => in_sosi.eop, -- for monitoring only - out_sync1 => in_sosi.sync -- use LOFAR1 style sync, is sync before sop + 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_fft_sz_w => g_fft_size_w, - g_dat_w => c_dat_w + g_nof_clk_per_sync => g_nof_clk_per_sync, + g_fft_sz_w => g_fft_size_w, + g_dat_w => c_dat_w ) PORT MAP ( - in_re => in_a, -- constant values - in_im => in_b, -- constant values - in_val => in_sosi.valid, - in_sync => in_sosi.sync, -- use LOFAR1 style sync, is sync before sop + in_re => in_a, + in_im => in_b, + in_val => in_val, switch_en => switch_en, out_re => switch_a, out_im => switch_b, - out_val => switch_sosi.valid, - out_sync => switch_sosi.sync, -- for monitoring only + out_val => switch_val, clk => clk, rst => rst ); -- Model A, B multiplexing part of FFT - -- 0 1 ..N-1 0 1 2 3 .. N-2 N-1 - -- switch_a: a a ..a --> fft_re: a b a b .. a b - -- switch_b: b b ..b --> fft_im: a b a b .. a b - mux_toggle <= NOT mux_toggle WHEN rising_edge(clk) AND switch_sosi.valid = '1'; - - mux_re <= switch_a WHEN mux_toggle = '0' ELSE switch_b; - mux_im <= switch_a WHEN mux_toggle = '0' ELSE switch_b; - mux_sosi <= switch_sosi; - - -- Create sync for fft_unswitch - u_sync_unswitch : ENTITY common_lib.common_create_sync_from_valid - GENERIC MAP ( - g_pipeline => TRUE, - 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 => mux_sosi.valid, - out_val => fft_sosi.valid, - out_sop => fft_sosi.sop, -- for monitoring only - out_eop => fft_sosi.eop, -- for monitoring only - out_sync1 => fft_sosi.sync -- use LOFAR1 style sync, is sync before sop - ); - - -- Pipeline the complex data to align with the strobes of fft_sosi - fft_re <= mux_re WHEN rising_edge(clk); - fft_im <= mux_im WHEN rising_edge(clk); + -- 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 @@ -189,41 +232,81 @@ BEGIN g_dat_w => c_dat_w ) PORT MAP ( - in_re => fft_re, - in_im => fft_im, - in_val => fft_sosi.valid, - in_sync => fft_sosi.sync, - switch_en => switch_en, + in_re => mux_re, + in_im => mux_im, + in_val => mux_val, + switch_en => unswitch_en, out_re => unswitch_re, out_im => unswitch_im, - out_val => unswitch_sosi.valid, - out_sync => unswitch_sosi.sync, -- for monitoring only + out_val => unswitch_val, clk => clk, rst => rst ); -- Demultiplex output to ease verification - -- 0 1 2 3 .. N-2 N-1 0 1 ..N-1 - -- unswitch_re: a b a b .. a b --> out_a: a a ..a - -- unswitch_im: a b a b .. a b --> out_b: b b ..b - out_toggle <= NOT out_toggle WHEN rising_edge(clk) AND unswitch_sosi.valid = '1'; - - out_a <= unswitch_re WHEN out_toggle = '0'; - out_b <= unswitch_im WHEN out_toggle = '1'; + -- 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 + ); - out_sosi.valid <= unswitch_sosi.valid; - out_sosi.sync <= unswitch_sosi.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); - dly_sosi <= in_sosi WHEN rising_edge(clk); -- Account for pipeling in fft_switch - exp_sosi <= dly_sosi WHEN rising_edge(clk); -- Account for pipeling in fft_unswitch + verify_en <= '1' WHEN exp_val = '1'; p_verify : PROCESS(clk) BEGIN IF rising_edge(clk) THEN - ASSERT out_a = in_a REPORT "Wrong out_re" SEVERITY ERROR; - ASSERT out_b = in_b REPORT "Wrong out_im" SEVERITY ERROR; - ASSERT out_sosi.valid = exp_sosi.valid REPORT "Wrong out_val" SEVERITY ERROR; - ASSERT out_sosi.sync = exp_sosi.sync REPORT "Wrong out_sync" SEVERITY ERROR; + 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;