Jump to content

    
Sign in to follow this  
ZED

Реализация БПФ на ПЛИС

Recommended Posts

Суворова Е.А. Шейнин Ю.Е. Проектирование цифровых систем на VHDL. Стр. 248. Но я, по-видимому, несколько ошибся, там это для сопоставления результатов моделирования и синтеза.

Share this post


Link to post
Share on other sites

Извиняюсь за долгое молчание. Совсем не было времени.

Я написал небольшую заметку о том, как формировать задержанные копии сигналов.

 

How_Make_Delayed_Copy_of_Signals.pdf

 

Надеюсь, теперь будет понятно, как реализовать блок управления БПФ.

Share this post


Link to post
Share on other sites

Я так понял, что сигналы для записи в память мы будем задерживать, основываясь на функциональной связи, т.е. вычитаем 5 из исходного значения счетчика бабочек. А как быть с сигналами для чтения коэффициентов? Там нужно вычитать 3*MODULE_ROM или их будем делать на FIFO?

Share this post


Link to post
Share on other sites
Я так понял, что сигналы для записи в память мы будем задерживать, основываясь на функциональной связи, т.е. вычитаем 5 из исходного значения счетчика бабочек. А как быть с сигналами для чтения коэффициентов? Там нужно вычитать 3*MODULE_ROM или их будем делать на FIFO?

 

Что касается адресов на запись, то так действительно можно сделать. Хорошее упражнение. Вопрос-подсказка: до скольки должен считать счетчик бабочек в этом случае?

 

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

Share this post


Link to post
Share on other sites
Вопрос-подсказка: до скольки должен считать счетчик бабочек в этом случае?

Счетчик бабочек по-моему должен по-прежнему считать от 0 до 511, а функциональную связь организовать следующим образом:

ADDR_WR <= std_logic_vector(BUTTERFLY_COUNTER - 5);

. Или я чего-то не так понял.

 

Кстати у меня еще появился вопрос по заметке. Если проанализировать схему на рис. 8. Там написано, что адрес записи WR_ADDR прекратит обновляться за 3 такта до окончания вычислений. Но ведь сигнал Enable запрещающий вычисления дойдет также за 3 такта, так что все компенсируется.

 

Вот еще у меня какая мысль увеличить счетчик бабочек на один разряд, чтоб он считал от 0 до 516 и вот по его значениям уже управлять.

Share this post


Link to post
Share on other sites
Счетчик бабочек по-моему должен по-прежнему считать от 0 до 511, а функциональную связь организовать следующим образом:
ADDR_WR <= std_logic_vector(BUTTERFLY_COUNTER - 5);

. Или я чего-то не так понял.

 

При использовании функциональной связи, если счетчик бабочек будет считать до 511, то ничего не получится по тем же причинам, что и на рис. 8. Поэтому, объяснения см. ниже.

 

Кстати у меня еще появился вопрос по заметке. Если проанализировать схему на рис. 8. Там написано, что адрес записи WR_ADDR прекратит обновляться за 3 такта до окончания вычислений. Но ведь сигнал Enable запрещающий вычисления дойдет также за 3 такта, так что все компенсируется.

 

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

 

Одна и та же схема может работать в разных режимах. Это определяется управляющими сигналами. Поэтому 2 по-разному реализованные схемы (блока) в одном режиме могут давать эквивалентные результаты, а в другом режиме совершенно разные. Эквивалентные результаты не всегда означает одинаковые - нередко на временной диаграмме есть интервалы времени, на которых все равно какие значения будут принимать сигналы. В этих интервалах два по-разному реализованных блока, могут выдавать совершенно разные значения сигналов и это ни сколько не помешает работе устройства. Но зато, в те интервалы времени, где сигналы должны иметь строго определенное значение их выходы будут строго совпадать. В заметке описано, при каких условиях и в каких случаях работу схемы управления на рисунках 7, 8 и 9 можно считать эквивалентной, а в каких нет. Соответственно, в каких случаях можно использовать функциональную связь (или как правильно ее реализовать), а в каких это не получится.

 

Возьмем схемы на рисунках 7 и 8 и посмотрим, что получится, если подать на них одно и то же управление. Начнем со схемы на рис. 7, когда адрес записи формирует клон счетчика формирующего адрес чтения. Enable_n обозначены выходы соответствующих триггеров задержки. Enable_4 - выход последнего триггера, подается непосредственно на WE. Как видно из диаграммы, за время активности входного Enable читаются данные по адресам 0...9. Через 4 такта эти обработанные данные появятся на входе памяти. Вместе с ними на память придет задержанная на 4 такта копия сигнала Enable (на диаграмме Enable_4) и пока этот Enable_4 активен счетчик-клон сформирует такую же последовательность адресов записи 0...9, что и счетчик, формирующий адреса чтения.

 

post-7537-1257111149_thumb.png

 

Теперь посмотрим, какая диаграмма получится у схемы на рис. 8 при таком же сигнале Enable на входе. Когда RD_ADDR станет равен 3, в этом же такте станет активен сигнал Enable_3. Вычитатель начнет работать - вычислять выражение "RD_ADDR - 3". Т.к вычитатель имеет регистр на выходе, то результат будет появляться с задержкой на 1 такт. Таким образом, когда на выходе вычитателя появится первый правильный адрес на память одновременно придут первые данные и разрешение записи (Enable_4). Но вот входной Enable закончился и RD_ADDR перестал обновляться - остановился на значении 10. Но Enable_3 будет активен еще 3 такта и вычитатель еще три такта будет вычислять выражение "RD_ADDR - 3". Но т.к. RD_ADDR больше не обновляется, то и WD_ADDR будет иметь одно и то же значение: 10 - 3 = 7. Т.е. 3 последних такта поступающие данные будут записаны по одному и тому же адресу памяти - 7.

 

