Как избежать задержки в выводе простого оператора процесса в VHDL

Я новичок в VHDL. я не хочу знать, почему в следующем коде есть задержка в один цикл и как ее избежать.. в то же время в verilog оператор всегда @(posedge clk) не имеет никакой задержки.. как сделать то же самое в VHDL

library IEEE;
 use IEEE.std_logic_1164.all;

   -- entity
entity t_ff_s is
port ( T,S,CLK : in std_logic;
Q : out std_logic);
 end t_ff_s;
 -- entity
 architecture my_t_ff_s of t_ff_s is
signal t_tmp : std_logic; -- intermediate signal declaration
  begin
    tff: process (S,rising_edge(clk))
     begin
        if (S = '0') then
     t_tmp <= '1';
      --elsif (rising_edge(CLK)) then
     else 
     t_tmp <= T XOR t_tmp; -- temp output assignment
     end if;
    end process tff;
     Q <= t_tmp; -- final output assignment
     end my_t_ff_s;

2 ответа

Решение

Списки чувствительности в VHDL не требуют спецификации края, как в Verilog. VHDL более гибок в том, что вы можете свободно использовать 'event атрибут сигнала в любом месте процесса для реализации поведения, инициируемого фронтом. Вы можете смешивать чувствительную к уровню и краю логику, не прибегая к разделению блоков / процессов или хаков типа negedge для сброса. Вызовы функций как rising_edge(clk) (который реализует тест для clk'event) не допускаются в списке чувствительности. Он содержит только названия сигналов. Ваш код не будет компилироваться как есть.

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

Рассмотрим следующий процесс:

tff: process(S, clk)
begin
  if S = '0' then -- Asynchronous reset (level sensitive)
    t_tmp <= '1';
  elsif rising_edge(clk) then -- Synchronous logic (edge sensitive)
    t_tmp <= T xor t_tmp;
  end if;
end process;

Q <= t_tmp;

Этот процесс выполняется, когда происходит событие S или же clk, Если S равно 0, то условие сброса выполняется с приоритетом над elsif пункт (clk это пофиг). Назначение t_tmp вступает в силу в следующем дельта-цикле, который остается таким же, как текущее время моделирования. В противном случае, если rising_edge(clk) оценивается как истина, то событие произошло clk и его состояние изменилось с "0" (или "L") на "1" (или "H"), что указывает на то, что событие было нарастающим фронтом. Синхронное присваивание имеет место и новый xored t_tmp вступает в силу в следующем дельта-цикле. Изменения в T не заставляйте процесс выполняться, так как его нет (и не должно быть) в списке чувствительности.

Потому что нет безусловных else клаузула t_tmp Сигнал сохраняет свое последнее присвоенное значение, если оба if условия ложные. Это изменится, когда в следующий раз будет S или же clk что вызывает новое назначение t_tmp, Это будет либо следующий фронт синхронизации, либо повторное применение асинхронного сброса.

Назначение Q является непрерывным и фактически совпадает с процессом с t_tmp в своем списке чувствительности. Как следствие, назначение Q происходит дельта-цикл после событий на t_tmp что составляет два дельта-цикла после нарастающего фронта. Если Q вводит логику, которая обновляется раньше, чем второй дельта-цикл ребра, и для его распространения потребуется дополнительный тактовый цикл.

Поведение, окружающее дельта-циклы, может иногда создавать непонятные результаты при проверке сигналов. У вас может быть нарастающий фронт, который должен захватывать входные данные, которые, по- видимому, переходят одновременно на одном и том же временном шаге, когда на самом деле данные переходят на более поздний дельта-цикл и будут захватываться только на следующем фронте тактового сигнала.

Точно так же, если вы создаете простые стробированные часы без какой-либо временной задержки, их фронты будут возникать в одно и то же время, но в более поздних дельта-циклах, чем в не стробированной версии часов. Данные, полученные с "более ранних" тактовых импульсов, будут захвачены стробированной логикой на такт раньше, чем ожидалось. Данные, полученные в другом направлении, будут иметь неожиданную задержку в тактовом цикле.

