Search code examples
vhdlghdl

Reading a file in GHDL/VHDL


I am working on reading a text file in vhdl. There are many examples on this, but I am curious why this minimal showcase example doesn't work in GHDL. It works in ModelSim (by Mentor).

  1. Is this because of missing features in GHDL? (I did not find anything in docs/ github issues)
  2. Is this because of wrong standard that I am using without knowing?
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use STD.textio.all;

entity test is
end test;

architecture behavioral of test is
    file input : text;
begin
    process
    variable line_in : line;
    begin
        file_open(input, "input.txt");
        while not endfile(input) loop
            readline(input, line_in); 
        end loop;
        wait for 100 ns;
    end process;

end behavioral;

The output I get is:

./test:error: cannot open file "input.txt"

This means, there is no file/cannot be opened, but the file exists with correct access rights (proven in Modelsim). I tried this with full file name, too.

I'm using GHDL 0.37 on Linux with these flags: --ieee=synopsys --std=08

If

   file input : text;

is replaced with

    file input : text open read_mode is "input.txt";

and

        file_open(input, "input.txt");

is removed it works in GHDL.

However, I still don't know why the previous version did not work.


Solution

  • The example code is expected to fail. Note the lack of a procedure file_close call.

    After the execution of the wait for 100 ns; wait statement the execution of the process statement will resume in a future simulation cycle. The statements in a process are executed in order and after the last statement (the wait statement) the first statement

            file_open(input, "input.txt");
    

    will be executed again.

    Without an intervening file_close call a subsequent file_open call will fail.

    IEEE Std 1076-2008

    10.2 Wait statement

    The timeout clause specifies the maximum amount of time the process will remain suspended at this wait statement. If no timeout clause appears, the timeout clause for (STD.STANDARD.TIME'HIGH – STD.STANDARD.NOW) is assumed. It is an error if the time expression in the timeout clause evaluates to a negative value.

    11.3 Process statement

    The execution of a process statement consists of the repetitive execution of its sequence of statements. After the last statement in the sequence of statements of a process statement is executed, execution will immediately continue with the first statement in the sequence of statements.

    5.5.2 File operations:

    In the second form of FILE_OPEN, the value returned through the Status parameter indicates the results of the procedure call:

    — A value of OPEN_OK indicates that the call to FILE_OPEN was successful. If the call to FILE_OPEN specifies an external file that does not exist at the beginning of the call, and if the access mode of the file object passed to the call is write-only, then the external file is created.
    — A value of STATUS_ERROR indicates that the file object already has an external file associated with it.
    — A value of NAME_ERROR indicates that the external file does not exist (in the case of an attempt to read from the external file) or the external file cannot be created (in the case of an attempt to write or append to an external file that does not exist). This value is also returned if the external file cannot be associated with the file object for any reason.
    — A value of MODE_ERROR indicates that the external file cannot be opened with the requested Open_Kind.

    The first form of FILE_OPEN causes an error to occur if the second form of FILE_OPEN, when called under identical conditions, would return a Status value other than OPEN_OK.

    The question use of the file_open procedure call is of the first form. The second form would fail returning a Status parameter value of STATUS_ERROR, already associating an external file with the file object input.

    The fix for that would be to transform the wait statement to prevent the process from continuing to execute:

            wait; -- wait for 100 ns;
        end process;
    

    or provide an explicit file_close call so a subsequent file_open call would succeed. (This would cause a lot of host activity for no useful purpose.)

    The modified code could look like:

    -- library IEEE;
    -- use IEEE.STD_LOGIC_1164.all;  -- NOT USED
    use STD.textio.all;
    
    entity test is
    end test;
    
    architecture behavioral of test is
        file input : text;
    begin
        process
        variable line_in : line;
        begin
            file_open(input, "input.txt");
            while not endfile(input) loop
                readline(input, line_in);
                write (OUTPUT, line_in.all & LF);
            end loop;
            wait; -- wait for 100 ns;  -- EXECUTE ONCE
        end process;
    
    end behavioral;
    

    Yields:

    %% ghdl -a --ieee=synopsys  --std=08 test.vhdl
    %% ghdl -e --ieee=synopsys  --std=08 test
    %% ghdl -r --std=08 test
    some text
    more text
    yet some more text
    getting boring
    %% 
    

    Where a write to the file OUTPUT (the console) echos the contents of each line found in input.txt. Note that the end of line is removed by the readline procedure call and reintroduced to the string being written to OUTPUT.

    So why does the different file declaration succeed?

    architecture file_declaration of test is
        -- file input : text;
        file input:     text open read_mode is "input.txt";
    begin
        process
        variable line_in:   line;
        begin
            -- file_open(input, "input.txt");
            while not endfile(input) loop
                readline(input, line_in);
                write (OUTPUT, line_in.all & LF);
            end loop;
            wait for 100 ns;
        end process;
    
    end architecture file_declaration;
    

    There's only one call to file_open, an implicit call during elaboration of the file declaration (6.4.2.5 File declarations). The file remains open yet there are no remaining lines to read, determined by the endfile call. Here the endfile call would occur every 100 ns, which would likely result in your CPU utilization to increase as test executes until TIME'HIGH is reached. Performing the endfile call would result in host file operations resulting in suspension and resumption of the ghdl model execution. An effective test of only making endfile procedure calls.

    A wait statement (10.2) without a timeout clause (for 100 ns) will wait until TIME'HIGH effective ending the simulation without any intervening signal events or other process suspensions and resumptions or making TIME'HIGH/100 ns - 1 endfile procedure calls, each involving suspension and resumption of the shown process statement.

    You could also specify a simulation stop time on ghdl's command line, matching the usage in Modelsim most likely:

    %% ghdl -a --ieee=synopsys  --std=08 test.vhdl
    %% ghdl -e --ieee=synopsys  --std=08 test
    %% ghdl -r --std=08 test --stop-time=300ns
    some text
    more text
    yet some more text
    getting boring
    ./test:info: simulation stopped by --stop-time @300ns
    %% 
    

    Host file operations can incur a significant execution time penalty. If you were to assign the read values to a signal or variable of a composite (array or record) type object they can be reused without waiting on host file operations.