I'm trying to implement a VHDL-08 version of our PoC.Simulation
helper package.
The original package uses shared variables to track the simulation status:
- pass: all asserts passed
- stop: stop all processes
There are several functions and procedures using that internal state. For example a tbGenerateClock()
procedure is used to create a Clock signal.
In my VHDL-08 version, I'm using protected types for the shared variable. I implemented several 'Methods' (what is the correct terminus for that?), which are using or modifying the internal state variable.
GHDL compiles all my sources and throws an error at runtime:
C:\Tools\GHDL\0.33dev\bin\ghdl.exe:internal error: protected_enter(2)
C:\Tools\GHDL\0.33dev\bin\ghdl.exe:error: simulation failed
Is this a GHDL internal error or am I using protected types in a wrong way?
I created a (hopefully) minimal example (download from Gist) that has only 2 procedures: generateClock
, stopSimulation
.
There are also tb*
wrapper procedures to ensure a compatible interface to my VHDL-93 implementation.
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;
Solution:
- move clock generation code into the
tbGenerateClock
procedure - add a function to the protected type to get the internal stop-state
- mark this function as
impure
Here is the modified simulation package:
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;