На первый взгляд можно ожидать, что приведенный ниже исходный код VHDL будет вести себя как регистр сдвига. В этом д со временем будет
"UUUU0", "UUU00", "UU000", "U0000", "00000", ....
но вместо этого это всегда U
после пяти (или более) последовательных тактов.
Почему это?
Этот код на самом деле является значительно упрощенной версией гораздо более сложной симуляции. Но это демонстрирует симптомы, которые я вижу.
Это демонстрирует этот интересный и неожиданный результат во время симуляции под ModelSim и ActiveHDL. Я не пробовал другие симуляторы и (во-вторых, объяснение причины) хотел бы знать, действуют ли другие так же.
Чтобы правильно ответить на этот вопрос, вы должны понимать, что:
- Я знаю, что это не лучший способ реализации регистра сдвига
- Я знаю, что для синтеза RTL это должно иметь сброс.
- Я знаю, что массив std_logic является std_logic_vector.
- Я знаю оператора агрегации
&
.
Что я также нашел:
- Если назначение
temp(0)<='0';
перемещается внутри процесса, оно работает. - Если цикл развернут (см. Закомментированный код), он работает.
Я еще раз повторю, что это очень упрощенная версия гораздо более сложной конструкции (для конвейерного процессора), сконфигурированная так, чтобы просто показывать неожиданные результаты моделирования. Фактические типы сигналов являются просто упрощением. По этой причине вы должны рассмотреть свои ответы с кодом в форме, как есть.
Я предполагаю, что оптимизатор механизма моделирования VHDL по ошибке (или, возможно, согласно спецификации) не утруждает себя выполнением выражений внутри цикла, так как нет никаких сигналов вне изменения, хотя я могу опровергнуть это, поместив развернутый цикл в цикл.
Поэтому я ожидаю, что ответ на этот вопрос больше связан со стандартами VHDL-моделирования неявного VHDL-синтаксиса и тем, как механизмы VHDL-моделирования выполняют свою оптимизацию, а не в том случае, если приведенный пример кода является лучшим способом сделать что-то или нет.
А теперь код, который я моделирую:
library ieee;
use ieee.std_logic_1164.all;
entity test_simple is
port (
clk : in std_logic;
q : out std_logic
);
end entity;
architecture example of test_simple is
type t_temp is array(4 downto 0) of std_logic;
signal temp : t_temp;
begin
temp(0) <= '0';
p : process (clk)
begin
if rising_edge(clk) then
for i in 1 to 4 loop
temp(i) <= temp(i - 1);
end loop;
--temp(1) <= temp(0);
--temp(2) <= temp(1);
--temp(3) <= temp(2);
--temp(4) <= temp(3);
end if;
end process p;
q <= temp(4);
end architecture;
И испытательный стенд:
library ieee;
use ieee.std_logic_1164.all;
entity Bench is
end entity;
architecture tb of bench is
component test_simple is
port (
clk : in std_logic;
q : out std_logic
);
end component;
signal clk:std_logic:='0';
signal q:std_logic;
signal rst:std_logic;
constant freq:real:=100.0e3;
begin
clk<=not clk after 0.5 sec / freq;
TB:process
begin
rst<='1';
wait for 10 us;
rst<='0';
wait for 100 us;
wait;
end process;
--Note: rst is not connected
UUT:test_simple port map (clk=>clk,q=>q) ;
end architecture;
источник
temp(0)
потому что нет никаких «событий», связанных с литеральной константой. Помещение назначения внутриprocess
создает связь с событиями часов, которая заставляет его работать. Интересно, было бы добавлениеafter
пункта к назначению потенциальным обходным путем.Ответы:
Это связано с тем, что может быть легко оценено во время разработки, формально, что называется «локально статическим выражением». Это не совсем понятное правило, но оно заслуживает некоторого размышления - в конце концов оно имеет смысл, и ваш симулятор совершенно правильно предупреждает вас, генерируя неочевидные результаты.
Теперь он
temp(1)
может быть оценен во время компиляции (даже раньше, чем время разработки), и он может генерировать драйвер для бита 1 «temp».Тем не менее,
temp(i)
включает в себя немного больше работы для инструментов. Принимая во внимание тривиальный характер границ цикла (1-4), для нас, людей, очевидно, что temp (0) не может управляться, а то, что вы делаете, безопасно. Но представьте, что границы были функциямиlower(foo) to upper(bar)
в пакете, объявленном где-то в другом месте ... теперь самое определенное, что вы можете с уверенностью сказать, это то, чтоtemp
оно управляется - таково выражение «локально статическое»temp
.А это значит, что процесс ограничен этими правилами, чтобы управлять всеми
temp
, и в этот момент у вас есть несколько драйверовtemp(0)
- управление процессом (без начального значения, то есть «u») и внешнийtemp(0) <= '0';
. Поэтому, естественно, два драйвера принимают решение «U».Альтернативой было бы «маленькое хакерское правило» (мнение), что если границы цикла были константами, делайте одно, а если они были объявлены как что-то другое, делайте что-то еще и так далее ... чем больше таких странных маленьких правил есть, чем сложнее становится язык ... на мой взгляд, не лучшее решение.
источник