如何解决 GHDL 中的 'protected_enter(2)' 错误

How to solve a 'protected_enter(2)' error in GHDL

我正在尝试实现我们的 PoC.Simulation 帮助程序包的 VHDL-08 版本。

原包使用共享变量跟踪模拟状态:

有几个函数和过程使用该内部状态。例如,tbGenerateClock() 过程用于创建时钟信号。

在我的 VHDL-08 版本中,我为共享变量使用受保护的类型。我实现了几个 'Methods'(正确的终点是什么?),它们正在使用或修改内部状态变量。

GHDL 编译我的所有源代码并在运行时抛出错误: C:\Tools\GHDL[=15=].33dev\bin\ghdl.exe:internal error: protected_enter(2) C:\Tools\GHDL[=16=].33dev\bin\ghdl.exe:error: simulation failed

这是 GHDL 内部错误还是我以错误的方式使用了受保护的类型?

我创建了一个(希望是)最小示例 (download from Gist),它只有 2 个过程:generateClockstopSimulation.

还有 tb* 包装程序来确保与我的 VHDL-93 实现兼容的接口。

library IEEE;
use     IEEE.STD_LOGIC_1164.all;

package simulation is
  type tSimStatus is protected
    procedure stopSimulation;
    procedure generateClock(signal Clock : out STD_LOGIC; constant ClockPeriod : TIME; Enable : BOOLEAN := TRUE);
  end protected;
  -- stop all simulation processes
  procedure tbStopSimulation;
  -- Generate clock waveform for simulation
  procedure tbGenerateClock(signal Clock : out STD_LOGIC; constant ClockPeriod : TIME; Enable : BOOLEAN := TRUE);
end;

package body simulation is
  type tSimStatus is protected body
    variable stop : BOOLEAN := FALSE;

    procedure stopSimulation is
    begin
      stop := TRUE;
    end procedure;

    procedure generateClock(signal Clock : out STD_LOGIC; constant ClockPeriod : TIME; Enable : BOOLEAN := TRUE) is
      constant HIGH_TIME  : TIME  := ClockPeriod / 2;
      constant LOW_TIME    : TIME  := ClockPeriod / 2;
    begin
      Clock <= '1';
      while not stop loop
        while Enable and not stop loop
          Clock <= '1';
          wait for HIGH_TIME;
          Clock <= '0';
          wait for LOW_TIME;
        end loop;
        wait until (Enable = TRUE) or (stop = TRUE);
      end loop;
    end procedure;
  end protected body;

  shared variable status : tSimStatus;

  procedure tbStopSimulation is
  begin
    status.stopSimulation;
  end procedure;

  procedure tbGenerateClock(signal Clock : out STD_LOGIC; constant ClockPeriod : TIME; Enable : BOOLEAN := TRUE) is
  begin
    status.generateClock(Clock, ClockPeriod, Enable);
  end procedure;  
end package body;

library IEEE;
use      IEEE.STD_LOGIC_1164.all;
use      work.simulation.all;

entity tb is
end entity;

architecture test of tb is
  constant CLOCK_PERIOD : TIME := 10 ns;

  signal Clock    : STD_LOGIC;
  signal Reset    : STD_LOGIC    := '0';
begin
  -- clock process
  process
  begin
    tbGenerateClock(Clock, CLOCK_PERIOD, TRUE);
  end process;

  -- stimuli process
  process
  begin
    wait until rising_edge(Clock);

    Reset    <= '1';
    wait until rising_edge(Clock);

    Reset    <= '0';
    wait until rising_edge(Clock);

    tbStopSimulation;
    wait;
  end process;
end;

解法:

  1. 将时钟生成代码移动到 tbGenerateClock 过程中
  2. 向受保护类型添加函数以获取内部停止状态
  3. 将此函数标记为impure

这里是修改后的模拟包:

package simulation is
  type tSimStatus is protected
    procedure stopSimulation;
    impure function getState return BOOLEAN;
  end protected;
  -- stop all simulation processes
  procedure tbStopSimulation;
  -- Generate clock waveform for simulation
  procedure tbGenerateClock(signal Clock : out STD_LOGIC; constant ClockPeriod : TIME; Enable : BOOLEAN := TRUE);
end;

package body simulation is
  type tSimStatus is protected body
    variable stop : BOOLEAN := FALSE;

    procedure stopSimulation is
    begin
      stop := TRUE;
    end procedure;

    impure function getState return BOOLEAN is
    begin
      return stop;
    end function;
  end protected body;

  shared variable status : tSimStatus;

  procedure tbStopSimulation is
  begin
    status.stopSimulation;
  end procedure;

  procedure tbGenerateClock(signal Clock : out STD_LOGIC; constant ClockPeriod : TIME; Enable : BOOLEAN := TRUE) is
    constant HIGH_TIME  : TIME  := ClockPeriod / 2;
    constant LOW_TIME   : TIME  := ClockPeriod / 2;
  begin
    Clock <= '1';
    while not status.getState loop
      while Enable and not status.getState loop
        Clock <= '1';
        wait for HIGH_TIME;
        Clock <= '0';
        wait for LOW_TIME;
      end loop;
      wait until (Enable = TRUE) or (status.getState = TRUE);
    end loop;
  end procedure;  
