如何解决 GHDL 中的 'protected_enter(2)' 错误
How to solve a 'protected_enter(2)' error in GHDL
我正在尝试实现我们的 PoC.Simulation
帮助程序包的 VHDL-08 版本。
原包使用共享变量跟踪模拟状态:
- 通过: 所有断言都通过了
- stop: 停止所有进程
有几个函数和过程使用该内部状态。例如,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 个过程: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;
解法:
- 将时钟生成代码移动到
tbGenerateClock
过程中
- 向受保护类型添加函数以获取内部停止状态
- 将此函数标记为
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 除非 Stop
是 TRUE
,status
被锁定并且 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
我正在尝试实现我们的 PoC.Simulation
帮助程序包的 VHDL-08 版本。
原包使用共享变量跟踪模拟状态:
- 通过: 所有断言都通过了
- stop: 停止所有进程
有几个函数和过程使用该内部状态。例如,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 个过程: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;
解法:
- 将时钟生成代码移动到
tbGenerateClock
过程中 - 向受保护类型添加函数以获取内部停止状态
- 将此函数标记为
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 除非 Stop
是 TRUE
,status
被锁定并且 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