如何通过超时停止模拟?

How to stop a simulation by timeout?

我已经设法在 VHDL 中实现了模拟超时。如果进程 运行 比 MaxRuntime 长,它们会得到 'killed'.

不幸的是,反之则行不通。如果我的模拟在 MaxRuntime 之前完成,一切都在等待 MaxRuntime 上的最后一个等待语句。

我发现可以将 wait onwait forwait until 语句合并为一个。

我当前的代码片段。完整的例子太长了...

package sim is
  shared variable IsFinalized : BOOLEAN := FALSE;

  procedure initialize(MaxRuntime : TIME := TIME'high);
  procedure finalize;
end package;

package body sim is
  procedure initialize(MaxRuntime : TIME := TIME'high) is
  begin
    -- do init stuff
    if (MaxRuntime = TIME'high) then
      wait on IsFinalized for MaxRuntime;
      finalize;
    end if;
  end procedure;

  procedure finalize;
  begin
    if (not IsFinalized) then
      IsFinalized := TRUE;
      -- do finalize stuff:
      --  -> stop all clocks
      -- write a report
    end if;
  end procedure;
end package body;

entity test is
end entity;

architecture tb of test is
begin
  initialize(200 us);

  process
  begin
    -- simulate work
    wait for 160 us;
    finalize;
  end process;
end architecture;

如果IsFinalized发生变化,等待语句不会退出。我的模拟运行了大约 160 us。如果我将 MaxRuntime 设置为 50 us,模拟将在大约 50 us 停止(加上一些额外的周期,直到每个进程注意到停止条件)。当我将 MaxRuntime 设置为 200 us 时,模拟会在 200 us 而不是 162 us 之后退出。

我不想使用模拟器的命令行开关来设置最大执行时间。

听起来你想终止模拟,要么在超时时终止,要么在超时前完成测试。当事件队列为空时,模拟器将停止是正确的,但是这确实达到了相当高的水平,正如您所体验的那样。

VHDL-2008 在 std.env 包中引入了 stop and finish 用于停止或终止模拟。

在 VHDL-2002 中,停止模拟的常见方法是通过 assertseverity FAILURE:

report "OK   ### Sim end: OK :-) ###   (not actual failure)" severity FAILURE;

这种停止模拟的方法是基于这样一个事实,即模拟器(例如ModelSim)通常会在assertseverity FAILURE发生时停止模拟。

  • 如何exit/abort等待语句?
  • 为什么我不能等待一个变量?

VHDL 中的所有顺序语句都是原子的。

等待语句恰好等待直到满足恢复条件,等待信号事件、信号表达式或模拟时间。

如果可以 exit/abort,您会用什么来 select 进程恢复的语句?它会恢复吗?您会使语句序列的执行无效。 VHDL 不做异常处理,它是事件驱动的。

这里值得记住的是,在模拟中执行的所有内容都是一个过程,函数调用是表达式,并发语句(包括过程调用)在详细说明过程中被转移到可能具有限制范围的超级强加块语句的过程中。

变量和信号之间存在以下基本区别(IEEE Std 1076-2008 附录 I 词汇表):

信号:具有历史值的对象。一个信号可能有多个驱动因素,每个驱动因素都有一个当前值和预计的未来值。术语信号是指由信号声明或端口声明声明的对象。 (6.4.2.3)

变量:具有单个当前值的对象。 (6.4.2.4)

你不能等待没有未来价值的东西,或者更简单一点变量不发出信号(来自字典 - 为特定事件的发生提供冲动或时机的事件或陈述)。

模拟由取决于未来值的信号事件驱动。它定义了模拟时间的进程。时间是离散事件模拟的基础

现在您可能想知道,如果有人声称 VHDL 是一种通用编程语言,他们是否会告诉您真相。如果您有能力任意中断和恢复进程,那么在保持通过离散时间事件正式指定硬件操作的能力的同时又如何呢?

所有这些都告诉您,您可以考虑使用信号而不是共享变量。

Morton 的停止和完成程序可在 std.env 中找到。

由于 user1155120 给出的原因,您不能等待变量。因此,您需要使用信号。 (包中的信号是全局信号)。

不幸的是,即使全局信号在范围内,它仍然需要作为过程的输出参数,这很丑陋。不仅如此,在您的代码中,您将从多个地方驱动信号,此全局信号需要是已解析的类型,例如 std_logic。这也有点难看。

这是您的代码版本,其中共享变量被信号替换,布尔类型被 std_logic 替换,全局信号被添加为输出参数:

library IEEE;
use IEEE.std_logic_1164.all;

package sim is
  signal IsFinalized : std_logic := '0';

  procedure initialize(signal f : out std_logic; MaxRuntime : TIME := TIME'high);
  procedure finalize (signal f : out std_logic);
end package;

package body sim is
  procedure initialize(signal f : out std_logic; MaxRuntime : TIME := TIME'high) is
  begin
    -- do init stuff
    if (MaxRuntime = TIME'high) then
      wait on IsFinalized for MaxRuntime;
      finalize(f);
    end if;
  end procedure;

  procedure finalize (signal f : out std_logic) is
  begin
    if (IsFinalized = '0') then
      f <= '1';
      -- do finalize stuff:
      --  -> stop all clocks
      -- write a report
      report "Finished!";
    end if;
  end procedure;
end package body;

use work.sim.all;

entity test is
end entity;

architecture tb of test is
begin
  initialize(IsFinalized, 200 us);

  process
  begin
    -- simulate work
    wait for 160 us;
    finalize(IsFinalized);
    wait;
  end process;
end architecture;

http://www.edaplayground.com/x/VBK

从测试自动化的角度来看,超时很重要,它可以使用 stop/finish/assert 失败强制停止测试。即使您的意图是通过不产生更多事件来终止一切,也存在导致一切挂起的错误的风险。如果在较长的每晚测试中的第一次测试中发生这种情况 运行 你就在浪费很多时间。

如果你使用 VUnit 它会像这样工作

library vunit_lib;
context vunit_lib.vunit_context;

entity tb_test is
  generic (runner_cfg : runner_cfg_t);
end tb_test;

architecture tb of tb_test is
begin
    test_runner : process
    begin
      test_runner_setup(runner, runner_cfg);
      while test_suite loop
        if run("Test that hangs") then
          -- Simulate hanging test case
          wait;
        elsif run("Test that goes well") then
          -- Simulate test case running for 160 us
          wait for 160 us;
        end if;
      end loop;

      test_runner_cleanup(runner); -- Normal forced exit point
    end process test_runner;

    test_runner_watchdog(runner, 200 us);
end;

挂起的第一个测试用例在 200 us 后被看门狗终止,这样第二个可以 运行。

在使用 test_runner_cleanup 过程强制停止测试台时可能会遇到的一个问题是,如果您有一个或多个额外的测试进程,当 test_runner 进程到达时有更多的事情要做test_runner_cleanup 过程调用。例如,如果您有这样的流程

another_test_process: process is
begin
  for i in 1 to 4 loop
    wait for 45 us;
    report "Testing something";
  end loop;
  wait;
end process;

不允许 运行 所有四次迭代。最后一次迭代应该在 180 us 时 运行 但在 160 us 时调用 test_runner_cleanup

所以需要同步test_runneranother_test_process进程。您可以创建一个带有全局解析信号的包来解决这个问题(如前所述),但 VUnit 已经提供了这样一个信号,您可以使用它来节省一些时间。 runner 是一条记录,其中包含 VUnit 测试平台的当前 阶段 。当调用 test_runner_cleanup 时,它进入同名阶段,然后移动到 test_runner_exit 阶段,在该阶段模拟被迫停止。任何进程都可以通过放置临时锁来阻止 VUnit 进入或退出其阶段。更新后的 another_test_process 将阻止 VUnit 退出 test_runner_cleanup 阶段,直到它完成所有迭代。

another_test_process: process is
begin
  lock_exit(runner, test_runner_cleanup);
  for i in 1 to 4 loop
    wait for 45 us;
    report "Testing something";
  end loop;
  unlock_exit(runner, test_runner_cleanup);
  wait;
end process;

任何数量的进程都可以在一个阶段上放置一个锁,并且在所有个锁被解锁之前阻止阶段转换。您还可以使用 get_phase 函数获取当前阶段,这在某些情况下可能很有用。

如果您正在开发可重用组件,您可能不应该使用这种技术。它为您节省了一些代码,但它也使您的组件依赖于 VUnit 而不是每个人都使用 VUnit。致力于此:-)

免责声明:我是 VUnit 的作者之一。