0

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

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

  • pass : 所有断言都通过
  • stop : 停止所有进程

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

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

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

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

我创建了一个(希望如此)最小的示例(从 Gist 下载),它只有 2 个过程:generateClock, stopSimulation.

还有一些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;
4

1 回答 1

2

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

有一个 0.32 的候选发布版本,您将自己的生命掌握在自己的手中,以获得 0.33dev,这将代表源代码树中超出 0.32rc1 的一些修订。

有关 ghdl 内部结构的问题更适合发送到 ghdl-discuss 邮件列表,并且那些能够解决这些问题并参与 Stackoverflow 的人不使用 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) 子程序调用的执行涉及对相应子程序声明的参数接口列表的细化;这涉及详细说明每个接口声明以创建相应的形式参数。然后将实际参数与形式参数相关联。接下来,如果子程序是受保护类型的方法(见 3.5.1)或隐式声明的文件操作(见 3.4.1),则精化块(在保留所有状态的同时暂停执行),如有必要,直到独占访问方法前缀表示的对象或文件操作的文件参数表示的文件对象是安全的。...

方法tbGenerateClock调用status.generateClock永远不会返回的过程,除非Stopis TRUEstatus被锁定并Stop``TRUE取决于被阻止的刺激过程。

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

您基本上status.generateClock就像在一个地方的并发过程调用一样操作,只有顺序过程调用应该适用。它在等待输入值或时间流逝时循环。过程调用的tbGenerateClock包装器status.generateClock掩盖了这个问题(我在标准中没有任何禁令)。

似乎 ghdl 投诉是有效的,尽管缺乏可识别的诊断信息是值得注意的。

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

2——如果两个或多个进程访问同一组共享变量,可能会发生活锁或死锁。也就是说,可能永远不可能授予对共享变量的独占访问权限,如上文 b) 项中所述。允许但不要求实现检测并在可能的情况下解决此类情况。

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

就我个人而言,我认为 ghdl 很聪明地发现这里存在僵局。

除了阅读上面提到的 ghdl 源代码或从标准中深入了解之外,我知道没有任何文档可以帮助您。(按照“对于有经验的程序员来说,任何错误都是显而易见的”)。

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

在这种情况下,您可能期望的行为将是一个长时间的模拟,并最终看到一条消息,即模拟在达到 TIME'HIGH 时停止,而tbStopSimulation刺激过程中的过程调用从未完成。

与 ghdl 相比,您获得的信息基本上更少(同样,'对于有经验的程序员来说,任何错误都是显而易见的')。

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

使用 paebbels 解决方案

将访问移到通过使用受保护方法访问s 值Stop生成的过程之外允许多个进程访问受保护类型提供的共享数据,对共享变量的独占访问仅足够长的时间来完成这两种方法(和不纯函数(方法))。ClockStopstatustbStopSimulationgetState

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

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

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

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

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

带有解决方案的 tb 测试台 png (可点击)

正是您在未标记过程中通过评论找到的内容

-- stimuli process
于 2015-04-23T00:59:10.990 回答