Невозможно разрешить несколько постоянных драйверов — два триггера должны изменить один и тот же вектор

Я знаю, что означает ошибка и почему это плохо, но не могу понять, как это сделать по-другому.

Не удается разрешить несколько постоянных драйверов для сети "snake[17]" в змее_драйвере.

(и другие такие же)

Назначение состоит в том, что у нас есть "движущаяся змейка" в std_logic_vector, которая идет от конца к концу, и когда вы нажимаете кнопку (сигнал переключения), змейка меняет свою длину (2, 3, 4, 5, 6, 2, ...)

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

/\____    +--------------------+
toggle ---->  change length    |
          |          v         |
          |  [snake register] =====> snake 17 downto 0
\/\/\/    |          ^         |
clock  ---->  move one step    |
          +--------------------+

Любые идеи приветствуются.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity snake_driver is
    generic
    (
        LEN : integer := 18;
        MAX_SNAKE_LEN : integer := 6
    );

    port
    (
        clk : in std_logic;
        change : in std_logic;
        output : out std_logic_vector(LEN-1 downto 0)
    );
end snake_driver;

architecture Anaconda of snake_driver is
signal snake : std_logic_vector(LEN-1 downto 0) := (0 => '1', 1 => '1', others => '0'); -- two snake pieces
signal dir_right : boolean := FALSE; -- start left
begin


    process(change)

        variable data : std_logic_vector(LEN-1 downto 0); -- two snake pieces

        variable snake_len : integer := 2; -- snake 2 long
    begin

        if rising_edge(change) then -- change snake length
            -- add snake piece based on direction

            data := snake; -- <-- here I tried to avoid the problem
                           -- by caching snake data. To no avail.

            if snake_len = MAX_SNAKE_LEN then

                snake_len := 2;

                -- shorten to 2 len

                if dir_right then
                    -- moving right, remove from left
                    data := std_logic_vector(unsigned(data) and shift_right(unsigned(data), MAX_SNAKE_LEN-2));
                else
                    -- moving left, remove from right
                    data := std_logic_vector(unsigned(data) and shift_left(unsigned(data), MAX_SNAKE_LEN-2));
                end if;
            else

                -- add one at the end
                if dir_right then
                    -- moving right, add on left
                    data := std_logic_vector(unsigned(data) or shift_left(unsigned(data), 1));
                else
                    -- moving left, add on right
                    data := std_logic_vector(unsigned(data) or shift_right(unsigned(data), 1));
                end if;

            end if;


            snake <= data;
        end if;

    end process;




    -- move snake on clock
    process(clk)
        -- variables in the process
        variable data : std_logic_vector(LEN-1 downto 0);

    begin
        -- shift the snake
        if rising_edge(clk) then

            data := snake;

            if dir_right then
                -- right move
                data(LEN-2 downto 0) := data(LEN-1 downto 1);

                if data(0) = '1' then
                    dir_right <= FALSE; -- change direction
                end if;             
            else
                -- left move
                data(LEN-1 downto 1) := data(LEN-2 downto 0);

                if data(LEN-1) = '1' then
                    dir_right <= TRUE; -- change direction
                end if;

            end if;

            snake <= data;

        end if;

    end process;


    -- send changed data to output
    output <= snake;    

end Anaconda;

person MightyPork    schedule 08.11.2014    source источник
comment
Я не уверен. Но в предыдущем проекте мы использовали только rising_edge() на clk. А для другого асинхронного сигнала мы просто использовали change = '1'. Я знаю, что это просто, но, может быть, это сработает?   -  person Lucas Godoy    schedule 08.11.2014


Ответы (1)


Насколько спешат ваши часы? Обычно часы в FPGA довольно быстрые (50 МГц или выше). Если это быстро, вы часто будете сэмплировать сигнал «переключения» на каждом фронте тактового сигнала с элементом задержки для обнаружения фронтов. Вы можете использовать это обнаружение края, чтобы увеличить длину змеи. Если вам нужно перемещать змею достаточно медленно, чтобы человек мог ее обнаружить, вы, как правило, будете перемещать только шаг через каждые х фронтов тактовых импульсов (используя счетчик для генерации включения тактовых импульсов). Все это можно сделать в одном процессе, который зависит только от ваших высокоскоростных тактовых импульсов.

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

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

Вот пример того, что я сказал выше, со змеей, которая всегда вращается вправо, начинается шириной 2 бита и не имеет ограничений на максимальный размер (предупреждение: не тестировалось):

architecture behav of snake is

signal clk       : std_logic; --actually an input. 50MHz
signal toggle    : std_logic := '0'; --actually an input, not an internal signal
signal toggle_d  : std_logic := '0';
signal snake_vec : std_logic_vector(17 downto 0) := "000000000000000011";
signal disable_count : unsigned(20 downto 0):=(others => '0'); --about 50ms to cycle through full range
signal step_count    : unsigned(24 downto 0):=(others => '0'); --about 0.7s to cycle through full range

