使用 TEXTIO 在测试台中读取的刺激文件
从文件中读取信号值是为被测设备 (DUT) 生成刺激的另一种方法。测试台序列和时序硬编码在 VHDL 测试台逐行读取的激励文件中。这使您可以轻松更改要馈送到测试对象的波形模式。
有时,您有一个非常具体的测试模式或事件序列,您希望您的 DUT 通过。您可以通过在 ASCII 文件中指定每个信号应具有的信号值以及它们应更改的相对仿真时间来实现此目的。 VHDL 测试平台在这种策略中的作用是从激励文件中读取数据,并在正确的时间将这些值应用于 DUT 输入。
本文是 VHDL 文件访问系列文章的第二篇。我们在上一篇博文中了解了如何从文件中读取十六进制、八进制和二进制值,如果您想了解有关使用 TEXTIO
从文件读取的更多信息,请返回阅读它 VHDL 中的库。
这篇博文是关于在 VHDL 中使用 TEXTIO 库的系列文章的一部分。在此处阅读其他文章:
如何使用 TEXTIO 从文件初始化 RAM
使用 TEXTIO 读取 BMP 文件位图图像
测试用例
示例 DUT 将是一个 4 输入多路复用器 (MUX),取自我之前的一篇博客文章。它是一个标准的异步 4 对 1 MUX,数据宽度为一个字节。它的工作原理对于本文并不重要,因为我们不会对输出进行任何检查,这只是为了演示目的。
MUX的实体如下图。
entity mux_4 is port( -- Data in din_0 : in unsigned(7 downto 0); din_1 : in unsigned(7 downto 0); din_2 : in unsigned(7 downto 0); din_3 : in unsigned(7 downto 0); -- Selector sel : in unsigned(1 downto 0); -- Data out dout : out unsigned(7 downto 0)); end entity;
在 VHDL 文件顶部导入必要的包后,我们继续声明我们将要连接到 DUT 的输入信号。从下面的清单可以看出,它们是 MUX 实体声明的蓝图。
signal din_0 : unsigned(7 downto 0); signal din_1 : unsigned(7 downto 0); signal din_2 : unsigned(7 downto 0); signal din_3 : unsigned(7 downto 0); signal sel : unsigned(1 downto 0); signal dout : unsigned(7 downto 0);
我们使用实体实例化方法在测试平台的架构区域顶部创建带有标签“DUT”的 MUX 实例。实体信号连接到本地同名的testbench信号,如下代码所示。
DUT: entity work.mux_4(rtl) port map ( din_0 => din_0, din_1 => din_1, din_2 => din_2, din_3 => din_3, sel => sel, dout => dout );
刺激文件
刺激文件可以有很多不同的格式,这里介绍的只是我在写这篇文章时想到的一个例子。不过,当您了解我是如何创建它时,您应该能够对其进行修改以满足您的需求。
下面的清单显示了本示例中使用的完整激励文件。
# Column description: # wait_time | sel | din_0 | din_1 | din_2 | din3 # Optional console printout 0 ns 0 AA BB CC DD # Setting initial values 10 ns 1 AA BB CC DD # Testing by changing the selector signal 10 ns 2 AA BB CC DD 10 ns 3 AA BB CC DD 10 ns 3 A1 B1 C1 D1 # Testing by changing all data inputs 10 ns 3 A2 B2 C2 D2 10 ns 3 A3 B3 C3 D3 10 ns 3 00 00 00 D2 # Changing all unselected inputs 10 ns 3 01 02 03 D2 10 ns 3 11 22 33 D2 1 ns 0 CC DD EE FF # Changing all inputs fast 1 ns 1 DD EE FF CC 1 ns 2 EE FF CC DD 1 ns 3 FF CC DD EE 10 ns 0 00 00 00 00 # Simulation stop
让我们暂时忽略评论,那些是用绿色标记的,总是以“#”字符开头。每条线代表模拟中的一个时间步长。每行有六列命令,实际上是七列文本,但前两列属于同一个数据项。
文本列一和二描述了一个时间值,即模拟器在应用其他列中列出的值之前应该在该行暂停多长时间。因此,执行命令时的绝对仿真时间是相对于上一行描述的事件而言的。我们只使用 0、1 或 10 纳秒,但它可以是任何值,1000 纳秒或 1000 小时(1000 hr
) 就此而言。
其余五个文本列描述了应用于 DUT 输入的信号值。它们以十六进制文字形式给出,信号顺序为 sel
, din_0
, din_1
, din_2
,最后是 din_3
.
现在,转到评论。有两种类型的评论;单行注释和尾随注释。我们的测试平台将区别对待它们。单行注释,如文件顶部的注释,将被忽略。另一方面,尾随注释应打印到模拟器控制台。我们可以使用它们为我们提供有关模拟运行时发生的情况的线索。
在 VHDL 中读取激励文件
VHDL 不是最适合文本处理的语言,但它可以胜任。对动态字符串的支持是有限的,并且它缺乏方便的例程,例如用于剥离或跳过空格。为了让我们自己更容易,我们将假设刺激文件写得很好。让我们非常小心地确保文本元素之间总是有一个空格,而“#”字符和注释文本之间总是有一个空格。此外,刺激文件中的任何位置都没有额外的前导或尾随空格。
PROC_SEQUENCER : process file text_file : text open read_mode is "stimulus.txt"; variable text_line : line; variable ok : boolean; variable char : character; variable wait_time : time; variable selector : sel'subtype; variable data : dout'subtype; begin
PROC_SEQUENCER
的声明区域 程序如上所示。首先,我们声明特殊的 file
对象,VHDL 文件处理程序类型。然后,我们声明一个 line
类型的变量 .这只是一个对字符串的访问类型,一个指向动态分配的字符串对象的指针。 ok
Boolean 类型的变量用于检查读取操作是否成功。最后,我们声明四个变量char
, wait_time
, selector
, 和 data
.这些变量用于从每一行文本中提取每一列的数据。
while not endfile(text_file) loop readline(text_file, text_line); -- Skip empty lines and single-line comments if text_line.all'length = 0 or text_line.all(1) = '#' then next; end if;
在流程主体中,我们直接进入一个 while 循环,该循环将遍历激励文件中的每一行文本。 readline
过程将新的文本行分配给 text_line
此循环的每次迭代中的变量。读取该行后,我们检查该行是否为空或第一个字符是否为“#”,在这种情况下,我们立即使用 next
转到下一行 关键字跳过循环的迭代。请注意,我们使用的是 text_line.all
访问 line
中的字符串 对象。
read(text_line, wait_time, ok); assert ok report "Read 'wait_time' failed for line: " & text_line.all severity failure; hread(text_line, selector, ok); assert ok report "Read 'sel' failed for line: " & text_line.all severity failure; sel <= selector; hread(text_line, data, ok); assert ok report "Read 'din_0' failed for line: " & text_line.all severity failure; din_0 <= data; hread(text_line, data, ok); assert ok report "Read 'din_1' failed for line: " & text_line.all severity failure; din_1 <= data; hread(text_line, data, ok); assert ok report "Read 'din_2' failed for line: " & text_line.all severity failure; din_2 <= data; hread(text_line, data, ok); assert ok report "Read 'din_3' failed for line: " & text_line.all severity failure; din_3 <= data;
接下来是来自 text_line
的一些读取 目的。 read
和 hread
过程调用跳过前导空格,这样我们就不必执行任何虚拟读取来移动 text_line
内的内部读取起始位置 目的。我们可以省略断言语句,但我希望在读取失败时停止模拟。至少在 ModelSim 中,发生这种情况时模拟不会自动停止。我们将每个成功读取的变量分配给相关的 DUT 信号,除了 wait_time
没有对应 DUT 输入的变量。
wait for wait_time;
分配信号值后,我们等待指定的时间。命中等待语句会导致预定的信号值在相同的增量周期内生效。
-- Print trailing comment to console, if any read(text_line, char, ok); -- Skip expected newline read(text_line, char, ok); if char = '#' then read(text_line, char, ok); -- Skip expected newline report text_line.all; end if; end loop; finish; end process;
最后,当程序从等待语句中唤醒时,我们在 text_line
上寻找附加的尾随注释 目的。在我们使用虚拟读取去除“#”字符和以下空格后,使用报告语句将任何注释打印到控制台。
在处理完刺激文件的最后一行文本后,while 循环终止。有一个 VHDL-2008 finish
负责停止测试平台的进程结束时的关键字。
输出
示例测试台在 ModelSim 中运行时将下面显示的文本打印到模拟器控制台。我们可以看到评论是来自刺激文件的评论。打印的时间值是基于刺激文件中指定的纳秒延迟的累积模拟时间。
# ** Note: Setting initial values # Time: 0 ns Iteration: 1 Instance: /file_stim_tb # ** Note: Testing by changing the selector signal # Time: 10 ns Iteration: 0 Instance: /file_stim_tb # ** Note: Testing by changing all data inputs # Time: 40 ns Iteration: 0 Instance: /file_stim_tb # ** Note: Changing all unselected inputs # Time: 70 ns Iteration: 0 Instance: /file_stim_tb # ** Note: Changing all inputs fast # Time: 91 ns Iteration: 0 Instance: /file_stim_tbf # ** Note: Simulation stop # Time: 104 ns Iteration: 0 Instance: /file_stim_tb # Break in Process PROC_SEQUENCER at file_stim_tb.vhd line 98
仿真波形如下所示。它直观地展示了我们的激励文件中的值如何在指定的仿真时间应用于信号。
最后的想法
如果您有需要应用的非常具体的测试模式,则从文件中读取测试台刺激可能会很有优势。整个测试台不必由文本文件控制,这个例子只是为了展示VHDL文件访问的可能性。
但是,我们尚未讨论的一件事是检查 DUT 输出。我们的示例测试平台根本不检查输出。您可以像在完整的 VHDL 测试平台中一样验证 DUT 行为,例如通过使用行为模型进行比较。或者您可以修改代码和激励文件以包含预期的输出值。无论您选择哪种策略,请确保您创建了一个自检测试平台,而不是依赖手动检查波形。
VHDL