end package body;

受保护的类型在 ghdl-0.31 中支持命令行标志 --std=02(对于 -2002 标准修订版)并给出相同的错误。

有一个 0.32 的候选发布版本,您正在为 0.33dev 夺走自己的生命,这将代表源代码树中超过 0.32rc1 的一些修订。

关于 ghdl 内部结构的问题更适合发送到 ghdl-discuss 邮件列表,那些能够解决这些问题并参与 Whosebug 的人不要这样做 Windows。

我使用了 C shell 别名

find . -iname \*.ad\[bs\] -exec grep -H !* \{\} \;

在ghdl源代码树中找到protected_enter

在 ghdl 的源代码 grt/grt-processes.adb 中,过程 Ghdl_Protected_Enter1 我们看到注释 -- Protected object already locked.-- Should be locked by the current process.protected_enter(2) 内部错误。

您可能有两个单独的进程寻求访问 status

这些是时钟过程和刺激过程(您可以使用标签)。

IEEE Std 1076-2002, 12.5 动态阐述:

b) Execution of a subprogram call involves the elaboration of the parameter interface list of the corresponding subprogram declaration; this involves the elaboration of each interface declaration to create the corresponding formal parameters. Actual parameters are then associated with formal parameters. Next, if the subprogram is a method of a protected type (see 3.5.1) or an implicitly declared file operation (see 3.4.1), the elaboration blocks (suspends execution while retaining all state), if necessary, until exclusive access to the object denoted by the prefix of the method or to the file object denoted by the file parameter of the file operation is secured. ...

方法 tbGenerateClock 调用过程 status.generateClock 永远不会 returns 除非 StopTRUEstatus 被锁定并且 Stop``TRUE 取决于阻塞的刺激过程。

解决这个问题的方法似乎是向方法 tbGenerateClock 和过程 generateClock 消除方法添加一个参数 tbStopSimulation 和程序 stopSimulation。

您实际上是在操作 status.generateClock,就好像它是一个并发过程调用,而在一个只有顺序过程调用才适用的地方。它在等待输入值或时间流逝时循环。过程调用 status.generateClock 周围的 tbGenerateClock 包装器掩盖了这个问题(我在标准中找不到任何禁令)。

尽管值得注意的是缺少可辨别的诊断消息,但 ghdl 投诉似乎是有效的。

此外,如果您查看 12.5 中的注释:

2—If two or more processes access the same set of shared variables, livelock or deadlock may occur. That is, it may not be possible to ever grant exclusive access to the shared variable as outlined in item b), above. Implementations are allowed to, but not required to, detect and, if possible, resolve such conditions.

解决办法似乎是产生一个深不可测的错误消息,而不是简单地让模拟时间达到 TIME'HIGH,在电路板加载时钟转换后结束模拟时间。

就我个人而言,我认为 ghdl 非常聪明地发现这里存在死锁。

我知道除了阅读上面提到的 ghdl 源代码或从标准中深入理解之外,没有任何文档可以帮助您。 (根据 'for the experienced programmer any errors are immediately obvious')。

不能保证另一个 VHDL 实现会以任何形式指示发生了死锁,这是可选的。

在那种情况下,您可能期望的行为将是长时间的模拟,最终会看到一条消息,表明模拟因达到 TIME'HIGH 而停止,而刺激过程中没有 tbStopSimulation 过程调用已完成。

与使用 ghdl(同样,'for the experienced programmer any errors are immediately obvious')相比,您获得的信息实际上更少。

现在您知道 protected_enter(和 protected_leave)消息与阻止对受保护的访问相关联ghdl 中多个进程的方法。 (而且我也必须通过艰难的方式找到它。)

用paebbels溶液

通过使用受保护的方法访问 Stop 的值,将对 Stop 的访问移到生成 Clock 的过程之外,允许多个进程访问受保护类型提供的共享数据,授予对共享变量 status 的独占访问权限,只需要足够长的时间来完成这两个方法(tbStopSimulation 和不纯函数(方法)getState)。

它允许多个进程读取或更新 Stop 给予每个独占访问足够长的时间来完成相应的方法。

我通常会使用信号在进程之间共享信息,但在这种情况下,如果您在精心设计的设计中有多个进程能够停止模拟,您仍然需要等效的受保护方法来更新 Stop通过停止时钟。

还有一个潜在的基于并发的可移植性问题(一个模拟周期Stop既读取又更新)。

您可能会精确到一个时钟,因为无法保证进程调用方法的执行或恢复顺序。如果 paebbels 使用 pass/fail 机制来评估测试结果,那将不会有问题,这是一个常见的解决方案。

那么测试平台 tb 对更改做了什么:

(可点击)

正是您在未标记的过程中发现的带有注释

的内容
-- stimuli process