begin
  process(clk)
    if (clk = '1' and clk'event) then
      toggle_d <= toggle; --store previous value of toggle
      if (toggle_d = '1' and toggle = '0') then --trigger blocking counter on negative edges of toggle
        disable_count <= (others => '1');
      elsif (disable_count /= 0) then --count down if blocking counter is enabled
        disable_count <= disable_count-1;
      end if;

      if (toggle_d = '0' and toggle = '1' and disable_count = 0) then --extend on allowed rising edges of toggle
        snake_vec <= snake_vec or (snake_vec(0) & snake_vec(17 downto 1); --extend the snake by 1
      else
        step_count <= step_count-1; --this could be + or -
        if (step_count = 0) then --functions as a clock enable every 1 in 33.5 million cycles
          snake_vec <= snake_vec(0) & snake_vec(17 downto 0); --rotate snake
        end if;
      end if;      
    end if;
  end process;
end behav;

РЕДАКТИРОВАТЬ: в ситуации, которую вы описали в комментариях, лучшим способом, вероятно, было бы зафиксировать событие на высокоскоростных часах и прочитать его с помощью низкоскоростных часов. Ниже приведен пример, как это сделать. Обратите внимание, что ваш делитель тактовых импульсов должен выдавать выходные данные высокоскоростных тактовых импульсов для каждого нарастающего фронта разделенных тактовых импульсов (при условии, что делитель тактовых импульсов основан на счетчике). Для вашего проекта вы можете захотеть зафиксировать высокую скорость за пределами блока, который использует медленные часы - он может передавать событие в качестве входных данных.

architecture behav of snake is

signal clk       : std_logic; --50MHz
signal divide_event : std_logic; --on the 50MHz domain, single clock wide pulse every rising edge of 4Hz clock, sourced from clock divider
signal clk_slow  : std_logic; --4Hz
signal toggle    : std_logic := '0'; --actually an input, not an internal signal
signal toggle_d  : std_logic := '0';
signal snake_vec : std_logic_vector(17 downto 0) := "000000000000000011";

begin
  process(clk)
    if (clk = '1' and clk'event) then
      toggle_d <= toggle; --store previous value of toggle
      if (toggle_d = '0' and toggle = '1') then
        extend_event <= '1';
      elsif divide_event = '1' then
        extend_event <= '0';
      end if;
  end process;

  process(clk_slow)
    if (clk_slow = '1' and clk_slow'event) then
      if (extend_event = '1') then
        snake_vec <= snake_vec or (snake_vec(0) & snake_vec(17 downto 1); --extend the snake by 1
      else
        snake_vec <= snake_vec(0) & snake_vec(17 downto 0); --rotate snake
      end if;
    end if;  
  end process
end behav;
person QuantumRipple    schedule 08.11.2014
comment
Обратите внимание, что, хотя я запускаю действия как по положительному, так и по отрицательному фронту, я на самом деле не использую логику запуска по фронту из сигнала toggle. Я просто читаю зарегистрированные выборки на положительном фронте часов, чтобы определить приблизительный временной интервал, в течение которого возник положительный или отрицательный фронт гораздо более медленного сигнала (toggle). - person QuantumRipple; 08.11.2014
comment
Змейка должна работать на частоте около 4 Гц, хотя эта частота действительно получена из часов 50M (змейка замедляется, так как другая часть проекта — это переключаемый прескалер, и другие вещи используют ту же частоту). Устранение дребезга не проблема, в комплект входит некоторая санация входов. Я думал установить какой-то сигнал, когда получен переключатель, и очистить и использовать его по краю тактовой частоты, но тут я получил жалобу, что две прецессии не могут записать один и тот же сигнал или что-то в этом роде. Действительно ли проверка переключения на изменение часов является единственным возможным способом? - person MightyPork; 08.11.2014
comment
Вы можете использовать ту же технику семплирования на тактовой частоте 4 Гц, но вам придется удерживать кнопку не менее 1/4 секунды, чтобы гарантировать обнаружение события. В качестве альтернативы вы можете обнаружить события переключения на тактовой частоте 50 МГц, а затем использовать фиксирующий регистр для хранения индикации до тех пор, пока она не будет прочитана на тактовой частоте 4 Гц. Обычно в FPGA физически невозможно записывать в один и тот же триггер (сигнал) из двух разных доменов часов. - person QuantumRipple; 08.11.2014
comment
См. мое редактирование с примером блокировки высокоскоростного домена. Вы также можете использовать сигнал подтверждения, установленный в низкоскоростном домене (на 1 такт каждый раз, когда змейка расширяется), чтобы отменить подтверждение extend_event в высокоскоростном домене, чтобы избежать выхода синхронизации (divide_event) из вашего делителя тактового сигнала, но это ограничивает вас продлением не более чем каждого второго цикла медленной скорости. - person QuantumRipple; 08.11.2014
comment
Спасибо, но уже очень поздно, так что я попробую завтра. Я надеюсь, что Quartus больше не будет проворачивать свои трюки. С тех пор, как я начал, он продолжает развлекать меня новыми и новыми ошибками... Кстати, знаете какую-нибудь хорошую книгу или учебник по этому вопросу? До сих пор мое кодирование в основном было удачным или неудачным. - person MightyPork; 08.11.2014
comment
Боюсь, я никогда не пользовался никакими книгами. Я изучил VHDL из существующего кода во время стажировки за год до того, как прошел свой первый курс по этому языку. Обычно я использую ics.uci.edu/~jmoorkan/vhdlref для простого синтаксиса. ссылка. - person QuantumRipple; 08.11.2014
comment
Итак, я закончил с добавлением защелки на входе, сбрасывающей ее после прихода тактового импульса. То, что не работало в одной архитектуре, отлично работает, если я добавляю его как отдельный экземпляр объекта. Работает похоже на ваш дизайн, я думаю. - person MightyPork; 08.11.2014