Перейти к содержанию
    

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

Во-первых, подправьте код умножителей под диапазон коэффициентов -1024 ... +1024, поскольку после умножения делить нам надо теперь на 1024.

Поправил, прилагается;

 

о-вторых, теперь, когда мы разобрались с умножением, нужен ли нам 0-ой умножитель в блоке умножителей?

Думаю нет, зачем нам постоянно умножать на 1?

 

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

Вот тут я не совсем понял, получается мне код для умножителей нужно впихнуть в код для бабочки? Код бабочки прилагаю: http://webfile.ru/3392071

 

В-четвертых, все блоки нашего БПФ имеют на входе и выходе один и тот же набор сигналов (относящийся к данным БПФ) и эти сигналы представляют собой комплексные числа. Я предлагаю воспользоваться такой возможностью языка как record и задать тип complex_data и complex_coef для данных и коэффициентов. Еще задать тип complex_data_vector и complex_coef_vector с параметризованной размерностью, чтобы не "таскать" везде по четыре сигнала данных и по три сигнала с коэффициентами. Все это, для удобства, объявить в package - скажем fft_pkg. Все сказанное в это пункте никак не влияет на работоспособность, поэтому если не хотите, то можете этого не делать.

Сделаю позже, пока нужно с 3 пунктом разобраться.

 

Вы с ModelSim работали?

Работал немного.

complex_multiplier.rar

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот тут я не совсем понял, получается мне код для умножителей нужно впихнуть в код для бабочки?

 

С чего Вы это взяли? Никто никого не обязывает ставить регистры на выходе блока. Чисто комбинаторный блок это вполне нормально. Я имел ввиду, что бабочка у нас не будет содержать регистров - будет чисто комбинаторной, а умножители будут иметь регистры на выходе. Впрочем, если хотите, то можете поставить регистры на выходе и бабочки и умножителей - это увеличит pipeline на 1 и практически никак не повлияет на скорость вычисления самого БПФ (ну будет БПФ вычисляться на 6 тактов дольше, ну и что...)

 

Кстати, о бабочке. Пока Вы не убрали из нее регистры, объясните мне пожалуйста, почему бабочка тратит на вычисления аж 2 такта вместо 1? Зачем Вы сделали двойное защелкивание? Бабочка (в отличие, например, от умножителей, особенно если их синтезировать на обычных ячейках, а не на DSP-блоках) выполняет настолько простые операции, что защелкивать промежуточный результат абсолютно бессмысленно - лишняя трата регистров :).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Чисто комбинаторный блок это вполне нормально. Я имел ввиду, что бабочка у нас не будет содержать регистров - будет чисто комбинаторной, а умножители будут иметь регистры на выходе.

Да, но бабочка содержит процесс, а это уже регистр. процесс убрать нельзя, т.к. конструкция if последовательная, а значит может содержаться только в процессе. Тут вижу выход только описывать эти 2 бабочки как компоненты, а затем применять конструкцию if generate. Ну можно в процесс засунуть тупо все сигналы, чтобы он запускался при изменении каждого из них.

 

Кстати, о бабочке. Пока Вы не убрали из нее регистры, объясните мне пожалуйста, почему бабочка тратит на вычисления аж 2 такта вместо 1? Зачем Вы сделали двойное защелкивание?

Я его не хотел делать, так вышло=)) Но тем не менее Quartus ставит промежуточный регистр. Это происходит как раз из-за различных назначений сигналам yi_re_sig/yi_im_sig, i=0,1,2,3 при конструкции if else/

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На счет if generate я погорячился, даже сказал бы тупанул, можно просто каждый сигнал yi_re_sig/yi_im_sig, i=0,1,2,3 посадить на case/ Таким образом уберется процесс и сгенеряться мультиплексоры. бабочка станет чисто комбинационной. Вечером попробую...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, но бабочка содержит процесс, а это уже регистр.

 

 

На счет if generate я погорячился, даже сказал бы тупанул, можно просто каждый сигнал yi_re_sig/yi_im_sig, i=0,1,2,3 посадить на case/ Таким образом уберется процесс и сгенеряться мультиплексоры. бабочка станет чисто комбинационной. Вечером попробую...

 

