The text I/O features of VHDL make it possible to open one or more data files, read lines from those files, and parse the lines to form individual data elements, such as elements in an array or record. To support the use of files, VHDL has the concept of a file data type, and includes standard, built-in functions for opening, reading from, and writing to file data types. (These data types and functions were described in the section Objects, Data Types and Operators.) The textio package, which is included in the standard library, expands on the built-in file type features by adding text parsing and formatting functions, functions and special file types for use with interactive ("std_input" and "std_output") I/O operations, and other extensions.

 

The following example demonstrates how you can use the text I/O features of VHDL to read test data from an ASCII file, using the standard text I/O features.

 

-------------------------------------------------------

-- Test bench, VHDL ‘93 style

--

library ieee;

use ieee.std_logic_1164.all;

use std.textio.all;

use work.fib;    -- Get the design out of library 'work'

 

entity testfib is

end entity testfib;

 

architecture stimulus of testfib is

    component fib is

       port (Clk,Clr: in std_logic;

             Load: in std_ulogic;

             Data_in: in std_ulogic_vector(15 downto 0);

             S: out std_ulogic_vector(15 downto 0));

    end component fib;

 

    function str_to_stdvec(inp: string) return std_ulogic_vector is

        variable temp: std_ulogic_vector(inp'range) := (others => 'X');

    begin

        for i in inp'range loop

            if (inp(i) = '1') then

                temp(i) := '1';

            elsif (inp(i) = '0') then

                temp(i) := '0';

            end if;

        end loop;

        return temp;

    end function str_to_stdvec;

 

    function stdvec_to_str(inp: std_ulogic_vector) return string is

        variable temp: string(inp'left+1 downto 1) := (others => 'X');

    begin

        for i in inp'reverse_range loop

            if (inp(i) = '1') then

                temp(i+1) := '1';

            elsif (inp(i) = '0') then

                temp(i+1) := '0';

            end if;

        end loop;

        return temp;

    end function stdvec_to_str;

 

    signal Clk,Clr: std_ulogic;

    signal Load: std_ulogic;

    signal Data_in: std_ulogic_vector(15 downto 0);

    signal S: std_ulogic_vector(15 downto 0);

    signal done: std_ulogic := '0';

 

    constant PERIOD: time := 50 ns;

 

begin

    UUT: fib port map(Clk=>Clk,Clr=>Clr,Load=>Load,

                      Data_in=>Data_in,S=>S);

 

    Clock: process

        variable c: std_ulogic := '0';

    begin

        while (done = '0') loop

            wait for PERIOD/2;

            c := not c;

            Clk <= c;

        end loop;

    end process Clock;  

 

    Read_input: process  

        file vector_file: text;

 

        variable stimulus_in: std_ulogic_vector(33 downto 0);

        variable S_expected: std_ulogic_vector(15 downto 0);

        variable str_stimulus_in: string(34 downto 1);

        variable err_cnt: integer := 0;

        variable file_line: line;

 

    begin

 

        file_open(vector_file,"tfib93.vec",READ_MODE);

 

        wait until rising_edge(Clk);

 

        while not endfile(vector_file) loop

            readline (vector_file,file_line);

            read (file_line,str_stimulus_in) ;

            assert (false)

                report "Vector: " & str_stimulus_in

                severity note;

            stimulus_in := str_to_stdvec (str_stimulus_in);

 

            wait for 1 ns;

 

            --Get input side of vector...

            Clr <= stimulus_in(33);

            Load <= stimulus_in(32);

            Data_in <= stimulus_in(31 downto 16);

 

            --Put output side (expected values) into a variable...

            S_expected := stimulus_in(15 downto 0);

 

            wait until falling_edge(Clk);

 

            -- Check the expected value against the results...

            if (S /= S_expected) then

                err_cnt := err_cnt + 1;

                assert false

                    report "Vector failure!" & lf &

                    "Expected S to be  " & stdvec_to_str(S_expected) & lf &

                    "but its value was " & stdvec_to_str(S) & lf

                    severity note;

            end if;

        end loop;

 

        file_close(vector_file);

 

        done <= '1';

 

        if (err_cnt = 0) then

            assert false

                report "No errors." & lf & lf

                severity note;

        else

            assert false

                report "There were errors in the test." & lf

                severity note;

        end if;

        wait;

 

    end process Read_input;

end architecture stimulus;

 

-- Add a configuration statement. This statement actually states the

-- default configuration, and so it is optional.

configuration build1 of testfib is

    for stimulus

        for DUT: fib use entity work.fib(behavior)

            port map(Clk=>Clk,Clr=>Clr,Load=>Load,

                      Data_in=>Data_in,S=>S);

        end for;

    end for;

end configuration build1;

 

This test bench reads lines from an ASCII file and applies the data contained in each line as a test vector to stimulate and test a simple Fibonacci sequence generator circuit. It begins with our by-now-familiar entity-architecture pair:

 

This test bench reads files of text "dynamically" during simulation, so the test bench does not have to be recompiled when test stimulus is added or modified. This is a big advantage for very large designs.

 

What does the test vector file that this test bench reads look like? The following file (testfib.vec) describes one possible sequence of tests that could be performed using this test bench:

 

1000000000000000000000000000000000

0000000000000000000000000000000001

0000000000000000000000000000000001

0000000000000000000000000000000010

0000000000000000000000000000000011

0000000000000000000000000000000101

0000000000000000000000000000001000

0000000000000000000000000000001101

0000000000000000000000000000010101

0000000000000000000000000000100010

0000000000000000000000000000110111

0000000000000000000000000001011001

0000000000000000000000000010010000

0000000000000000000000000011101001

0000000000000000000000000101111001

0000000000000000000000001001100010

0000000000000000000000001111011011

0000000000000000000000011000111101

0000000000000000000000101000011000

0000000000000000000001000001010101

0000000000000000000001101001101101

0000000000000000000010101011000010

0000000000000000000100010100101111

0000000000000000000110111111110001

0000000000000000001011010100100000

0000000000000000000010010100010001

0000000000000000000000000000000001

0000000000000000000000000000000001

0000000000000000000000000000000010

0000000000000000000000000000000011

0000000000000000000000000000000101

0000000000000000000000000000001000

 

This file could have been entered manually, using a text editor. Alternatively, it could have been generated from some other software package or from a program written in C, Basic or any other language. Reading text from files opens many new possibilies for testing and for creating interfaces between different design tools.

 

Although test vectors are quite useful for tabular test data, they are not particularly readable. In the last example of this chapter, we will describe how you can read and process test stimulus files that are more command-oriented, rather than simply being tables of binary values.

 

Reading Non-tabular Data from Files

You can use VHDL’s text I/O features to read and write many different built-in data types, including such data types as characters, strings, and integers. This is a powerful feature of the language that you will make great use of as you become proficient with the language.

 

VHDL’s text I/O features are somewhat limited, however, when it comes to reading data that is not expressed as one of the built-in types defined in Standard 1076. The primary example of this is when you wish to read or write standard logic data types. In the previous example (the Fibonacci sequence generator), we made use of type coversion functions to read standard logic input data as characters. This method works fine, but it is somewhat clumsy. A better way to approached this common problem is to develop a reusable package of functions for reading and writing standard logic data. Writing a comprehensive package of such functions is not a trivial task. It would probably require a few days of coding and debugging.

 

Fortunately, one such package already exists and is in widespread use. This package, std_logic_textio, was originally developed by Synopsys. Synopsys allows the package to be used and distributed without restriction. We will use the std_logic_textio package to demonstrate how you might read data fields from a file and write other data to another file (or, in this case, to the console or simulator transcript window).

 

The circuit that we will be testing with our test bench is a 32-bit adder-subtractor unit, the complete source code for which is provided on the companion CD-ROM. The test bench that we wish to write will read information from a file in the form of hexadecimal numeric values. The data file, which we will name TST_ADD.DAT, will include both the inputs and the expected outputs for the circuit. A listing of TST_ADD.DAT, containing a small number of test lines, is shown below:

 

0 00000001 00000001 00000002 0

0 00000002 00000002 00000004 0

0 00000004 00000004 00000008 0

0 FFFFFFFF FFFFFFFF FFFFFFFE 1

0 0000AAAA AAAA0000 AAAAAAAA 0

0 158D7129 E4C28B56 FA4FFC7F 0

1 00000001 00000001 00000000 0

1 A4F67B92 00000001 5B09846F 0

1 FFFFFFFF FFFFFFFE FFFFFFFF 0

1 FFFFFFFE FFFFFFFF 00000001 1

1 00000002 00000004 00000002 1

 

The standard text I/O features defined in VHDL standard 1076 do not include procedures to read data in hexadecimal format, so we will make use of the hread procedure provided in the Synopsys std_logic_textio package. Hread accepts the same arguments as the standard read procedure, but allows values to be expressed in hexadecimal format. We will use hread to read the second, third and fourth fields in each line of the file, as these fields are represented in hexadecimal format.

 

Because the first and last fields of the data file are single-bit values of type std_ulogic, we will also make use of an overloaded read procedure provided in std_logic_textio. VHDL’s built-in read procedure is not capable of reading std_ulogic values, so the std_logic_textio package includes additional read procedure definitions that extend read for these values.

 

Finally, we wish to display the results of simulation in the simulator’s transcript window, so we use the hwrite and overloaded write procedures provided in std_logic_textio to format and display the data values. Once again, these are procedures that are not provided in the standard VHDL text  I/O package.

 

library ieee;

use ieee.std_logic_1164.all;

use work.all;

use std.textio.all;

 

library textutil;       -- Synposys Text I/O package

use textutil.std_logic_textio.all;

 

entity tst_add is

end tst_add;

 

architecture readhex of tst_add is

    component adder32 is

        port (cin: in std_ulogic;

              a,b: in std_ulogic_vector(31 downto 0);

              sum: out std_ulogic_vector(31 downto 0);

              cout: out std_ulogic);

    end component;

    for all: adder32 use entity work.adder32(structural);

    signal Clk: std_ulogic;

    signal x, y: std_ulogic_vector(31 downto 0);

    signal sum: std_ulogic_vector(31 downto 0);

    signal cin, cout: std_ulogic;

 

    constant PERIOD: time := 200 ns;

 

begin

    UUT: adder32 port map (cin, x, y, sum, cout);

 

    readcmd: process

 

        -- This process loops through a file and reads one line

        -- at a time, parsing the line to get the values and

        -- expected result.

        --

        -- The file format is CI A B SUM CO, with A, B and SUM

        -- expressed as hexadecimal values.

 

        file cmdfile: TEXT;       -- Define the file 'handle'

        variable line_in,line_out: Line; -- Line buffers

        variable good: boolean;   -- Status of the read operations

 

        variable CI, CO: std_ulogic;

        variable A,B: std_ulogic_vector(31 downto 0);

        variable S: std_ulogic_vector(31 downto 0);

        constant TEST_PASSED: string := "Test passed:";

        constant TEST_FAILED: string := "Test FAILED:";

 

        -- Use a procedure to generate one clock cycle...

        procedure cycle (n: in integer) is

        begin

            for i in 1 to n loop

                Clk <= '0';

                wait for PERIOD / 2;

                Clk <= '1';

                wait for PERIOD / 2;

            end loop;

        end cycle;

 

    begin

 

        -- Open the command file...

 

        FILE_OPEN(cmdfile,"TST_ADD.DAT",READ_MODE);

 

        loop

 

            if endfile(cmdfile) then  -- Check EOF

                assert false

                    report "End of file encountered; exiting."

                    severity NOTE;

                exit;

            end if;

 

            readline(cmdfile,line_in);     -- Read a line from the file

            next when line_in'length = 0;  -- Skip empty lines

 

            read(line_in,CI,good);     -- Read the CI input

            assert good

                report "Text I/O read error"

                severity ERROR;

 

            hread(line_in,A,good);     -- Read the A argument as hex value

            assert good

                report "Text I/O read error"

                severity ERROR;

 

            hread(line_in,B,good);     -- Read the B argument

            assert good

                report "Text I/O read error"

                severity ERROR;

 

            hread(line_in,S,good);     -- Read the Sum expected resulted

            assert good

                report "Text I/O read error"

                severity ERROR;

 

      read(line_in,CO,good);     -- Read the CO expected resulted

            assert good

                report "Text I/O read error"

                severity ERROR;

 

            cin <= CI;

            x <= A;

            y <= B;

 

            wait for PERIOD;   -- Give the circuit time to stabilize

 

            if (sum = S) then

                write(line_out,TEST_PASSED);

            else

                write(line_out,TEST_FAILED);

            end if;

            write(line_out,CI,RIGHT,2);

            hwrite(line_out,A,RIGHT,9);

            hwrite(line_out,B,RIGHT,9);

            hwrite(line_out,sum,RIGHT,9);

            write(line_out,cout,RIGHT,2);

            writeline(OUTPUT,line_out);     -- write the message

 

        end loop;

 

        wait;

 

    end process;

 

end architecture readhex;