İçeriğe geç

How to Use txt files in VHDL Test Bench?

Introduction

While testing a VHDL module, the declared input/output ports should be tested as much as possible against all possible scenarios. For example, if a module you are designing has 5 input and 1 output ports, all input signal combinations that can enter these ports should be tested for a completely reliable design. However, the number of input combinations can reach hundreds of thousands, maybe millions for some designs. Of course, it is not possible for us to consider all combinations for these situations. Different methodologies are applied for such cases, but this will be the subject of another article. In this article, we will perform an arithmetic multiplication by assuming that our VHDL module we designed has 12 input combinations, and instead of manually defining these 12 input combinations, we will access them from a txt file.

RTL Design

Since the main theme of the article is to extract data from the txt file, our VHDL module will be an ordinary multiplication operation. Our module, which has two 8-bit wide input ports, will have one 16-bit wide output port. In this context, let’s create our design as follows.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL; -- PACKAGE FOR MULTIPLICATION OF UNSIGNED VECTORS

entity design is
  generic (
     IN_DATA_WIDTH  : integer range 0 to 8 := 8;   -- Input Data Width
     OUT_DATA_WIDTH : integer range 0 to 16 := 16  -- Output Data Width
  );
  Port ( 
     i_clk : in  std_logic;  -- Global Clock
     i_rst : in  std_logic;  -- Global Reset
     i_a   : in  std_logic_vector(IN_DATA_WIDTH - 1 downto 0); -- First Input
     i_b   : in  std_logic_vector(IN_DATA_WIDTH - 1 downto 0); -- Second Input
     o_c   : out std_logic_vector(OUT_DATA_WIDTH - 1 downto 0) -- Output
  );
end design;

architecture Behavioral of design is
    
    -- Registers
    signal a : std_logic_vector(IN_DATA_WIDTH  - 1 downto 0) := (others => '0'); 
    signal b : std_logic_vector(IN_DATA_WIDTH  - 1 downto 0) := (others => '0');
    signal c : std_logic_vector(OUT_DATA_WIDTH - 1 downto 0) := (others => '0');

begin
    -- Registering
    a <= i_a;
    b <= i_b;
    o_c <= c;
    
    -- Operation
    process (i_clk,i_rst)
    begin
        if(rising_edge(i_clk)) then
            if(i_rst = '1') then
                c <= (others => '0');
            else
                c <= a * b;
            end if;
        end if;
    end process;

end Behavioral;

Test Bench Design

As we said at the beginning, we will test 12 input combinations in the test bench. Since we have 2 inputs, we can use separate txt files as “input_a” and “input_b” for these two inputs. Let’s perform the process by reading these files simultaneously.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use STD.TEXTIO.ALL;               -- TEXT PACKAGE TO READ TXT FILES
use IEEE.STD_LOGIC_TEXTIO.ALL;    -- TEXT PACKAGE TO READ TXT FILES

entity test is
--  Port ( );
end test;

architecture Behavioral of test is

    constant in_data_width : integer range 0 to 8 := 8;    -- Input width
    constant out_data_width : integer range 0 to 16 := 16; -- Output Width

    signal clk  : std_logic := '0'; -- Global Clock
    signal rst  : std_logic := '0'; -- Global Reset
    signal a    : std_logic_vector(in_data_width  - 1 downto 0) := (others => '0'); -- Input a
    signal b    : std_logic_vector(in_data_width  - 1 downto 0) := (others => '0'); -- Input b
    signal c    : std_logic_vector(out_data_width - 1 downto 0) := (others => '0'); -- Output c
    
    signal read_txt  : std_logic := '0';  -- Flag that starts to read data from txt file
    signal eofile_in : std_logic := '0';  -- Flag that indicates txt file full read
    
begin

-- Generate 100 MHz Clock
gen_clk: process 
begin
    clk <= '1';
    wait for 5 ns;
    clk <= '0';
    wait for 5 ns;
end process;

-- Reset module for 30 ns, then drop
rst <= '1', '0' after 30 ns;

-- Start to read files after 40 ns (10 ns after reset drops)
-- and keep it high until whole txt files end 
read_txt_gen: process 
begin
    read_txt <= '0';
    wait for 40 ns;
    read_txt <= '1';
    wait until rising_edge(eofile_in);
    read_txt <= '0';   
    wait;
end process;

read_a: process (clk,rst)
    file in_file       : text open read_mode is "C:\Users\Berk\Desktop\input_a.txt"; -- Input txt file for input a
    variable in_line   : line;                                                       -- Input line in txt file
    variable data_read : std_logic_vector(in_data_width - 1 downto 0);               -- Data in line of txt file 
begin
    if rising_edge(clk) then
        if (rst = '1') then
           a <= (others => '0');     
        else
           if(read_txt = '1') then
               if(not endfile(in_file)) then
                   readline(in_file, in_line);  -- read the line in file
                   read(in_line,data_read);     -- read the data in line
                   a <= data_read;              -- assign the data to output
               else
                  eofile_in <= '1'; 
               end if;
           end if;
        end if;
    end if;
end process;

read_b: process (clk,rst)
    file in_file       : text open read_mode is "C:\Users\Berk\Desktop\input_b.txt";   -- Input txt file for input b 
    variable in_line   : line;                                                         -- Input line in txt file     
    variable data_read : std_logic_vector(in_data_width - 1 downto 0);                 -- Data in line of txt file   
begin
    if rising_edge(clk) then
        if (rst = '1') then
           b <= (others => '0'); 
        else
           if(read_txt = '1') then
               if(not endfile(in_file)) then     -- read the line in file    
                  readline(in_file, in_line);    -- read the data in line    
                  read(in_line,data_read);       -- assign the data to output
                  b <= data_read;
               end if;
           end if;
        end if;
    end if;
end process;

-- Module initialization   
init: entity work.design 
    generic map (
        IN_DATA_WIDTH => in_data_width,
        OUT_DATA_WIDTH => out_data_Width
    ) port map (
        i_clk => clk,
        i_rst => rst,
        i_a   => a,
        i_b   => b,
        o_c   => c
    );

end Behavioral;

As seen in the code above, input_a and input_b files are read simultaneously and assigned to a, b inputs as long as the read_txt command is logic-1. Since our design is designed to be sequential to the clock signal, the result of the input combination will come out after 1 clock.

Simulation Results

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.