Спокойствие... только спокойствие.

 

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

 

Синтезатор "детектирует" наличие регистра по конструкциям вида

CLK'event and CLK = '1', rising_edge(clk) и т.д.

 

Если Вы напишите такой, например, код

process(SEL,B,C,D,E)
begin
 if SEL = '0' then
   A <= B or C;
 else
   A <= C and D;
 end if;
end process

то получите обычную комбинаторную логику - никаких регистров синтезатор и не подумает поставить.

 

Причем, если вместо process(SEL,B,C,D,E) Вы напишите process(SEL,B,C), то синтезатор все сделает правильно, но вот симулятор просимулирует неправильно.

 

Если вне всякого процесса Вы напишите

A <= B when rising_edge(CLK) else A;

то получите регистр и без использования process.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

process никогда и ни при каких обстоятельствах не является признаком регистра.

Я знаю. Я в принципе так и указал:

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

Просто сигналов уж больно много.

Вот проект: http://webfile.ru/3398933

Изменено пользователем ZED

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Соеденил:

http://webfile.ru/3399176

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Прошу прощения забыл регистры, на выходе для отсчетов y0_re, y0_im:

http://webfile.ru/3400144

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

наконец-то выдалось немного свободного времени - посмотрел Ваш код.

 

1) Зачем, всетаки, приставка but к портам бабочки ?

 

2) Зачем в умножителе Вы ввели еще один промежуточный сигнал result_re_buf ? Чем Вам не нравится такой код :) ?

process(CLK)
begin
   	if (rising_edge(CLK)) then
		result_re <= std_logic_vector(result_re_sig(b_size + w_size - 3 downto w_size - 2 ));
		result_im <= std_logic_vector(result_im_sig(b_size + w_size - 3 downto w_size - 2));
    end if;
end process;

 

3) Зачем Вам resize в умножителе? Если бы это был просто умножитель предназначенный для чего угодно, то требуется учесть возможность переполнения при +/-. Но тогда и выход должен уже быть не 17 разрядов, а 18. Наш умножитель он только для нашего БПФ годится, а в нашем БПФ в умножителе уже не будет переполнения при +/-. Кстати, объясните почему?

 

4) Зачем в бабочке регистр на y0? Очень плохая идея и стиль выравнивать задержки не там где создается "перекос". У нас регистры стоят в умножителях, 0-ой умножитель удален за ненадобностью, но раз имменно в нем был регистр, то именно вместо него в блоке умножителей и надо поставить регистр для выравнивания, а не в бабочке.

 

Пока мы разбираемся с кодом бабочки и умножителя и вводим record (как там дела? ) давайте параллельно подумаем про память - нам скоро ей заниматься. Посмотрите, пожалуйста, на последнюю версию блок схемы. Согласно блок-схеме у нас получаются два совершенно разных блока памяти. Что нужно добавить на блок схеме, чтобы блоки памяти у нас были совершенно одинаковыми?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1) Зачем, всетаки, приставка but к портам бабочки ?

Чтобы не запутаться, убрал...

 

2) Зачем в умножителе Вы ввели еще один промежуточный сигнал result_re_buf

Опять же, чтобы не запутаться, убрал...=)

 

3) Зачем Вам resize в умножителе? Если бы это был просто умножитель предназначенный для чего угодно, то требуется учесть возможность переполнения при +/-. Но тогда и выход должен уже быть не 17 разрядов, а 18. Наш умножитель он только для нашего БПФ годится, а в нашем БПФ в умножителе уже не будет переполнения при +/-. Кстати, объясните почему?

Resize не нужен т.к. мы расширили до 17 разрядов отчсеты и до 12 разрядов поворачивающие множители, убрал...

 

4) Зачем в бабочке регистр на y0? Очень плохая идея и стиль выравнивать задержки не там где создается "перекос". У нас регистры стоят в умножителях, 0-ой умножитель удален за ненадобностью, но раз имменно в нем был регистр, то именно вместо него в блоке умножителей и надо поставить регистр для выравнивания, а не в бабочке.

