【问题标题】:How to solve a 'protected_enter(2)' error in GHDL如何解决 GHDL 中的“protected_enter(2)”错误
【发布时间】:2015-04-22 19:14:22
【问题描述】:

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

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

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

有几个函数和过程使用该内部状态。例如,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 内部错误还是我以错误的方式使用受保护类型?

我创建了一个(希望是)最小示例 (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.33。这只是默认的 ghdl :)

标签: vhdl simulation ghdl


【解决方案1】:

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 我们看到 cmets -- 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 永远不会返回,除非StopTRUEstatus 被锁定并且Stop``TRUE 取决于被阻止的刺激过程。

解决方法似乎是在方法 tbGenerateClock 和过程 generateClock 消除方法中添加一个参数 tbStopSimulation 和过程 stopSimulation。

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

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

如果您进一步查看 12.5 中的 NOTES:

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

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

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

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

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

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

实际上,您获得的信息比使用 ghdl 获得的信息还要少(同样,“对于有经验的程序员来说,任何错误都是显而易见的”)。

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

使用 paebbels 解决方案

Stop的访问移到生成Clock的过程之外,通过使用受保护的方法访问Stops值允许多个进程访问受保护类型提供的共享数据,从而独占访问共享变量@ 987654341@只够完成这两个方法(tbStopSimulation和不纯函数(方法)getState)。

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

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

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

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

那么 testbench tb 对更改做了什么:

(可点击)

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

-- stimuli process

【讨论】:

  • VHDL-93 的原始代码只使用 tbGenerateClock(...) 和一个共享变量 stopSimulation 来完成这项工作(在 iSim 和 ghdl 中测试,vSim 在明天的列表中)。我对 VHDL-08 尝试了同样的方法,但我无法从 tbGenerateClock 访问“status.stop”。我还尝试在 tSimStatus 中实现一个 getter 函数,它返回停止,但我得到一个访问/范围错误。旧代码不兼容,因为 VHDL-08 需要保护共享变量。我会在 ghdl 邮件列表上重新发布这个问题。我不确定我是否以错误的方式使用受保护的类型,或者是否是 ghdl 错误。
  • 你知道你也可以在包中声明信号吗? package_declarative_item,IEEE Std 1076-2008 4.7 包声明。您以错误的方式使用受保护类型 - 5.6.1 受保护类型定义......“受保护类型实现顺序语句的可实例化区域,每个语句都保证对共享数据的独占访问。......”,只要由于tbGenerateClock 方法没有完成tbStopSimulation 调用将保持阻塞,这不仅仅是一个ghdl 错误。所有这些方法都作为顺序过程调用被调用,它们应该完成。
  • 如果我将getSimulationStop 方法添加到受保护类型,它只返回内部停止变量;我收到以下错误:reference to variable "stop" violate pure rule for function "getstopsimulation"。所以你的建议是使用全局信号而不是共享变量?
  • oOo ...我解决了:方法/函数必须声明为impure,因为它使用受保护类型的内部状态。
  • 也许您可以将“修复”附加到您的问题中。了解您是如何克服问题的可能很有价值。
猜你喜欢
  • 1970-01-01
  • 2019-09-30
  • 2019-01-07
  • 2020-03-31
  • 2017-10-06
  • 2020-11-01
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多