Не ясно, что является причиной проблемы, которую вы видите, без дополнительной информации о том, как вы ведете S, T, а также clk сигналы, но это, вероятно, связано с поведением дельта-цикла двигателя моделирования в некотором роде.

Эта проблема

Немного более лаконично, чем Кевин,ising_edge - это выражение, а не сигнал, для списка чувствительности требуется именованный сигнал, транзакция, для которой вы возобновляете выполнение приостановленного процесса. Поместите elsif обратно, и в списке чувствительности есть только S и clk.

Обратите внимание, что поскольку t_tmp отсутствует в списке чувствительности, вы не увидите, что Q следует за t_tmp до следующего тактового события, вызывающего отмеченную вами задержку.

Фиксированный синтаксис процесса:

     tff: process (S,clk) -- was (S, risingedge(CLK)), a syntax error)
     begin
        if (S = '0') then
     t_tmp <= '1';
      elsif (rising_edge(CLK)) then  -- put back
     -- else
     t_tmp <= T XOR t_tmp; -- temp output assignment
     end if;
      Q <= t_tmp; -- final output assignment
      end process tff;

Который показывает задержку между t_tmp и Q:

t_ff_s половинная задержка(кликабельно)

Исправьте это, сделав Q одновременным назначением сигнала

Чтобы устранить задержку в половинном такте, вы можете сделать присвоение Q оператором одновременного присваивания сигнала (переместить его за пределы процесса).

tff:
    process (S, clk)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
    end process;
    Q <= t_tmp;   -- concurrent signal assignment

Который дает:

сделать назначение Q параллельным(кликабельно)

И вы можете видеть выше, что t_tmp и Q сейчас в фазе.

Исправьте это, сделав t_tmp переменной

Вы также можете объявить t_tmp как переменную в процессе dff вместо сигнала, и переключение назначений на него в качестве назначения переменных также устранит задержку в один такт между t_tmp и Q.

tff:
    process (S, clk)
        variable t_tmp:  std_logic;
    begin
        if S = '0' then
            t_tmp := '1';
        elsif  rising_edge(clk) then
            t_tmp := T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;

Который показывает:

переменная(кликабельно)

И ghdl, использующий gtkwave, не выводит переменные и не показывает дельта-циклы. Вы можете видеть, что Q происходит на переднем крае часов.

Создание переменной t_tmp также позволяет исключить дельта-цикл между транзакцией в t_tmp и транзакцией в Q.

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

Исправьте это, добавив t_tmp в список чувствительности

И наоборот, вы можете просто добавить t_tmp в список чувствительности (вместе с S и clk).

tff:
    process (S, clk, t_tmp)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;

просто добавьте t_tmp в список чувствительности(кликабельно)

И это медленнее, чем все остальные исправления, потому что оператор if выполняется каждый раз, когда t_tmp имеет событие, а также S или CLK. ising_edge - это вызов функции, который динамически разрабатывает свой список интерфейсов, что значительно снижает производительность симулятора, особенно если вы используете много этих примитивов.

Это было сделано с помощью испытательного стенда:

library IEEE;
 use IEEE.std_logic_1164.all;

   -- entity
entity t_ff_s is
port ( T,S,CLK : in std_logic;
Q : out std_logic);
 end entity t_ff_s;

architecture my_t_ff_s of t_ff_s is
signal t_tmp : std_logic; -- intermediate signal declaration
  begin
    tff: process (S,clk) -- was (S, risingedge(CLK)), a syntax error)
     begin
        if (S = '0') then
            t_tmp <= '1';
        elsif (rising_edge(CLK)) then  -- put back
        -- else
            t_tmp <= T XOR t_tmp; -- temp output assignment
        end if;
             Q <= t_tmp; -- final output assignment
    end process tff;
end my_t_ff_s;

architecture foe of t_ff_s is
    signal t_tmp: std_logic;
