Почему этот простой шаблон VHDL для регистра сдвига не работает должным образом

8

На первый взгляд можно ожидать, что приведенный ниже исходный код 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 в объявлении сигнала, я обнаружил, что симуляторы vhdl странно определяют, где вы инициализируете вещи
Matt
Похоже, что симулятор игнорирует параллельное присваивание, temp(0)потому что нет никаких «событий», связанных с литеральной константой. Помещение назначения внутри processсоздает связь с событиями часов, которая заставляет его работать. Интересно, было бы добавление afterпункта к назначению потенциальным обходным путем.
Дэйв Твид

Ответы:

7

Это связано с тем, что может быть легко оценено во время разработки, формально, что называется «локально статическим выражением». Это не совсем понятное правило, но оно заслуживает некоторого размышления - в конце концов оно имеет смысл, и ваш симулятор совершенно правильно предупреждает вас, генерируя неочевидные результаты.

Теперь он 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».

Альтернативой было бы «маленькое хакерское правило» (мнение), что если границы цикла были константами, делайте одно, а если они были объявлены как что-то другое, делайте что-то еще и так далее ... чем больше таких странных маленьких правил есть, чем сложнее становится язык ... на мой взгляд, не лучшее решение.

Брайан Драммонд
источник
Хороший ответ (+1), но я не согласен с вашей характеристикой "хакерского правила". Весь смысл симуляции состоит в том, чтобы представлять поведение реального оборудования. Я понимаю ограничения, создаваемые независимой компиляцией отдельных модулей, но я думаю, что должно быть правило, что все, что может быть оценено во время компиляции, должно быть. Это было бы гораздо более общим правилом и помогло бы системе придерживаться принципа «наименьшего удивления». Позволить инструментам не выполнять эти оценки, мне кажется, что это более "хакерски".
Дэйв Твид
Справедливый комментарий - Ада, например, имеет (и формально выражает) гораздо большую сложность в отношении таких правил, и ей удается представить нам гораздо более простое представление (без WTF-фактора C!). VHDL был изначально упрощен (IMO слишком далеко) от Ada. Но, возможно, он мог бы принять правила «замораживания типов» Ады, которые позволили бы оптимизировать этот тип, когда он явно безопасен (как здесь), и запретить его в противном случае ...
Брайан Драммонд
Спасибо Брайан, то, что вы говорите, безусловно, имеет смысл. Идея одного простого правила, а не многих непонятных правил, кажется, тоже имеет смысл. Вы бы сказали, что это поведение верно (и действительно указано) для всех симуляторов, или это только два, которые я пробовал?
Джейсон Морган
2
Если бы я нашел тот, который сделал что-то другое, я бы подал сообщение об ошибке! Одна вещь, которую крупнейшие хулители VHDL скажут в ее пользу, это то, что она гарантирует согласованные результаты моделирования в тех случаях, когда другие языки (не только Verilog) этого не делают. (хотя да, иногда его недостатки меня тоже беспокоят!)
Брайан Драммонд
1
Эксперимент по быстрому исправлению: если мой ответ правильный, вы можете набрать "temp (0) <= 'Z';" в процессе, поэтому «отключение» фантомного драйвера, и внешний драйвер будет работать ...
Брайан Драммонд