Before you read that post, I encourage you to go through my two previous posts (here and here), which are about working with files. They describe basic approach and explain why that method is not recommended. Instead of this, in the following post I am proposing more standard, popular and convenient way to work with files in the VHDL.
TextIO is a package created to simplify working with files. It works in the same way on every operating system with almost every type available in the VHDL.
How does it work?
Whole procedure looks very similar to basic process. Following steps have to be taken to use TextIO:
1. Declare object of the file type
2. Declare variable of the type line
3. Open the file
4. Perform write/read operations
5. Close the file
Comparing to basic way, here, it is not needed to define type of a file. Why? Because that definition is already done in the package. If you look at the sources of the package, you will find it. There is defined a new type of a file: text. All data in files is treated as string.
1. Declare object of the file type
Declaration of the pointer to the file has to be done.
file fptr : text;
2. Declare variable of the type line
Comparing to basic way, this part is new. TextIO package reads/writes always a whole line, so it needs declaration of a variable of that type.
variable flptr : line;
Definition of type line is included in TextIO package.
3. Open the file
Opening files is done by open_file procedure. Details are described here.
4. Perform write/read operations
Read/write processes are always performed in two steps. For reading, first must be read a whole line from the file and then, values can be taken from that line one after another. For writing, first all values must be written to a line, and then, a whole line is written to a file.
Reading:
Line is read from the file by using procedure readline.
procedure readline (file f: text; l: inout line);
Now, access to values stored in the line variable is made by using read procedures. Every type has its own procedures. There are two various procedures, which differs by number of arguments. Below are shown both procedures. I think they are self-explanatory.
procedure read (l: inout line; value: out type);
procedure read (l: inout line; value: out type; good: out boolean);
Writing:
To write data to a file, first all values have to be written to a line. It is done by write procedure. Every type has its own procedure. It allows to write not only specific type, but allows to adjust the data and set the space for every value.
procedure write (l: inout line; value: in type; justified: in side := right; field: in width := 0);
After creating a line, it can be written to a file by using writeline procedure.
procedure writeline (file f: text; l: inout line);
* Declarations of all procedures (read, readline, write, writeline) are in TextIO package. I encourage you to look into sources of the package. You will see there definitions and procedures for different types. It will make you more familiar with the package.
5. Close the file
Closing files is done by close_file procedure. Details are described here.
Examples:
Reading:
library ieee; use ieee.std_logic_1164.all; use std.textio.all; entity ReadDataFromFile is end ReadDataFromFile; architecture ReadDataFromFile_rtl of ReadDataFromFile is constant C_FILE_NAME :string := "DataIn.dat"; constant C_DATA1_W :integer := 16; constant C_DATA3_W :integer := 4; constant C_CLK :time := 10 ns; signal clk :std_logic := '0'; signal rst :std_logic := '0'; signal data1 :std_logic_vector(C_DATA1_W-1 downto 0); signal data2 :integer; signal data3 :std_logic_vector(C_DATA3_W-1 downto 0); signal eof :std_logic := '0'; file fptr: text; begin ClockGenerator: process begin clk <= '0' after C_CLK, '1' after 2*C_CLK; wait for 2*C_CLK; end process; rst <= '1', '0' after 100 ns; GetData_proc: process variable fstatus :file_open_status; variable file_line :line; variable var_data1 :std_logic_vector(C_DATA1_W-1 downto 0); variable var_data2 :integer; variable var_data3 :std_logic_vector(C_DATA3_W-1 downto 0); begin data1 <= (others => '0'); var_data1 := (others => '0'); data2 <= 0; var_data2 := 0; data3 <= (others => '0'); var_data3 := (others => '0'); eof <= '0'; wait until rst = '0'; file_open(fstatus, fptr, C_FILE_NAME, read_mode); while (not endfile(fptr)) loop wait until clk = '1'; readline(fptr, file_line); hread(file_line, var_data1); data1 <= var_data1; read(file_line, var_data2); data2 <= var_data2; read(file_line, var_data3); data3 <= var_data3; end loop; wait until rising_edge(clk); eof <= '1'; file_close(fptr); wait; end process; end ReadDataFromFile_rtl;
Writing:
library ieee; use ieee.std_logic_1164.all; use std.textio.all; entity WriteDataToFile is end WriteDataToFile; architecture WriteDataToFile_rtl of WriteDataToFile is constant C_FILE_NAME :string := "DataOut.dat"; constant C_DATA1_W :integer := 16; constant C_DATA3_W :integer := 4; constant C_CLK :time := 10 ns; signal clk :std_logic := '0'; signal rst :std_logic := '0'; signal eof :std_logic := '0'; file fptr: text; begin ClockGenerator: process begin clk <= '0' after C_CLK, '1' after 2*C_CLK; wait for 2*C_CLK; end process; rst <= '1', '0' after 100 ns; WriteData_proc: process variable fstatus :file_open_status; variable file_line :line; variable var_data1 :std_logic_vector(C_DATA1_W-1 downto 0); variable var_data2 :integer; variable var_data3 :std_logic_vector(C_DATA3_W-1 downto 0); begin var_data1 := (0 => '1', others => '0'); var_data2 := 0; var_data3 := (3 => '1', others => '0'); eof <= '0'; wait until rst = '0'; file_open(fstatus, fptr, C_FILE_NAME, write_mode); while (var_data2 < 4) loop wait until clk = '1'; var_data1 := var_data1(C_DATA1_W-2 downto 0) & '0'; var_data2 := var_data2 + 1; var_data3 := '0' & var_data3(C_DATA3_W-1 downto 1); hwrite(file_line, var_data1, left, 5); write(file_line, var_data2, right, 2); write(file_line, var_data3, left, 5); writeline(fptr, file_line); end loop; wait until rising_edge(clk); eof <= '1'; file_close(fptr); wait; end process; end WriteDataToFile_rtl;
* I used in examples procedure hwrite, which is accessible starting from the standard VHDL-2008. Remember to set simulator to use that standard. If it is impossible, it should be easy to modify code to previous standard, just by removing that procedure.
*** *** ***
All source codes used in that post you can find on gitlab.com.
*** *** ***