Developing design: moving average filter. Part 7 (last) – testbench.

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.

*** *** ***

Leave a Reply

Your email address will not be published. Required fields are marked *