Как решить ошибку "protected_enter(2)" в GHDL
Я пытаюсь реализовать версию нашего VHDL-08 PoC.Simulation
вспомогательный пакет.
Исходный пакет использует общие переменные для отслеживания статуса симуляции:
- пройти: все утверждения пройдены
- остановить: остановить все процессы
Есть несколько функций и процедур, использующих это внутреннее состояние. Например, 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;
Решение:
- переместить код генерации часов в
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;
1 ответ
Защищенные типы поддерживаются в ghdl-0.31 для флага командной строки --std=02 (для стандартной редакции -2002) и выдают ту же ошибку.
Есть релиз-кандидат на 0,32, вы берете свою жизнь в свои руки за 0,33, что представляло бы некоторую ревизию в дереве исходников за пределами 0,32rc1.
Вопросы по внутренним компонентам ghdl более правильно адресованы списку рассылки ghdl-Discussion, а те, кто способен адресовать их и участвовать в Stackru, не работают в Windows.
Я использовал псевдоним оболочки C
find . -iname \*.ad\[bs\] -exec grep -H !* \{\} \;
найти protected_enter в исходном дереве ghdl.
В исходном коде ghdl grt / grt -cesses.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), блоки разработки (приостанавливают выполнение при сохранении всех состояний), если необходимо, до эксклюзивного доступа к объект, обозначенный префиксом метода, или файловый объект, обозначенный параметром file файловой операции , защищен....
метод tbGenerateClock
процедура звонков status.generateClock
который никогда не возвращается, если Stop
является TRUE
, status
заблокирован и Stop``TRUE
зависит от процесса стимулов, который заблокирован.
Выходом из этого является добавление аргумента в метод tbGenerateClock и процедуру generateClock, исключая метод tbStopSimulation и процедуру stopSimulation.
Вы по существу работаете status.generateClock
как если бы это был параллельный вызов процедуры в месте, должен применяться только последовательный вызов процедуры. Он зацикливается при ожидании либо входных значений, либо времени. tbGenerateClock
обертка вокруг вызова процедуры status.generateClock
маскирует проблему (и в стандарте нет запрета, который я могу найти).
Казалось бы, жалоба ghdl действительна, хотя примечательным является отсутствие заметных диагностических сообщений.
Далее, если вы посмотрите на примечания в 12.5:
2 - если два или более процессов обращаются к одному и тому же набору общих переменных, может возникнуть живая или тупиковая блокировка. То есть, возможно, никогда не удастся предоставить эксклюзивный доступ к совместно используемой переменной, как описано в пункте b) выше. Реализациям разрешается, но не требуется, обнаруживать и, если возможно, разрешать такие условия.
Похоже, что разрешение состояло в том, чтобы создать непостижимое сообщение об ошибке, вместо того, чтобы просто позволить времени моделирования достичь TIME'HIGH, заканчивая время моделирования после загрузки платы переходов часов.
Лично я представляю, что ГХДЛ довольно умно понимает, что здесь тупик.
Я не знаю никакой документации, которая могла бы вам здесь помочь, кроме прочтения упомянутого выше источника ghdl или разработки полного понимания из стандарта. (По аналогии с "для опытного программиста любые ошибки очевидны сразу").
Нет никакой гарантии, что другая реализация VHDL в любой форме будет указывать на возникновение взаимоблокировки, это необязательно.
Поведение, которое вы могли бы ожидать в этом случае, было бы длительным моделированием и, в конечном итоге, появлением сообщения о том, что моделирование было остановлено при достижении TIME'HIGH без tbStopSimulation
вызов процедуры в процессе стимулов, когда-либо завершенных.
По сути, у вас было бы даже меньше информации, чем у ghdl (опять же, "для опытного программиста любые ошибки сразу очевидны").
И теперь вы знаете, что сообщения protected_enter (и protected_leave) связаны с блокировкой доступа к защищенным методам более чем одним процессом в ghdl. (И мне тоже пришлось это выяснить трудным путем.)
С решением Паеббельса
Перемещение доступа к Stop
вне процедуры генерации Clock
используя защищенный метод для доступа Stop
Значение s позволяет нескольким процессам получать доступ к общим данным, предоставляемым защищенным типом, предоставляя эксклюзивный доступ к общей переменной status
только достаточно долго, чтобы завершить два метода (tbStopSimulation
и нечистая функция (метод) getState
).
Это позволяет нескольким процессам читать или обновлять Stop
предоставление каждому эксклюзивному доступу только достаточно долго, чтобы завершить соответствующий метод.
Обычно я использую сигнал для обмена информацией между процессами, но в этом случае вам все равно нужен эквивалент защищенного метода для обновления Stop
Если у вас есть несколько процессов в тщательно разработанном проекте, способном остановить симуляцию, остановив часы.
Существует также потенциальная проблема переносимости на основе параллелизма (цикл моделирования Stop
и читать и обновлять).
Вы можете иметь точность в один такт, потому что нет никакой гарантии выполнения или возобновления порядка для процессов, вызывающих методы. Если paebbels использует механизм прохождения / отказа для оценки результатов тестирования, это не будет проблемой, и это обычное решение.
Так что тестбенк tb
делает с изменением:
это именно то, что вы найдете в немаркированном процессе с комментарием
-- stimuli process