Search code examples
testingvhdltest-bench

Read file line of unknown size as string in VHDL


I'm trying to make a Test Bench where a file of one single line, where posible characters are "1" and "0". I've to read them all, and use one by one as input in my DUT.

So, in my TB, I've defined a process like the following, in order to read the file an pass it values to my DUT.

stim_proc: process

 file input_file: TEXT is in "DatosEntrada.dat";
 
 variable rdline : LINE;
 variable line_content : string ( 1 to 4);
 variable readed_char : character;
 
 variable j : integer := 0;

begin       

 while not endfile(input_file) loop
   readline(input_file, rdline);
   --read(rdline, line_content);
   
   for j in 1 to rdline'length-1 loop
     readed_char := line_content(j);
     
     if (readed_char = '1') then
       input <= '1';
     else
       input <= '0'; 
     end if;
     
     wait for clk_period;
   end loop;
 end loop;
end process;

I'm reading the first (and only) line of my file with the first readline execution. After this, this loop shouldn't execute again.

Then, data from file should be inside rdline. So I've to process it. In order to do it, I've tried to loop over rdline length, but this loop doesn't execute.

for j in 1 to rdline'length-1 loop

So I thought I need to read this line in order to loop over it, and tried to move its data to a string var. The problem is that vector var like string need to have a defined size, and I don't know the file line size.

I've tried reading each time 4 chars from rdline into a string, process it, then repeat. However, I couldn't make it work.

I've found quite lot examples about reading files which have defined line formats, like columns or expected integers.

But how can I read an unknown text of one line?


Solution

  • This readed_char := line_content(j); doesn't work when line_content isn't loaded. Otherwise your attempt to read values is basically sound.

    The end of line is not contained in a read LINE buffer, there's no reason to not read the last character of rdline. An end of line is signaled by one or more format effectors other than horizontal tab, and just the line contents are present.

    There's also this inference that you have some relationship to a clock edge and not just a clock period. The following example shows that. Note you can also supply an offset from an edge using a wait for time_value.

    A loop constant is declared in the loop statement. The variable j you declared is not the same j the loop uses. The loop statement hides the j in the outer declarative region (the variable declaration in the process statement).

    Your code treats any other character in the string buffer than '1' as a '0'. I didn't change that, do demonstrate it. You should be aware of the impact.

    A LINE is an allocated string of some length dependent on the length of a line in your read file. Every time you call readline the string rdline points to is updated. It doesn't leak memory, the previous buffer rdline pointed to released. You can read the length by using the 'RIGHT attribute or as in this case simply consume all the characters.

    There may be line length limits in a VHDL tool implementation. There are none defined in the standard other than the maximum length of a string (POSITIVE'RIGHT).

    An MCVE:

    library ieee;
    use ieee.std_logic_1164.all;
    use std.textio.all;
    
    entity foo is
    end entity;
    
    architecture fum of foo is
        signal input:           std_logic ;
        signal clk:             std_logic := '0';
        constant clk_period:    time := 10 ns;
    begin
        
    stim_proc: 
        process
            file input_file: TEXT is in "DatosEntrada.dat";
                variable rdline:    LINE;
                -- variable line_content : string ( 1 to 4);
                -- variable readed_char : character;
                -- variable j:         integer := 0;
        begin       
            while not endfile(input_file) loop
                readline(input_file, rdline);
                --read(rdline, line_content);
                -- for j in 1 to rdline'length - 1 loop -- EOL not in rdline
                for j in rdline'range loop
                    -- readed_char := line_content(j);
                    -- if readed_char = '1' then
                    if rdline(j) = '1' then   -- changed
                        input <= '1';
                    else
                        input <= '0'; 
                    end if;
                    -- wait for clk_period;  -- sync to edge instead
                    wait until falling_edge(clk); -- input related to clk edge
                end loop;
            end loop;
            wait;     -- added prevents needless loops
        end process;
        
    CLOCK:
        process
        begin
            wait for clk_period/2;
            clk <= not clk;
            if now > 32 * clk_period then
                wait;
            end if;
        end process;
        
    end architecture;
    

    And for DatosEntrada.dat containing:

    11011110001HELLO11230000

    That produces:

    foo.png

    Where you can see all non '1' characters are interpreted as '0'.