2014-02-09 4 views
2

В университетском курсе о настраиваемых встроенных системах (на ZYNQ-7010) мы недавно реализовали (наивный) фильтр нижних частот, который применил бы одномерное гауссовское ядро ​​(0,25 * [1 2 1]) к данным, поступающим из блока ОЗУ.VHDL - поведение переменной по сравнению с сигналом в очереди

Мы решили кэшировать (то есть очередь) три пикселя, а затем работать с ними в режиме on-line в процессе вывода данных. Наш первый подход состоял в том, чтобы иметь три переменные процесса и перевернуть их в

pixel[k-2] := pixel[k-1]; 
pixel[k-1] := pixel[k]; 
pixel[k] := RAM(address); 

мода; Ниже приводится полный процесс:

process (clk25) 
    -- queue 
    variable pixelMinus2 : std_logic_vector(11 downto 0) := (others => '0'); 
    variable pixelMinus1 : std_logic_vector(11 downto 0) := (others => '0'); 
    variable pixelCurrent : std_logic_vector(11 downto 0) := (others => '0'); 

    -- temporaries 
    variable r : unsigned(3 downto 0); 
    variable g : unsigned(3 downto 0); 
    variable b : unsigned(3 downto 0); 
begin 
    if clk25'event and clk25 = '1' then 
     pixelMinus2 := pixelMinus1; 
     pixelMinus1 := pixelCurrent; 
     pixelCurrent := RAM(to_integer(UNSIGNED(addrb))); 

     IF slv_reg0(3) = '0' THEN 
      -- bypass filter for debugging 
      dob <= pixelCurrent; 
     ELSE 
      -- colors are 4 bit each in a 12 bit vector 
      -- division by 4 is done by right shifting by 2 
      r := (
          ("00" & unsigned(pixelMinus2(11 downto 10))) 
         + ("00" & unsigned(pixelMinus1(11 downto 10))) 
         + ("00" & unsigned(pixelMinus1(11 downto 10))) 
         + ("00" & unsigned(pixelCurrent(11 downto 10))) 
        ); 

      g := (
          ("00" & unsigned(pixelMinus2(7 downto 6))) 
         + ("00" & unsigned(pixelMinus1(7 downto 6))) 
         + ("00" & unsigned(pixelMinus1(7 downto 6))) 
         + ("00" & unsigned(pixelCurrent(7 downto 6))) 
        ); 

      b := (
          ("00" & unsigned(pixelMinus2(3 downto 2))) 
         + ("00" & unsigned(pixelMinus1(3 downto 2))) 
         + ("00" & unsigned(pixelMinus1(3 downto 2))) 
         + ("00" & unsigned(pixelCurrent(3 downto 2))) 
        ); 

      dob <= std_logic_vector(r) & std_logic_vector(g) & std_logic_vector(b); 
     END IF; 
    end if; 
end process; 

Однако это оказалось ужасно неправильным; Синтез потребует возраста и приводит к приблизительному использованию LUT приблизительно 130% возможностей устройства.

Мы позже изменили реализацию для использования сигналов вместо переменных и это решены все проблемы; Аппарат вел себя так, как ожидалось, и использование LUT снизилось до нескольких процентов.

Мой вопрос заключается в том, что вызывает здесь проблему при использовании переменных, поскольку, как мы понимаем, она должна работать именно так.

ответ

2

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

Таким образом, когда переменное является использование, эта линия реализует RAM с асинхронным чтения на основе addrb:

pixelCurrent := RAM(to_integer(UNSIGNED(addrb))); 

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

Типичные технологии ПЛИС имеют выделенное оборудование для ОЗУ с синхронным , но ОЗУ с асинхронным состоят из комбинаторной логики (смотрите таблиц/LUT).

Так огромное количество ТМП, которое появляется при использовании переменной для pixelCurrent потому, что инструмент синтеза пытается отобразить ОЗУ с асинхронного чтения в ТМП, который обычно требует огромного количества ТМП и делает в результате RAM очень медленно.

В конвейерном дизайне это звучит как асинхронные RAM чтения не требуется, так что если pixelCurrent представляет собой сигнал, синхронный ОЗУ используется вместо и инструмента синтеза будет отображать ОЗУ до внутреннего аппаратного блока ОЗУ, с код, как:

pixelMinus2 := pixelMinus1; 
pixelMinus1 := pixelCurrent; 
pixelCurrent <= RAM(to_integer(UNSIGNED(addrb))); 
3

сигналы, являющихся средства связи между процессами, имеет семантику присваивания тщательно разработана, чтобы избежать гонок условий и опасности. См. this Q&A и this link to "VHDL's crown jewel" для подробностей.

Поэтому при назначении pixelCurrent (сигнал)

pixelCurrent <= RAM(to_integer(UNSIGNED(addrb))); 

назначение не происходит, пока процесс не приостановит (что для RTL кода, как правило, когда процесс завершается и в списке чувствительности), и результат не доступен в течение этого процесса, пока он не просыпается до if rising_edge(clk25). Таким образом, это создает регистр конвейера.

Переменные в процессе VHDL действуют как переменные в процессе на любом другом императивном языке (C и т. Д.) - после обновления их новое значение немедленно доступно.

Поэтому следующее:

pixelCurrent := RAM(to_integer(UNSIGNED(addrb))); 

IF slv_reg0(3) = '0' THEN 
    -- bypass filter for debugging 
    dob <= pixelCurrent; 

распространяется новое значение pixelCurrent в остальной части процесса, создавая ОГРОМНЫЙ конструкцию, которая пытается выполнить все, что в один тактовый цикл.

Существует два решения: я предпочитаю использовать сигналы для регистров трубопроводов, так как вы можете describe the pipeline самым естественным образом (сначала с первым этапом).

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

pixelMinus2 := pixelMinus1; 
pixelMinus1 := pixelCurrent; 
pixelCurrent := RAM(to_integer(UNSIGNED(addrb))); 

является описание трубопровода ОБРАТНОЙ так, что присвоение переменной приходит после последнего использования его стоимости ,

Просто переместите эти три задания после большого IF slv_reg0(3), и ваша переменная версия должна работать.

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

Смежные вопросы