Перенес регистр из бабочки в блок умножителей.

 

Пока мы разбираемся с кодом бабочки и умножителя и вводим record (как там дела? )

Не было времени разобраться.

 

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

Не хватает мультиплексора, исправил, прилагаю: http://webfile.ru/3409277

 

Код полной бабочки: http://webfile.ru/3409275

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Никак не пойму, что нужно с записью сделать, как она должна выглядеть, вот до чего я пока додумался:

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

package fft_pkg is
    constant b_size: natural := 17;
    constant w_size: natural := 12;
    
    type complex_data is record
        x_re: std_logic_vector(b_size - 1 downto 0);
        x_im: std_logic_vector(b_size - 1 downto 0);
    end record;
    
    type complex_coef is record
        w_re: std_logic_vector(w_size - 1 downto 0);
        w_im: std_logic_vector(w_size - 1 downto 0);
    end record;
    
    type complex_data_vector is record
        x0: complex_data;
        x1: complex_data;
        x2: complex_data;
        x3: complex_data;
    end record;
    
    type complex_coef_vector is record
        w1: complex_coef;
        w2: complex_coef;
        w3: complex_coef;
    end record;
end package fft_pkg;

 

 

Акак же тогда с проектом быть придется задавать только входной сигнал x, сигнал для коэффициентов w и оперировать типа так:

x.x0.x_re это будет x0_re?

 

и с компонентами тогда как получится можно ли будет сделать так:

entity full_butterfly is
generic (b_size: natural := 17;
         w_size: natural := 12);
port (
CLK: in std_logic;
switch: in std_logic;
x : in complex_data_vector;

w : in complex_coef_vector;

y0_re: out std_logic_vector(b_size - 1 downto 0);
y0_im: out std_logic_vector(b_size - 1 downto 0);
y1_re: out std_logic_vector(b_size - 1 downto 0);
y1_im: out std_logic_vector(b_size - 1 downto 0);
y2_re: out std_logic_vector(b_size - 1 downto 0);
y2_im: out std_logic_vector(b_size - 1 downto 0);
y3_re: out std_logic_vector(b_size - 1 downto 0);
y3_im: out std_logic_vector(b_size - 1 downto 0)
);
end full_butterfly;

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

component butterfly_complex is
generic (b_size: natural := 17);
port(
CLK: in std_logic;
switch: in std_logic;
x: in complex_data_vector;
y0_re: out std_logic_vector(b_size - 1 downto 0);
y0_im: out std_logic_vector(b_size - 1 downto 0);
y1_re: out std_logic_vector(b_size - 1 downto 0);
y1_im: out std_logic_vector(b_size - 1 downto 0);
y2_re: out std_logic_vector(b_size - 1 downto 0);
y2_im: out std_logic_vector(b_size - 1 downto 0);
y3_re: out std_logic_vector(b_size - 1 downto 0);
y3_im: out std_logic_vector(b_size - 1 downto 0)
);
end component butterfly_complex;


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

butterfly: butterfly_complex
port map(
CLK => CLK,
switch => switch,
x => x;
y0_re => y0_re_connect,
y0_im => y0_im_connect,
y1_re => y1_re_connect,
y1_im => y1_im_connect,
y2_re => y2_re_connect,
y2_im => y2_im_connect,
y3_re => y3_re_connect,
y3_im => y3_im_connect
);

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Resize не нужен т.к. мы расширили до 17 разрядов отчсеты и до 12 разрядов поворачивающие множители, убрал...

 

К сожалению, объяснить отсутствие переполнения только лишь увеличением разрядности недостаточно. Представьте, к примеру, что x_re = 32768, x_im = 32768, w_re = 1024 и w_im = 1024. В этом случае result_im = (x_re *w_im + x_im *w_re)/1024 = 65536 - однозначно переполнение... Здесь дело в свойствах БПФ. Если все реализовано правильно, то точки, поступающие на вход БПФ всегда находятся внутри единичной окружности на комплексной плоскости. Коеффициенты - это точки лежащие на краю единичной окружности ( w_re = cos(a), w_im = sin(a) ). Так что если w_re = 1024, то w_im неизбежно будет = 0. Поворачивающими их называют не случайно - при умножении на них точка всего лишь поворачивается на угол a. Она никогда не выйдет за пределы этой единичной окружности - это и есть главная причина отсутствия переполнения. Таким образом, если все сделано правильно, то случай x_re = x_im = 32768 исключен так же как и w_re = w_im = 1024.

 