post-7537-1257111204_thumb.png

 

Такая же ситуация произойдет и в БПФ, если счетчик бабочек остановится на 511, а адрес записи будет реализован на основе функциональной связи.

 

Вот еще у меня какая мысль увеличить счетчик бабочек на один разряд, чтоб он считал от 0 до 516 и вот по его значениям уже управлять.

 

Мысль хорошая. Если бы Вы высказали только ее одну, то я бы ошибочно подумал, что Вы правильно поняли как реализовать адрес записи, опираясь на функциональную связь. :) Чтобы правильно реализовать адрес записи, опираясь на функциональную связь, нужно чтобы счетчик бабочек не останавливался на 511 и считал дальше. Разумеется, что нет необходимости вводить еще один счетчик для отсчета тактов паузы, если у нас и так уже есть счетчик, по которому мы можем определить и начало и конец паузы.

Share this post


Link to post
Share on other sites

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

 

БПФ получился одноразовый - включили питание, сбросили ресетом все в исходное состояние, загрузили данные, подали старт, провели вычисления, выгрузили данные и все... Больше БПФ запустить нельзя... Нужно опять подавать ресет. Схема управления не восстанавливает своего начального значения к следующему запуску. Либо все должно приходить в исходное состояние по сигналу Start, либо самостоятельно, после окончания вычислений.

 

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

 

Продолжение следует... :)

Share this post


Link to post
Share on other sites

Так гораздо лучше.

 

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

 

process
begin
  wait until (rising_edge(CLK));
    if (RST = '1') then
      STATE <= '1';
    elsif (START = '1') then
      STATE <= '0';
    end if;

    if (COUNT_ETAP = 5) and (BUTTERFLY_COUNTER = 516) then
      STATE <= '1';
    end if;

end process;

 

Сделали бы что-нибудь подобное за пределами process получили бы от синтезатора сообщение об ошибке, что сигнал имеет несколько драйверов. Но поскольку в теле process операторы "исполняются" последовательно, синтезатор опираясь на это преобразует ваш код в один единственный if ... elsif ... elsif ... end if, а условия и соответствующие им действия расставит в таком порядке приоритетов, который соответствует по поведению вашему коду. Для наглядности поменяем значения, присваиваемые STATE при сбросе и при старте.

 

process
begin
  wait until (rising_edge(CLK));
    if (RST = '1') then
      STATE <= '0';         --было '1'
    elsif (START = '1') then
      STATE <= '1';         --было '0'
    end if;

    if (COUNT_ETAP = 5) and (BUTTERFLY_COUNTER = 516) then
      STATE <= '1';
    end if;

end process;

 

Предположим, что случился фронт CLK и все условные выражения истинны. Начинаем "исполнять" код. В первом if сигнал RST имеет высший приоритет по сравнению со START. Значит STATE получит значение '0'. Идем дальше, "исполняем" второй if. Т.к. условие истинно, то STATE получит значение '1' и, поскольку на этом "исполнение" приостанавливается до следующего фронта CLK, STATE будет хранить '1'. Синтезатор должен поставить триггер, работающие именно таким образом. Поэтому получается, что наибольшим приоритетом обладает самый последний if, а наименьшим - самый первый. Эквивалентный код, которым синтезатор заменит ваш получается следующий:

 

process
begin
  wait until (rising_edge(CLK));
    if (COUNT_ETAP = 5) and (BUTTERFLY_COUNTER = 516) then
      STATE <= '1';
    elsif (RST = '1') then
      STATE <= '0';         --было '1'
    elsif (START = '1') then
      STATE <= '1';         --было '0'
    end if;

    end if;

end process;

 

Получается, что сигнал сброса RST не самый приоритетный! Сброс, поданный одновременно с истинным выражением "(COUNT_ETAP = 5) and (BUTTERFLY_COUNTER = 516)" будет проигнорирован.

 

В вашем коде по условиям "(COUNT_ETAP = 5) and (BUTTERFLY_COUNTER = 516)" и "(RST = '1')" STATE получает одно и тоже значение. Поэтому, в конечном счете, код будет работать правильно, но такие удачные совпадения будут далеко не всегда. В каком-нибудь проекте получите трудно обнаруживаемую ошибку, которая будет давать сбои редко и не стабильно.

 

Такая же ситуация у Вас с BANK_COUNTER_WR.

 

Я советую задавать приоритеты условий в явном виде.

 

Еще я обратил внимание, что у Вас довольно часто в разных process встречается условие "(STATE = '0' and BUTTERFLY_COUNTER = 516)". Я, обычно, в таких случаях добавляю еще один комбинаторный signal, который отражает истинность данного условия и имеет, по возможности, имя отражающее суть этого условия. Так же это удобно, если условие потребуется изменить - не придется выискивать все его экземпляры в коде. Например:

 

END_OF_STAGE <= '1' when (STATE = '0' and BUTTERFLY_COUNTER = 516) else '0';

process
begin
  wait until (rising_edge(CLK));
    if END_OF_STAGE = '1' then
      ...
    end if;

end process;
...
process
begin
  wait until (rising_edge(CLK));
    if END_OF_STAGE = '1' then
      ...
    end if;

end process;

 

 

Формирование ADDR_ROM_COUNTER теперь, кажется, сделано так как я имел ввиду. Но это я еще посмотрю по-внимательнее. Диаграмму пока тоже еще не проверил, хотя выглядит правдоподобно.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this