begin
tff:
    process (S, clk)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
    end process;
    Q <= t_tmp;   -- concurrent signal assignment
end architecture;

architecture fie of t_ff_s is
begin
tff:
    process (S, clk)
        variable t_tmp:  std_logic;
    begin
        if S = '0' then
            t_tmp := '1';
        elsif  rising_edge(clk) then
            t_tmp := T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;
end architecture;

architecture fee of t_ff_s is
    signal t_tmp: std_logic;
begin
tff:
    process (S, clk, t_tmp)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;
end architecture;

library ieee;
use ieee.std_logic_1164.all;

entity test_tff is
end entity;

architecture foo of test_tff is
    signal CLK: std_logic := '0';
    signal T:   std_logic := '0';
    signal S:   std_logic := '0';
    signal Q:   std_logic;

    component t_ff_s is
        port (
            signal CLK:     in  std_logic;
            signal T:       in  std_logic;
            signal S:       in  std_logic;
            signal Q:       out std_logic
        );
    end component;
begin

DUT: 
    t_ff_s
        port map (
            T => T,
            S => S,
            CLK => CLK,
            Q => Q
        );
CLOCK:
    process
    begin
        wait for 10 ns;
        CLK <= not CLK;
        if Now > 250 ns then
            wait;
        end if;
    end process;

SET:
    process
    begin
        S <= '0';
        wait for 20 ns;
        S <= '1';
        wait;
    end process;

TOGGLE:
    process
    begin
        wait for 20 ns;
        T <= '1';
        wait for 60 ns;
        T <= '0';
        wait for 40 ns;
        T <= '1';
        wait;
    end process;

end architecture;

configuration my_t_ff_s_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(my_t_ff_s);
        end for;
    end for;
end configuration;

configuration concurrent_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(foe);
        end for;
    end for;
end configuration;

configuration variable_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(fie);
        end for;
    end for;
end configuration;

configuration sensitivity_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(fee);
        end for;
    end for;
end configuration;

обратите внимание на использование конфигурации

Использование объявлений конфигурации VHDL для разрешения использования нескольких архитектур. (my_t_ff_s - оригинал, foe - с одновременным назначением Q, fie - с t_tmp в качестве переменной и fee - с t_tmp в списке чувствительности).

И, что удивительно, анализатор ghdl был очень полезен для правильного синтаксиса объявлений конфигурации. Как только вы получите первый, остальные легко.

Мы склонны ржаветь, используя конфигурацию, которая обычно не поддерживалась исторически инструментами синтеза. Но опять же, это симуляция для проверки.

И для тех, кто с ghdl и gtkwave, это было сделано так:

ghdl -a t_ff.vhdl
ghdl -e my_t_ff_s_config
ghdl -e concurrent_config
ghdl -e concurrent_config
ghdl -e чувствительность_конфиг
ghdl -r my_t_ff_s_config --wave = test_tff_my_t_ff_s.ghw
ghdl -r concurrent_config --wave = test_tff_foe.ghw
ghdl -r variable_config --wave = test_tff_fie.ghw
ghdl -ritivity_config --wave = test_tff_fee.ghw

GHW - это собственный формат файла дампа формы волны ghdl, понятный gtkwave.

В gtkwave:

открыть t_ff_s.gtkw (читает в test_tff_my_t_ff_s.ghw)
(иначе читайте в test_tff_my_t_ff_s.ghw и добавляйте сигналы в
отображение формы сигнала, форматирование окна, сохранение файла сохранения в t_ff_s.gtkw)

новая вкладка открыта test_tff_foe.ghw
читать сохранить файл открыть t_ff_s.gtkw
открыть новую вкладку test_tff_fie.ghw
читать сохранить файл открыть t_ff_s.gtkw
новая вкладка открыта test_tff_fee.ghw
читать сохранить файл открыть t_ff_s.gtkw

Обратите внимание, что ghdl не сохраняет переменное состояние или дельта-циклы, t_tmp не будет отображаться в осциллограмме для test_ff_fie.ghw.

Другие вопросы по тегам