从 VHDL 中的 *.txt 文件中读取特定行

Reading a specific line from a *.txt file in VHDL

我正在编写应该以某种方式模拟指令内存读取的代码。内存(数据存储在 1 字节长的字中)由每行 1 个字的 *.txt 文件表示;因为指令是 32 位长,所以我必须一次检索 4 个单词,以便将它们连接起来并构建一个完整的指令,作为输出发送。这样的内存不是按顺序访问的,但要读取的指令可能位于文件的任何行(作为跳转或分支的结果)。 举个例子,内存文本文件看起来是这样的(字符串"InstrN"实际上并不在文件中;我写在这里只是为了指出后续指令之间的区别。真正的文件只有8个每行位数)

Instr1 11111111
       11111111
       11111111
       11111111
Instr2 00000000
       ........

因此,我工作的核心在于读取这个文件的过程,为此我编写了如下代码:

read_instruction_memory : PROCESS(clk)
    FILE     mem           : TEXT; 
    VARIABLE mem_line      : LINE;
    VARIABLE read_byte     : STD_ULOGIC_VECTOR(7 DOWNTO 0);     -- byte of read memory
    VARIABLE line_counter  : INTEGER := 0;              -- counter to keep track of how many lines have already been read; it's initialized to 0 at the beginning of the process
    VARIABLE target_line   : INTEGER;                   -- line to be read, computed according to "pc" (program counter)
