Начните статью со страницы мотивации

Реализация базовых компонентов ЦОС : Комплексный умножитель

VHDL, комплексное умножение, цос

Автор: Amurak

Дата: 31.05.2021 10:29

Категория:ЦОС

1085

11

Оглавление

 

*О найденных опечатках и замечаниях просим сообщить admin@fpga-systems.ru

 

Аннотация

В данной статье рассматриваются особенности реализации одного из базовых компонентов цифровой обработки сигналов - комплексного умножителя. Разобраны способы разбиения умножения комплексных чисел на простейшие арифметические операции. Представлено RTL описание параметризируемого модуля на языке VHDL-2008.

1. Теоретическая часть

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

Умножение двух комплексных чисел определяется как:

Если раскрыть скобки, то мы получим следующее выражение:

При этом:

Схематическое изображение данных операций представлено на рисунке 1.

Рисунок 1 – Схема комплексного умножения

Для реализации данной схемы требуется четыре умножителя и два сумматора. Обозначим данную схему как «Схема 1».

Возможен и другой способ получения тех же значений:

Схематическое изображение выделенных операций представлено на рисунке 2.

Рисунок 2 – Альтернативная схема комплексного умножения

Для реализации данной схемы требуется три умножителя и пять сумматоров. Обозначим данную схему как «Схема 2».

2. Функциональные схемы

Для достижения максимально возможной частоты обработки при реализации комплексного умножения на ПЛИС фирмы Xilinx следует использовать аппаратный блок DSP48, который содержит в себе умножитель, сумматор, предварительный сумматор, а так же наборы триггеров для входных/выходных данных и промежуточных результатов вычислений. В качестве примера будут рассмотрены схемы на основе DSP48E1.

Один из таких вариантов реализации «Схемы 1» представлен на рисунке 3.

Рисунок 3 – реализация «Схемы 1» на блоках DSP48

Как видно из рисунка, данный вариант комплексного умножителя полностью укладывается в ресурсы аппаратных блоков DSP48E1 без использования дополнительной логики. Блоки 0 и 1 используются для расчета действительной части, 2 и 3 – мнимой части выходного числа.

Ввиду того, что умножитель DSP48E1 имеет разрядность 25x18, это накладывает ограничения на максимальную разрядность входных данных. В данном случае для входа A выбрана разрядность 25 бит, для входа B– 18 бит. Выход в таком случае будет иметь разрядность 44 бита.

Латентность данной схемы равняется четырем тактам.

«Схему 2» можно реализовать аналогичным образом, если использовать предварительный сумматор, как показано на рисунке 4.

Рисунок 4 - реализация «Схемы 2» на блоках DSP48

Как видно из рисунка, данный вариант также полностью укладывается в ресурсы DSP48E1 и при этом используется на один аппаратный блок меньше. Блоки 0 и 1 используются для расчета действительной части, 1 и 2 – мнимой части выходного числа.

В данной схеме дополнительные ограничения на разрядность входных данных вносит тот факт, что данные поступают на предварительный сумматор и умножитель в различных комбинациях, поэтому максимальная разрядность входов A и B равна 17 битам. Выход в таком случае будет иметь разрядность 35 бит.

Латентность данной схемы также равняется четырем тактам.

 

3. RTL описание

Для описания комплексных чисел будет использоваться пользовательский тип t_iq, который объявлен в пакете pkg_rtl_modem_types и включает в себя два поля: i и q типа signed. VHDL 2008 позволяет объявлять данные поля без ограничений.

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

package pkg_rtl_modem_types is
    type t_iq is record
        i : signed;
        q : signed;
    end record;
end;

Листинг 1 – Объявление типа t_iq

Объявим интерфейс комплексного умножителя:

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

library rtl_modem;
    use rtl_modem.pkg_rtl_modem_types.all;

entity complex_multiplier is
    generic(
          g_a_dw                    : integer := 12
        ; g_b_dw                    : integer := 12
        ; g_type                    : integer := 1
    );
    port(
          iCLK                      : in std_logic
        ; iV                        : in std_logic
        ; iA                        : in t_iq
        ; iB                        : in t_iq
        ; oV                        : out std_logic
        ; oC                        : out t_iq
    );
end;

Листинг 2 – Интерфейс комплексного умножителя

Порт iCLK – сигнал тактирования модуля, iV– сигнал валидности входных данных, iAи iB– входные комплексные числа, oV– валидность выходных данных, oC– результат умножения.

Параметры g_a_dwи g_b_dwзадают разрядность соответствующих входов Aи B. Параметр g_typeопределяет, какая из схем умножения будет реализована: 1 – «схема 1», 2 – «схема 2».

Объявим сигналы для приема данных с входных и выдачи данных на выходные порты:

architecture behavioral of complex_multiplier is
    constant c_c_dw : integer := g_a_dw + g_b_dw + 1;

    -- input buffers
    signal ib_v : std_logic;
    signal ib_a : t_iq(i(g_a_dw - 1 downto 0), q(g_a_dw - 1 downto 0));
    signal ib_b : t_iq(i(g_b_dw - 1 downto 0), q(g_b_dw - 1 downto 0));

    signal pipe_v : std_logic_vector(0 to 4) := (others => '0');

    -- output buffers
    signal ob_v : std_logic;
    signal ob_c : t_iq(i(c_c_dw - 1 downto 0), q(c_c_dw - 1 downto 0;

begin
----------------------------------------------------------------------------------------
-- input 
----------------------------------------------------------------------------------------
    ib_v                            <= iV;
    ib_a                            <= iA;
    ib_b                            <= iB;

    pipe_v(0)                       <= ib_v;
    pipe_v(1 to pipe_v'high)        <= pipe_v(0 to pipe_v'high - 1)             
                                        when rising_edge(iCLK);
----------------------------------------------------------------------------------------
-- output 
----------------------------------------------------------------------------------------
    oV                              <= ob_v;
    oC                              <= ob_c;
end;

Листинг 3 – Описание основных сигналов

Сигнал pipe_v предназначен для задержки сигнала валидности со входа на выход, длина конвейера при этом определяется латентностью схемы (напомним, у обеих реализация умножителя она равна четырем).

Разрядность выходных данных определяется операциями умножения и сложения. Тело основного процесса, реализующего умножитель по «схеме 1» имеет вид:

    p_main : process(iCLK)
    begin
        if rising_edge(iCLK) then
            if pipe_v(0) = '1' then
                dsp0_areg1 <= ib_a.i;                       -- A
                dsp1_areg1 <= ib_a.q;                       -- a
                dsp2_areg1 <= ib_a.i;                       -- A
                dsp3_areg1 <= ib_a.q;                       -- a
                dsp0_breg1 <= ib_b.i;                       -- B
                dsp1_breg1 <= ib_b.q;                       -- b
                dsp2_breg1 <= ib_b.q;                       -- b
                dsp3_breg1 <= ib_b.i;                       -- B
            end if;

            if pipe_v(1) = '1' then
                dsp0_areg2 <= dsp0_areg1;                   -- A
                dsp0_breg2 <= dsp0_breg1;                   -- B
                dsp1_mreg  <= dsp1_areg1 * dsp1_breg1;      -- a x b
                dsp2_areg2 <= dsp2_areg1;                   -- A
                dsp2_breg2 <= dsp2_breg1;                   -- b
                dsp3_mreg  <= dsp3_areg1 * dsp3_breg1;      -- a x B
            end if;

            if pipe_v(2) = '1' then
                dsp0_mreg <= dsp0_areg2 * dsp0_breg2;       -- A x B
                dsp1_preg <= resize(dsp1_mreg, c_c_dw);     -- a x b
                dsp2_mreg <= dsp2_areg2 * dsp2_breg2;       -- A x b
                dsp3_preg <= resize(dsp3_mreg, c_c_dw);     -- a x B
            end if;

            if pipe_v(3) = '1' then
                dsp0_preg <=                                -- A x B - a x b
                    resize(dsp0_mreg, c_c_dw) -
                    dsp1_preg;
                dsp2_preg <=                                -- A x b + a x B
                    resize(dsp2_mreg, c_c_dw) +
                    dsp3_preg;
            end if;
        end if;
    end process;

Листинг 4 – Исходный код основного процесса «Схемы 1»

По сути, в теле описаны преобразования всех триггеров, представленных на рисунке 3. Аналогичным образом описывается «схема 2»:

    p_main : process(iCLK)
    begin
        if rising_edge(iCLK) then
            if pipe_v(0) = '1' then
                dsp0_breg1 <= ib_b.q;                       -- b
                dsp0_areg1 <= ib_a.i;                       -- A
                dsp0_dreg  <= ib_a.q;                       -- a
                dsp1_breg1 <= ib_a.i;                       -- A
                dsp1_areg1 <= ib_b.i;                       -- B
                dsp1_dreg  <= ib_b.q;                       -- b
                dsp2_breg1 <= ib_b.i;                       -- B
                dsp2_areg1 <= ib_a.i;                       -- A
                dsp2_dreg  <= ib_a.q;                       -- a
            end if;

            if pipe_v(1) = '1' then
                dsp0_breg2 <= dsp0_breg1;                   -- b
                dsp0_adreg <=                               -- A + a
                    resize(dsp0_areg1, c_ad_a_dw) +
                    resize(dsp0_dreg,  c_ad_a_dw);
                dsp1_breg2 <= dsp1_breg1;                   -- A
                dsp1_adreg <=                               -- B + b
                    resize(dsp1_areg1, c_ad_b_dw) +
                    resize(dsp1_dreg,  c_ad_b_dw);
                dsp2_breg2 <= dsp2_breg1;                   -- B
                dsp2_adreg <=                               -- a - A
                    resize(dsp2_dreg,  c_ad_a_dw) -
                    resize(dsp2_areg1, c_ad_a_dw);
            end if;

            if pipe_v(2) = '1' then
                dsp0_mreg <= dsp0_breg2 * dsp0_adreg;       -- b x (A + a)
                dsp1_mreg <= dsp1_breg2 * dsp1_adreg;       -- A x (B + b)
                dsp2_mreg <= dsp2_breg2 * dsp2_adreg;       -- B x (a - A)
            end if;

            if pipe_v(3) = '1' then
                dsp0_preg <=                                -- A x (B + b) - b x (A + a)
                    resize(dsp1_mreg, c_c_dw) -
                    resize(dsp0_mreg, c_c_dw);
                dsp2_preg <=                                -- A x (B + b) + B x (a - A)
                    resize(dsp1_mreg, c_c_dw) +
                    resize(dsp2_mreg, c_c_dw);
            end if;
        end if;
    end process;

Листинг 5 – Исходный код основного процесса «Схемы 2»

 

4. Синтез

Для синтеза будет использоваться Vivado версии 2020.2.

Vivado не позволяет синтезировать модули, порты которых имеют неограниченные поля, поэтому для синтеза мы напишем компонент – обертку:


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

library rtl_modem;
    use rtl_modem.pkg_rtl_modem_types.all;

entity complex_multiplier_wrap is
    generic(
          g_a_dw                    : integer := 25
        ; g_b_dw                    : integer := 18
        ; g_type                    : integer := 1
    );
    port(
          iCLK                      : in std_logic
        ; iV                        : in std_logic
        ; iA_I                      : in signed(g_a_dw - 1 downto 0)
        ; iA_Q                      : in signed(g_a_dw - 1 downto 0)
        ; iB_I                      : in signed(g_b_dw - 1 downto 0)
        ; iB_Q                      : in signed(g_b_dw - 1 downto 0)
        ; OV                        : out std_logic
        ; oC_I                      : out signed(g_a_dw + g_b_dw downto 0)
        ; oC_Q                      : out signed(g_a_dw + g_b_dw downto 0)
    );
end;

Листинг 6 – Обертка комплексного умножителя

Синтезируем наш модуль по «схеме 1» с разрядностями 25 х 18.

В логе синтеза мы увидим следующее сообщение:

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

Аналогично синтезируем наш модуль по «схеме 2» с разрядностями 17 х 17.

Лог для данного случая:

В данном случае, поскольку необходимо передать промежуточный результат с одного аппаратного блока на два других, используются порты PCIN и C, что снижает максимально возможную частоту обработки в сравнении со «схемой 1».

По итогу мы выяснили, что «схема 1» обладает преимуществами в разрядности входных данных и позволяет выжать максимум скорости из аппаратных блоков ПЛИС. Однако в проектах, где не требуется такая большая разрядность, «схема 2» позволяет экономить один DSP48 на каждую операцию комплексного умножения. Кроме того, максимальная рабочая частота чаще всего ограничена другими компонентами проекта, поэтому в этом плане разница между «схемами» несущественна.

 

Ссылки

 

1. https://github.com/vlotnik/rtl_modem - исходные коды, представленные в статье

2. https://www.xilinx.com/support/documentation/user_guides/ug193.pdf - гайд по использованию аппаратных блоков DSP48

3. https://www.xilinx.com/support/documentation/user_guides/ug479_7Series_DSP48E1.pdf - описание аппаратного блока DSP48E1

4. https://www.xilinx.com/support/documentation/user_guides/ug579-ultrascale-dsp.pdf - описание аппаратного блока DSP48E2

Всего комментариев : 11
avatar
0
1 KeisN13 • 13:47, 31.05.2021
Отличный туториал, ждем продолжения
avatar
2 elf2flash5955 • 13:52, 31.05.2021
А где цифры с получившейся частотой работы схем?
avatar
0
3 KeisN13 • 14:07, 31.05.2021
Предположу, что если это все действительно влазит в DSP секцию, то там вполне можно рассчитывать на 500+МГц
avatar
4 Amurak • 14:47, 31.05.2021
Там слишком много нюансов в определении цифр и это не очень интересно.
avatar
5 elf2flash5955 • 14:58, 31.05.2021
"По итогу мы выяснили, что «схема 1» ... позволяет выжать максимум скорости из аппаратных блоков ПЛИС..."
как выяснили то? Самый простой способ: задать высокую тактовую, ну пусть будет 700МГц, в файле с констрейнами, скомпилить, посмотреть отчет в виваде, какие слаки получились, не знаю как в виваде, в квартусе в отчете таймквеста есть fmax. Вот если так сделать, то можно написать - мы выяснили...
avatar
6 Amurak • 15:08, 31.05.2021
Более оптимальной схемы на этих ресурсах построить не получится. А первая схема чуть более оптимальна, чем вторая.
avatar
7 elf2flash5955 • 16:47, 31.05.2021
Чуть более оптимальная - по каким критериям? В статье написано, что латенси и той и той схемы - 4 такта, все влазит в ДСП ядро, значит максимальная частота каждой схемы будет определяться частотой работы ДСП ядра. Не пойму за счёт чего будет выжат максимум скорости
avatar
8 Amurak • 19:03, 31.05.2021
"В данном случае, поскольку необходимо передать промежуточный результат с одного аппаратного блока на два других, используются порты PCIN и C, что снижает максимально возможную частоту обработки в сравнении со «схемой 1»."

Порты PCIN-PCOUT дают максимально возможную цифру. Если используется еще C, то это уже минус время.
avatar
9 Strijar • 23:33, 31.05.2021
Спасибо! Интересно и жду продолжения
avatar
10 nesterovengineer • 04:19, 03.06.2021
Отличная статья, спасибо!

У Xilinx есть IP ядро комплексного умножителя. Я так понимаю, что оно сделано на тех же выкладках. С оптимизацией по ресурсам - 3 DSP блока, и по скорости - 4 DSP блока. И обе схемы работают на одинаковой максимальной частоте (544МГц для 7 серии, кто спрашивал выше).
Т.е. получается у них латентность разная для двух схем. Не оценивал готовые блоки?
avatar
11 Amurak • 13:06, 03.06.2021
Скорее всего оно там примерно в те же схемы и раскладывается, латентность в принципе можно крутить, если убирать/добавлять триггеры.
avatar

Последние статьи нашего сообщества

ЦОС

Реализация базовых компонентов ЦОС : Комплексный умножитель

Подробнее

Познавательное

Асинхронный и синхронный сброс: Методы проектирования - часть вторая (раздел 5)

Подробнее

Общее

Асинхронный и синхронный сброс: Методы проектирования - часть вторая (раздел 4)

Подробнее

Общее

Асинхронный и синхронный сброс: Методы проектирования - часть вторая (разделы 1, 2, 3)

Подробнее

Познавательное

Что нового в VHDL 2019?

Подробнее

Познавательное

Введение в EDA Playground

Подробнее

Xilinx FPGA

ZYNQ HW: EBAZ4205: часть 2

Подробнее

Xilinx FPGA

ZYNQ HW: EBAZ4205: Часть 1

Подробнее

SystemVerilog

UVM тест таблицы sin/cos

Подробнее

Познавательное

FPGA или микроконтроллер: что же выбрать?

Подробнее
Все статьи

Календарь актуальных событий и мероприятий

Вебинар (состоится )

Вебинар: Kria – новый подход к проектированию встраиваемых систем на ПЛИС Xilinx

Подробнее

Вебинар (состоится )

Очередной вебинар от Doulos по основам UVM

Подробнее

Мероприятия (состоится )

Онлайн-конференция Cadence. Проектирование электроники. 8-9 июня

Подробнее

Вебинар (состоится 18 мая 2021)

Вебинар: Внедрение ИИ в EDGE устройства на FPGA Xilinx

Подробнее

Мероприятия (состоится )

Онлайн-конференция для инженеров по встраиваемым системам

Подробнее

Вебинар (состоится 20 и 26 / 05 / 2021)

Проектирование RTL на Haskell/Clash

Подробнее

Мероприятия (состоится )

Конференция «Теоретические и прикладные аспекты разработки устройств на микроконтроллерах и ПЛИС»

Подробнее

Вебинар (состоится )

Выложены материалы вебинара по развертыванию сверточных нейронных сетей на основе ПЛИС Microchip

Подробнее

Вебинар (состоится )

SoM-модули Trenz Electronic для проектирования и производства устройств на ПЛИС Xilinx. Вебинар

Подробнее

Вебинар (состоится )

Вебинар о построении сверточных нейронных сетей на основе ПЛИС Microchip

Подробнее
Все предстоящие события

FPGA-Systems – это живое, постоянно обновляемое и растущее сообщество.
Хочешь быть в курсе всех новостей и актуальных событий в области?
Подпишись на рассылку