This is the last part about creating almost automatic testbench for Moving Average Filter. In the previous post I presented blocks for reading and writing. Now it is time to connect all blocks to each other in the main testbench. Testbench contains three blocks:
- block which reads data from external file,
- filter,
- block which writes output data to external file.
Testbench additionally contains clock generation and other processes which drive control and testbench signals.
library ieee; use ieee.std_logic_1164.all; entity tb_AvgFilter is generic ( G_DATA_W :integer := 16; G_FIL_L :integer := 4 ); end entity; architecture tb_AvgFilter_rtl of tb_AvgFilter is component ReadDataFromFile is generic ( G_FILE_NAME :string := "DataIn.dat"; G_DATA_W :integer := 16 ); port ( clk :in std_logic; en :in std_logic; o_data_en :out std_logic; ov_data :out std_logic_vector(G_DATA_W-1 downto 0); o_eof :out std_logic ); end component; component AvgFilter is generic ( G_DATA_W :integer := 16; G_FIL_L :integer := 4 ); port ( clk :in std_logic; rst :in std_logic; en :in std_logic; iv_data :in std_logic_vector(G_DATA_W-1 downto 0); ov_avg :out std_logic_vector(G_DATA_W-1 downto 0) ); end component; component WriteDataToFile is generic ( G_FILE_NAME :string := "DataOut.dat"; G_DATA_W :integer := 16 ); port ( clk :in std_logic; en :in std_logic; iv_data :in std_logic_vector(G_DATA_W-1 downto 0); i_eod :in std_logic; o_eof :out std_logic ); end component; signal clk :std_logic := '0'; signal rst :std_logic := '0'; signal en :std_logic := '0'; signal DataEn :std_logic := '0'; signal DataEn_reg1 :std_logic := '0'; signal DataEn_reg2 :std_logic := '0'; signal DataIn :std_logic_vector(G_DATA_W-1 downto 0); signal eof :std_logic := '0'; signal eof_reg1 :std_logic := '0'; signal eof_reg2 :std_logic := '0'; signal DataOut :std_logic_vector(G_DATA_W-1 downto 0); constant ClkGenConst :time := 10 ns; begin ReadData_inst: ReadDataFromFile generic map ( G_FILE_NAME => "DataIn.dat", G_DATA_W => G_DATA_W ) port map ( clk => clk, en => en, o_data_en => DataEn, ov_data => DataIn, o_eof => eof ); UUT: AvgFilter generic map ( G_DATA_W => G_DATA_W, G_FIL_L => G_FIL_L ) port map ( clk => clk, rst => rst, en => DataEn, iv_data => DataIn, ov_avg => DataOut ); WriteData_inst: WriteDataToFile generic map ( G_FILE_NAME => "DataOut.dat", G_DATA_W => G_DATA_W ) port map ( clk => clk, en => DataEn_reg2, iv_data => DataOut, i_eod => eof_reg2, o_eof => open ); ClockGenerator: process begin clk <= '0' after ClkGenConst, '1' after 2*ClkGenConst; wait for 2*ClkGenConst; end process; rst <= '1', '0' after 100 ns; en_proc: process begin en <= '0'; wait until rst = '0'; wait until rising_edge(clk); en <= '1'; wait until eof = '1'; en <= '0'; wait; end process; DelayEnForWrite: process(clk) begin if rising_edge(clk) then DataEn_reg1 <= DataEn; DataEn_reg2 <= DataEn_reg1; eof_reg1 <= eof; eof_reg2 <= eof_reg1; end if; end process; end architecture;
One comment about DelayEnForWrite process. Its role is to pass enable signal to block, which writes output data to file. It informs the block about active samples, which should be stored.
Finally all sources can be compiled and simulated:)
Data comparison
After all, you will get output data, which then can be compared with data gotten from Octave. For comparing, you can use simple Octave script:
clear; SimRes = ReadMat('DataOut.dat', '%x'); OctRes = ReadMat('DataOut_Octave.dat', '%x'); SimLen = size(SimRes); OctLen = size(OctRes); CompRes = isequal(SimRes, OctRes); if CompRes disp('Comparison OK!'); else disp('Comparison NOT OK!'); end
If both files are equal, it means that designed filter works well.
*** *** ***
All source codes used in that post you can find on gitlab.com.
*** *** ***