BEGIN 
    IF (clk'EVENT AND clk = '1') THEN 
        file_open(mem, "instruction_memory.txt", READ_MODE);
        target_line := to_integer(unsigned(pc));    
        WHILE line_counter < (target_line + 4) LOOP
            IF NOT endfile(mem) THEN                    -- discarded lines (don't correspond to the wanted lines)
                readline(mem, mem_line);
                IF line_counter >= target_line  THEN        -- subsequent four words are loaded
                    read(mem_line, read_byte);
                    instr(7+8*(line_counter - target_line) DOWNTO 8*(line_counter - target_line)) <= to_StdUlogicVector(read_byte);
                END IF;
            END IF;
            line_counter := line_counter + 1;
        END LOOP;
        file_close(mem);
    END IF;
END PROCESS read_instruction_memory;

基本上,在时钟上升沿,进程进入循环,每次迭代读取一行,忽略它直到它对应于要检索的指令中的一个字;然后,将其存储到指令 instr 的适当部分(在向量中从右到左)。我在每次执行进程时打开和关闭文件的事实意味着读取每次都从文件的开头开始,不是吗?

问题是它不起作用:当我开始模拟时,std_ulogic_vector 结果总是不确定的,我就是不明白为什么。请让我知道这是一个 "algorithmic" 错误还是只是因为我对 I/O 函数的理解不足。

创建 MCVE:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;

entity readinstr is
end entity;

architecture foo of readinstr is
    signal pc:      std_ulogic_vector (7 downto 0) := x"08";
    signal clk:     std_ulogic := '0';
    signal instr:   std_ulogic_vector (31 downto 0);

begin
read_instruction_memory: 
    process (clk)
        file     mem:            text; 
        variable mem_line:       line;
        variable read_byte:      std_ulogic_vector(7 downto 0);     -- byte of read memory
        variable line_counter:   integer := 0;              -- counter to keep track of how many lines have already been read; it's initialized to 0 at the beginning of the process
        variable target_line:    integer;                   -- line to be read, computed according to "pc" (program counter)
    begin 
        if clk'event and clk = '1' then 
            file_open(mem, "instruction_memory.txt", read_mode);
            target_line := to_integer(unsigned(pc));    
            while line_counter < target_line + 4 loop
                if not endfile(mem) then                    -- discarded lines (don't correspond to the wanted lines)
                    readline(mem, mem_line);
                    if line_counter >= target_line  then        -- subsequent four words are loaded
                        read(mem_line, read_byte);
                        instr(7+8*(line_counter - target_line) downto 8*(line_counter - target_line)) <= to_stdulogicvector(read_byte);
                        report "read_byte = " & to_string(read_byte);
                    end if;
                end if;
                line_counter := line_counter + 1;
            end loop;
            file_close(mem);
        end if;
    end process read_instruction_memory;

MONITOR:
    process (instr)
    begin
        if now > 0 ns then
            report "instr = " & to_string(instr);
        end if;
    end process;

CLOCK:
    process
    begin
        wait for 10 ns;
        clk <= not clk;
        wait for 10 ns;
        wait;
    end process;
end architecture;

与 instruction_memory.txt:

00000000
00000001
00000010
00000011
00010000
00010001
00010010
00010011
00100000
00100001
00100010
00100011
00110000
00110001
00110010
00110011

显示:

ghdl -a --std=08 readinstr.vhdl
ghdl -e --std=08 readinstr
ghdl -r readinstr
readinstr.vhdl:32:25:@10ns:(report note): read_byte = 00100000
readinstr.vhdl:32:25:@10ns:(report note): read_byte = 00100001
readinstr.vhdl:32:25:@10ns:(report note): read_byte = 00100010
readinstr.vhdl:32:25:@10ns:(report note): read_byte = 00100011
readinstr.vhdl:45:13:@10ns:(report note): instr = 00100011001000100010000100100000

您的代码似乎确实在做正确的事情。

您在时钟边沿的 if 语句的最后一条语句中关闭文件,并在 if 语句的第一条语句中打开文件。这应该适用于连续时钟(未在此 MCVE 中测试)。

在每个挂起的进程恢复和挂起之前,不会发生信号更新。

read_byte 的报告语句显示了正确的值。

(我删除了一些多余的括号对)。

Basicly, on the clock rising edge, the process enters a loop in which reads one line per iteration, disregarding it until it corresponds to one of the words of the instruction to retrieve; then, stores it into the proper portion of the instruction instr (from right to left in the vector). The fact that I open and close the file at each execution of the process implies that the read begins at the beginning of the file at each time, doesn't it?

是的,每个时钟都会打开文件,并且'scanned',PC代表的四行组装成instr只要END_FILE不return ] 是的。

The problem is that it doesn't work: when I start the simulation, the std_ulogic_vector results always undefined and I just can't understand why. Please, let me know whether it is an "algorithmic" error or it is just due to my poor understanding of I/O functions.

您的问题中没有足够的设计来排除算法错误。在您显示的代码中,有些事情可能会有所不同。我们还可以指出 'systemic' 可能由于未显示代码而导致错误的问题。

例如使用值检测时钟边沿,如果您将 clk 初始化为“1”,“EVENT 会出现误报”。 IEEE std_logic_1164 包包含两个函数,rising_edge 和 falling_edge,它们需要在零('L' 或 '0')和一('H'或“1”)来检测边缘,而不仅仅是事件和电平。

查看系统问题也指出了您的流程中的实际问题。每次过程中出现上升沿事件时,变量 line_counter 都不会被分配值 0连续条指令将从错误的地方获取。 line_counter 会告诉我们正在读取的正确 PC 值,但它会从 instruction_memory.txt 中获取前四个字节,因为 line_counter 未重置。这将通过将正确的 line_counter 值与错误的 read_byte 值一起显示出来。

修复:

architecture foo of readinstr is
    signal pc:              std_ulogic_vector (7 downto 0) := x"08";
    signal clk:             std_ulogic := '0';
    signal instr:           std_ulogic_vector (31 downto 0);

begin

read_instruction_memory: 
    process (clk)
        file     mem:            text; 
        variable mem_line:       line;
        variable read_byte:      std_ulogic_vector(7 downto 0); 
        variable line_counter:   integer := 0; -- INTIALIZSATION NOT NEEDED
        variable target_line:    integer;
    begin 
        if clk'event and clk = '1' then
            line_counter := 0;
            file_open(mem, "instruction_memory.txt", read_mode);
            target_line := to_integer(unsigned(pc)); 
            while line_counter < target_line + 4 loop
                if not endfile(mem) then 
                    readline(mem, mem_line);
                    if line_counter >= target_line  then 
                    report "line_counter = " & integer'image(line_counter);
                        read(mem_line, read_byte);
                        instr(7 + 8 * (line_counter - target_line) downto 8 * (line_counter - target_line)) <= read_byte;
                        report "read_byte = " & to_string(read_byte);
                    end if;
                end if;
                line_counter := line_counter + 1;
            end loop;
            file_close(mem);
        end if;
    end process read_instruction_memory;

POSITION_COUNTER:
    process (clk)
    begin
        if rising_edge(clk) then
            pc <= std_ulogic_vector(unsigned(pc) + 4);
        end if;
    end process;

MONITOR:
    process (instr)
    begin
        if now > 0 ns then
            report "instr = " & to_string(instr);
        end if;
    end process;

PC_MONITOR:
    process (pc)
    begin
        report "pc = " & to_string(pc);
    end process;

CLOCK:
    process
    begin
        wait for 10 ns;
        clk <= not clk;
        wait for 10 ns;
        clk <= not clk;
        wait for 10 ns;
        clk <= not clk;
        wait for 10 ns;
        wait;
    end process;
end architecture;

我们得到:

ghdl -a --std=08 readinstr.vhdl
ghdl -e --std=08 readinstr
ghdl -r readinstr
readinstr.vhdl:131:9:@0ms:(report note): pc = 00001000
readinstr.vhdl:100:21:@10ns:(report note): line_counter = 8
readinstr.vhdl:103:25:@10ns:(report note): read_byte = 00100000
readinstr.vhdl:100:21:@10ns:(report note): line_counter = 9
readinstr.vhdl:103:25:@10ns:(report note): read_byte = 00100001
readinstr.vhdl:100:21:@10ns:(report note): line_counter = 10
readinstr.vhdl:103:25:@10ns:(report note): read_byte = 00100010
readinstr.vhdl:100:21:@10ns:(report note): line_counter = 11
readinstr.vhdl:103:25:@10ns:(report note): read_byte = 00100011
readinstr.vhdl:124:13:@10ns:(report note): instr = 00100011001000100010000100100000
readinstr.vhdl:131:9:@10ns:(report note): pc = 00001100
readinstr.vhdl:100:21:@30ns:(report note): line_counter = 12
readinstr.vhdl:103:25:@30ns:(report note): read_byte = 00110000
readinstr.vhdl:100:21:@30ns:(report note): line_counter = 13
readinstr.vhdl:103:25:@30ns:(report note): read_byte = 00110001
readinstr.vhdl:100:21:@30ns:(report note): line_counter = 14
readinstr.vhdl:103:25:@30ns:(report note): read_byte = 00110010
readinstr.vhdl:100:21:@30ns:(report note): line_counter = 15
readinstr.vhdl:103:25:@30ns:(report note): read_byte = 00110011
readinstr.vhdl:124:13:@30ns:(report note): instr = 00110011001100100011000100110000
readinstr.vhdl:131:9:@30ns:(report note): pc = 00010000

这正确地显示了获取两个连续的指令。

另请注意,我为 read_byte.

删除了多余的 to_stdulogicvector 转换

这并没有让我们更接近您的系统问题,但确实允许推测。你有一个对多个时钟有效的重置吗? instr 也有可能有多个驱动程序。如果没有 MCVE,您的读者就会有点不知所措。

而这一切也引出了另一个想法。如何从文件instruction_memory.txt, 'seeking'中将内存数组初始化为对象,文件中的位置在VHDL中特别麻烦。

是的,read_instruction_memory 过程中确实存在错误,建模显示了这一错误。 line_counter 在每次执行 while 循环之前都没有重置。

有可能在创建 MCVE 以重现该问题的过程中揭示这一点。如果不是,那么对于那些使用一些仪器(报告声明)回答您的问题的人来说,这将变得显而易见。