diff --git a/libraries/dsp/rTwoSDF/tb/vhdl/tb_rTwoSDF.vhd b/libraries/dsp/rTwoSDF/tb/vhdl/tb_rTwoSDF.vhd new file mode 100644 index 0000000000000000000000000000000000000000..ffa1d2a7a28296e130b9ce06577c815ee711b9ac --- /dev/null +++ b/libraries/dsp/rTwoSDF/tb/vhdl/tb_rTwoSDF.vhd @@ -0,0 +1,395 @@ +-- Author: Raj Thilak Rajan : rajan at astron.nl: Nov 2009 +-------------------------------------------------------------------------------- +-- +-- Copyright (C) 2012 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-------------------------------------------------------------------------------- + +-- +-- Purpose: Test bench for the rTwoSDF pipelined radix 2 FFT +-- +-- Description: ASTRON-RP-755 +-- The testbench can simulate (via g_use_uniNoise_file): +-- +-- a) complex uniform noise input from a file generated by testFFT_input.m +-- b) impulse input from a manually created file +-- +-- Stimuli b) are useful for visual interpretation of the FFT output, because +-- an impulse at the real input and zero at the imaginary inpu will result +-- in DC and zero if the pulse occurs at the first sample or in sinus and +-- cosinus wave if the impulse occurs at a later sample. However because the +-- imaginary input is zero this does not cover all internals of the PFT +-- implementation. Therefore stimuli a) are needed to fully verify the PFT. +-- +-- The rTwoSDF output can be verified in two ways: +-- +-- 1) The MATLAB testFFT_output.m can calculate the floating point FFT and +-- compare it with the rTwoSDF implementation output file result. +-- 2) The rTwoSDF implementation output file is also kept in SVN as golden +-- reference result to allow verification using a file diff command like +-- e.g. WinMerge. This then avoids the need to run MATLAB to verify. +-- +-- The tb asserts an error when the output does not match the expected output +-- that is read from the golden reference file. The output is also written +-- to a default output file to support offline analysis. +-- +-- Usage: +-- > vsim -vopt -voptargs=+acc work.tb_rtwosdf (double click tb_rtwosdf icon) +-- > do wave_rTwoSDF.do +-- > run -all +-- . it may be necessary to rescale the analogue formats in the Wave +-- window after running the simulation. +-- +-- Remarks: +-- . The tb uses LRM 1076-1987 style for file IO. This implies that only +-- simulator start and quit can open and close the file. The output file +-- can be read in a file editor, but the SVN file icon indicates that the +-- file is modified even if the contents is not changed. Only after closing +-- the simulation (> quit -sim) does the SVN file icon indicate the true +-- state. Next time we better use LRM 1076-1993 style for file IO. +-- +-- . The generic g_post_sim can be used to switch between functional simulation +-- and timing simulation. Note that the entity name of the timing model from +-- the .vho file must be changed to rTwoSDF_t. + +library ieee, common_lib; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use IEEE.std_logic_textio.all; +use STD.textio.all; +use common_lib.common_pkg.all; +use common_lib.common_lfsr_sequences_pkg.all; +use common_lib.tb_common_pkg.all; +use work.rTwoSDFPkg.all; +use work.twiddlesPkg.all; + + +entity tb_rTwoSDF is + generic( + -- generics for tb + g_use_uniNoise_file : boolean := true; + g_in_en : natural := 1; -- 1 = always active, others = random control + g_post_sim : boolean := false; + -- generics for rTwoSDF + g_use_reorder : boolean := true; + g_nof_points : natural := 1024; + g_in_dat_w : natural := 8; + g_out_dat_w : natural := 14; + g_guard_w : natural := 2; -- guard bits are used to avoid overflow in single FFT stage. + + -- Internal pipeline settings for rTwoSDF + g_pipeline : t_fft_pipeline := (1, 1, 3, 1, 1, 0, 0, 1) -- type t_rtwo_sdf_stage_pipeline is record + -- -- generics for rTwoSDFStage + -- stage_lat : natural; -- = 1 + -- weight_lat : natural; -- = 1 + -- mul_lat : natural; -- = 3 + -- -- generics for rTwoBFStage + -- bf_lat : natural; -- = 1 + -- -- generics for rTwoBF + -- bf_use_zdly : natural; -- = 1 + -- bf_in_a_zdly : natural; -- = 0 + -- bf_out_d_zdly : natural; -- = 0 + -- sep_lat : natural; -- = 1 + -- end record; + ); +end entity tb_rTwoSDF; + + +architecture tb of tb_rTwoSDF is + + constant c_clk_period : time := 20 ns; + + + + -- input/output data width + constant c_stage_dat_w : natural := sel_a_b(g_out_dat_w > c_dsp_mult_w, g_out_dat_w, c_dsp_mult_w); -- number of bits used between the stages + + -- input/output files + constant c_file_len : natural := 8*g_nof_points; + constant c_repeat : natural := 2; -- >= 2 to have sufficent frames for c_outputFile evaluation by testFFT_output.m + + -- input from uniform noise file created automatically by MATLAB testFFT_input.m + constant c_noiseInputFile : string := "../../../tb/data/test/in/uniNoise_p" & natural'image(g_nof_points)& "_b"& natural'image(g_in_dat_w) &"_in.txt"; + constant c_noiseGoldenFile : string := "../../../tb/data/test/out/uniNoise_p" & natural'image(g_nof_points)& "_in"& natural'image(g_in_dat_w) &"_out"&natural'image(g_out_dat_w) &"_out.txt"; + constant c_noiseOutputFile : string := "../../../tb/data/test/out/uniNoise_out.txt"; + + -- input from manually created file + constant c_impulseInputFile : string := "../../../tb/data/test/in/impulse_p" & natural'image(g_nof_points)& "_b"& natural'image(g_in_dat_w)& "_in.txt"; + constant c_impulseGoldenFile : string := "../../../tb/data/test/out/impulse_p" & natural'image(g_nof_points)& "_b"& natural'image(g_in_dat_w)& "_out.txt"; + constant c_impulseOutputFile : string := "../../../tb/data/test/out/impulse_out.txt"; + + -- determine active stimuli and result files + constant c_inputFile : string := sel_a_b(g_use_uniNoise_file, c_noiseInputFile, c_impulseInputFile); + constant c_goldenFile : string := sel_a_b(g_use_uniNoise_file, c_noiseGoldenFile, c_impulseGoldenFile); + constant c_outputFile : string := sel_a_b(g_use_uniNoise_file, c_noiseOutputFile, c_impulseOutputFile); + + -- signal definitions + signal tb_end : std_logic := '0'; + signal clk : std_logic := '0'; + signal rst : std_logic := '0'; + signal enable : std_logic := '1'; + signal random : std_logic_vector(15 downto 0) := (others=>'0'); -- use different lengths to have different random sequences + signal in_en : std_logic := '0'; + + signal in_re : std_logic_vector(g_in_dat_w-1 downto 0); + signal in_im : std_logic_vector(g_in_dat_w-1 downto 0); + signal in_sync : std_logic:= '0'; + signal in_val : std_logic:= '0'; + + signal out_re : std_logic_vector(g_out_dat_w-1 downto 0); + signal out_im : std_logic_vector(g_out_dat_w-1 downto 0); + signal out_sync : std_logic:= '0'; + signal out_val : std_logic:= '0'; + + signal in_file_data : t_integer_matrix(0 to c_file_len-1, 1 to 2) := (others=>(others=>0)); -- [re, im] + signal in_file_sync : std_logic_vector(0 to c_file_len-1):= (others=>'0'); + signal in_file_val : std_logic_vector(0 to c_file_len-1):= (others=>'0'); + + signal in_index : natural := 0; + signal in_repeat : natural := 0; + + signal gold_file_data : t_integer_matrix(0 to c_file_len-1, 1 to 2) := (others=>(others=>0)); -- [re, im] + signal gold_file_sync : std_logic_vector(0 to c_file_len-1):= (others=>'0'); + signal gold_file_val : std_logic_vector(0 to c_file_len-1):= (others=>'0'); + + signal gold_index : natural; + signal gold_sync : std_logic; + signal gold_re : integer; + signal gold_im : integer; + +begin + + clk <= (not clk) or tb_end after c_clk_period/2; + rst <= '1', '0' after c_clk_period*7; + enable <= '0', '1' after c_clk_period*23; + random <= func_common_random(random) when rising_edge(clk); + in_en <= '1' when g_in_en=1 else random(random'HIGH); + + + p_read_input_file : process + file v_input : TEXT open READ_MODE is c_inputFile; -- this is LRM 1076-1987 style and implies that only simulator start and quit can open and close the file + variable v_log_line : LINE; + variable v_input_line : LINE; + variable v_index : integer :=0; + variable v_comma : character; + variable v_sync : std_logic_vector(0 to c_file_len-1):=(others=>'0'); + variable v_val : std_logic_vector(0 to c_file_len-1):=(others=>'0'); + variable v_data : t_integer_matrix(0 to c_file_len-1, 1 to 2) := (others=>(others=>0)); + begin + -- wait 1 clock cycle to avoid that the output messages in the transcript window get lost in the 0 ps start up messages + proc_common_wait_some_cycles(clk, 1); + -- combinatorially read the file into the array + write(v_log_line, string'("reading stimuli file : ")); + write(v_log_line, c_inputFile); + writeline(output, v_log_line); + loop + exit when endfile(v_input); + readline(v_input, v_input_line); + + read(v_input_line, v_sync(v_index)); -- sync + read(v_input_line, v_comma); + + read(v_input_line, v_val(v_index)); -- valid + read(v_input_line, v_comma); + + read(v_input_line, v_data(v_index,1)); -- real + read(v_input_line, v_comma); + + read(v_input_line, v_data(v_index,2)); -- imag + v_index := v_index + 1; + end loop; + write(v_log_line, string'("finished reading stimuli file")); + writeline(output, v_log_line); + + in_file_data <= v_data; + in_file_sync <= v_sync; + in_file_val <= v_val; + wait; + end process; + + p_in_stimuli : process(clk, rst) + begin + if rst='1' then + in_re <= (others=>'0'); + in_im <= (others=>'0'); + in_sync <= '0'; + in_val <= '0'; + + in_index <= 0; + in_repeat <= 0; + elsif rising_edge(clk) then + + in_sync <= '0'; + in_val <= '0'; + + -- start stimuli some arbitrary time after rst release to ensure that the proper behaviour of the DUT does not depend on that time + if enable='1' then + -- use always active input (the in_file contents may still contain blocks with in_val='0') or use random active input + if in_en='1' then + if in_index<c_file_len-1 then + in_index <= in_index+1; + else + in_index <= 0; + in_repeat <= in_repeat + 1; + end if; + + if in_repeat < c_repeat then + in_re <= std_logic_vector(to_signed(in_file_data(in_index, 1), g_in_dat_w)); + in_im <= std_logic_vector(to_signed(in_file_data(in_index, 2), g_in_dat_w)); + in_sync <= std_logic(in_file_sync(in_index)); + in_val <= std_logic(in_file_val(in_index)); + end if; + + if in_repeat > c_repeat then + tb_end <= '1'; + end if; + end if; + end if; + + end if; + end process; + + + gen_func_dut : if g_post_sim = FALSE generate + -- DUT = Device Under Test + u_rTwoSDF : entity work.rTwoSDF + generic map( + -- generics for the FFT + g_use_reorder => g_use_reorder, + g_in_dat_w => g_in_dat_w, + g_out_dat_w => g_out_dat_w, + g_stage_dat_w => c_stage_dat_w, + g_guard_w => g_guard_w, + g_nof_points => g_nof_points, + -- generics for rTwoSDFStage + g_pipeline => g_pipeline + ) + port map( + clk => clk, + rst => rst, + in_re => in_re, + in_im => in_im, + in_val => in_val, + out_re => out_re, + out_im => out_im, + out_val => out_val + ); + end generate; + +-- In case a timing simulation should be made the next code +-- should be un-commented. And the name of the timing varina entity +-- in the .vho file must be renamed to rTwoSDF_t. + +-- gen_time_dut : if g_post_sim = TRUE generate +-- -- DUT = Device Under Test +-- u_rTwoSDF_t : entity work.rTwoSDF_t +-- port map( +-- clk => clk, +-- rst => rst, +-- in_re => in_re, +-- in_im => in_im, +-- in_val => in_val, +-- out_re => out_re, +-- out_im => out_im, +-- out_val => out_val +-- ); +-- end generate; + + -- Read golden file with the expected DUT output + p_read_golden_file : process + file v_golden : TEXT open READ_MODE is c_goldenFile; -- this is LRM 1076-1987 style and implies that only simulator start and quit can open and close the file + variable v_log_line : LINE; + variable v_golden_line : LINE; + variable v_index : integer :=0; + variable v_comma : character; + variable v_sync : std_logic_vector(0 to c_file_len-1):=(others=>'0'); + variable v_val : std_logic_vector(0 to c_file_len-1):=(others=>'0'); + variable v_data : t_integer_matrix(0 to c_file_len-1, 1 to 2) := (others=>(others=>0)); + begin + -- wait 1 clock cycle to avoid that the output messages in the transcript window get lost in the 0 ps start up messages + proc_common_wait_some_cycles(clk, 1); + -- combinatorially read the file into the array + write(v_log_line, string'("reading golden file : ")); + write(v_log_line, c_goldenFile); + writeline(output, v_log_line); + loop + exit when endfile(v_golden); + readline(v_golden, v_golden_line); + + read(v_golden_line, v_sync(v_index)); -- sync + read(v_golden_line, v_comma); + + read(v_golden_line, v_val(v_index)); -- valid + read(v_golden_line, v_comma); + + read(v_golden_line, v_data(v_index,1)); -- real + read(v_golden_line, v_comma); + + read(v_golden_line, v_data(v_index,2)); -- imag + v_index := v_index + 1; + end loop; + write(v_log_line, string'("finished reading golden file")); + writeline(output, v_log_line); + + gold_file_data <= v_data; + gold_file_sync <= v_sync; + gold_file_val <= v_val; + wait; + end process; + + -- Show read data in Wave Window for debug purposes + gold_index <= gold_index + 1 when rising_edge(clk) and out_val='1'; + gold_sync <= gold_file_sync(gold_index); + gold_re <= gold_file_data(gold_index,1); + gold_im <= gold_file_data(gold_index,2); + + -- Verify the output of the DUT with the expected output from the golden reference file + p_verify_output : process(clk) + begin + -- Compare + if rising_edge(clk) then + if out_val='1' then + -- only write when out_val='1', because then the file is independent of cycles with invalid out_dat + assert out_sync = gold_sync report "Output sync error" severity error; + assert TO_SINT(out_re) = gold_re report "Output real data error" severity error; + assert TO_SINT(out_im) = gold_im report "Output imag data error" severity error; + end if; + end if; + end process; + + -- Write to default output file, this allows using command line diff or graphical diff viewer to compare it with the golden result file + p_write_output_file : process(clk) + file v_output : TEXT open WRITE_MODE is c_outputFile; -- this is LRM 1076-1987 style and implies that only simulator start and quit can open and close the file + variable v_line : LINE; + begin + if rising_edge(clk) then + if out_val='1' then + -- only write when out_val='1', because then the file is independent of cycles with invalid out_dat + write(v_line, out_sync); + write(v_line, string'(",")); + write(v_line, out_val); + write(v_line, string'(",")); + write(v_line, to_integer(signed(out_re))); + write(v_line, string'(",")); + write(v_line, to_integer(signed(out_im))); + writeline(v_output, v_line); + end if; + end if; + end process; + +end tb;