С record Вы все сделали правильно за исключением ненужных услохнений с именами членов record. Достаточно так:

  type complex_data is record
   re: std_logic_vector(b_size - 1 downto 0);
   im: std_logic_vector(b_size - 1 downto 0);
 end record;

 

Что касается complex_data_vector / complex_coef_vector, то я имел ввиду следующее:

 

type complex_data_vector is array (natural range <>) of complex_data;

type complex_coef_vector is array (natural range <>) of complex_coef;

 

Впрочем, (natural range <>) – это больше для тренировки в освоении языка. При таком задании типа complex_data_vector Вы размерность указываете при использовании. т.е. Вы можете написать

 

entity full_butterfly is
 port
  (
   CLK    : in std_logic;

   switch : in std_logic;

   x        : in complex_data_vector(3 downto 0);

   w        : in complex_coef_vector(3 downto 1);

   y         : out complex_data_vector(3 downto 0)
 );
end full_butterfly;

 

В нашем случае, я, все же, склоняюсь задать размерность жестко. Т.е.

 

type complex_data_vector is array (3 downto 0) of complex_data;

type complex_coef_vector is array (3 downto 1) of complex_coef;

 

тогда

 

entity full_butterfly is
 port
  (
   CLK    : in std_logic;

   switch : in std_logic;

   x        : in complex_data_vector;

   w        : in complex_coef_vector;

   y         : out complex_data_vector
 );
end full_butterfly;

 

обратите внимание, что y, являясь выходным сигналом, тоже может быть complex_data_vector.

 

К сожалению, в record не возможно параметризовать размерность членов (как можно параметризовать размерность самого array) - ее приходится задавать жестко или с помощью констант. Поэтому, что касается внутренних сигналов в бабочке, то ввиду необходимости сделать их на 2 разряда больше не остается ничего другого, как прямо внутри бабочки задать "служебный" тип complex_data_p2_vector:

 

architecture beh_butter of butterfly_complex is

 type complex_p2 is record
   re : std_logic_vector(complex.re'high + 2 downto 0);
   im : std_logic_vector(complex.im'high + 2 downto 0);
 end record complex_p2;

 type complex_data_p2_vector is array (complex_data_vector'range) of complex2;

 

Выносить его в общий package нет никакого смысла т.к. нигде кроме бабочки он не будет востребован.

 

В результате вместо замысловатого x.x0.x_re будет x(0).re, что по-моему, весьма читаемо и удобно.

 

Про память. Т.к. память у нас не тривиальная, то нарисуйте, пожалуйста, блок-схему ее реализации, как Вы ее себе представляете. Могу сразу сказать, что т.к. мы должны вычитывать/сохранять по 4 точки за такт, то без четырех блоков памяти не обойтись, но и 4 блока это далеко не все. Чтобы понять что там еще нужно посмотрите внимательно схему вычисления БПФ.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну, если предположить, что каждая память состоит из 4 блоков памятей, то нам нужны 4 самих этих блока + схема, которая будет преобразовывать адреса для каждого блока памяти:

 

174906.jpg

 

If (addr_in_0 >= 0) and (addr_in_0 <= 511) then
    addr_out_0 <= addr_in_0;
elsif (addr_in_0 >= 512) and (addr_in_0 <= 1023) then
    addr_out_0 <= addr_in_0 - 512;
elsif (addr_in_0 >= 1024) and (addr_in_0 <= 1535) then
    addr_out_0 <= addr_in_0 - 1024;
else
    addr_out_0 <= addr_in_0 - 1536;
end if

 

Вот что-то типа этого я думаю и так для каждого сигнала: addr_in_1, addr_in_2, addr_in_3.

 

Т.е. получатся схемы сравнения (компараторы) и мультиплексоры...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...