Русские Блоги
Подробное введение в Xilinx Vivado (3): Использование IP-сердечников
IP-ядро (IP-ядро)
Есть много IP-сердечников в Vivado использовать напрямую, например, математические операции (умножители, дивизии, операторы с плавающей точкой и т. Д.), Обработка сигналов (FFT, DFT, DDS и т. Д.). IP-ядро, похожее на библиотеку программирования (например, язык C printf() Функция), можно назвать напрямую, очень удобно ускорить скорость разработки.
Используйте Verilog, чтобы вызвать IP-ядро
Вот пример использования IP-ядра мультипликатора, используйте вызовы Verilog. Первый новый проект, новое строительство demo.v Верхний модуль.
Добавить IP-ядро
Щелчок Flow Navigator середина IP Catalog 。
Выбрать Math Functions Ниже Multiplier Множитель и двойной щелчок.
Диалоговое окно настройки параметров появится диалоговое окно IP-ядро. Нажмите на левый верхний угол Documentation Вы можете открыть это ядро IP, используя это ядро IP. Здесь входные сигналы A и B устанавливаются непосредственно на 4-битные не символические данные, а другие по умолчанию нажмите ОК.
Нажмите на окно, которое всплывает позже Generate 。
Вызов IP Core
Выбрать IP Sources Развернуть и выберите mult_gen_0 — Instantiation Template — mult_gen_0.veo Вы можете открыть файл шаблона инсалиции. Как показано на рисунке, этот код является примером кода для вызова этого IP-ядра с помощью Verilog.
Скопируйте образец код на demo.v В файл и изменено, в конечном итоге следующее. Код объявлен в 4-битных переменных A и B, соответственно назначающим начальные значения 7, 8, как использование умножителя; нет символической 8-битной переменной P, для сохранения результата расчета. CLK — цикл 20ns тактовой сигнал, написанный в Testbench; mult_gen_0 mul(. ) Заявление создается. mult_gen_0 Тип объекта модуля mul И включить CLK, A, B и P в качестве параметров.
- module demo(
- );
- reg clk = 0;
- always #10 clk =
Проверка поведенческого моделирования
Принимая демо в качестве верхнего модуля, начните симуляцию поведения, вы можете вывести форму волны. Установите, B, P Дисплей в виде беззнаковных десятичных (щелкните правой кнопкой мыши) Radix — Unsigned Decimal ). Как показано на рисунке, вы можете увидеть a=7, b=8 После того, как первые часы поднимаются p = a * b = 56 。
Коробка (блок-дизайн) называется IP-ядро
Здесь простой пример вызывается путем вызова MultiLier IP-сердечника, создавая новый модуль, который может рассчитать квадрат.
Создайте файл дизайна блок-диаграммы
Выбрать Flow Navigator середина Create Block Design Создайте файл дизайна блок-диаграммы.
Введите имя файла и нажмите OK 。
Добавить IP-ядро
Щелкните правой кнопкой мыши на поле, выберите Add IP 。
Вы можете найти напрямую, вы можете найти IP-ядро, дважды щелкните подтверждение.
IP-ядро может быть добавлено для подключения к другим устройствам с проводом.
Дважды щелкните этот символ ядра IP, вы можете открыть диалоговое окно «Настройки параметров». Нажмите на левый верх Documentation Вы можете просмотреть основное руководство IP. Здесь все из которых введены здесь до 4 — без знака, и другие значения по умолчанию щелкните OK подтверждать.
Схема рисования
Щелкните правой кнопкой мыши Diagram Окно пробел, выберите Create Port 。
Всплывающее окно, установите порт a Для 4-значного входного сигнала нажмите OK 。
будут a против A 、 B Они связаны.
Тот же метод, добавить 8-битный выходной порт p ,против P связь.
Добавьте еще один clk Порт ввода часов, с CLK связь.
Конечный результат как показано.
Тест моделирования
Щелкните правой кнопкой мыши на файле дизайна диаграммы коробки design_1 ,Выбрать Create HDL Wrapper 。
Выберите второй элемент и нажмите OK 。
Открытый design_1_wrapper.v Файл, как показано на рисунке, код в красном поле используется для вызова предыдущих окрашенных Block Design Модуль.
в design_1_wrapper.v В файле вы можете добавить код TestBench, чтобы сделать поведенческое моделирование. Код модификации выглядит следующим образом, дайте входной сигнал a Начальное значение 8 , clk Подключиться к тактовой сигналу TestBench c на.
- wire [3:0]a = 8;
- wire clk;
- wire [7:0]p;
- reg c = 0;
- always #10 c <=
в Simulation Sources Под папкой, установить design_1_wrapper.v Верхний файл (щелкните правой кнопкой мыши, выберите) для поведенческого моделирования Set as Top )。
Начните симуляцию поведения, форма волны окончательного выхода заключается в следующем. Это можно увидеть, clk После первого роста края есть p = a*a = 64 То есть реализовать квадратную операцию.
Генератор системных IP-ядер (CORE Generator & Architecture Wizard)
Теперь рассмотрим более мощные средства для создания сложных элементов принципиальной схемы ПЛИС с помощью встроенного в САПР Xilinx ISE 10.1.i – Генератор системных IP-ядер (CORE Generator & Architecture Wizard). Это очень необходимый и удобный инструмент для разработчика. Он содержит в себе специальные генерируемые библиотеки символов, оптимизированные под конкретное семейство ПЛИС. Можно пользоваться и стандартной библиотекой элементов и на их основе строить более сложные узлы разрабатываемой схемы, но бывают моменты, что разрабатываемая схема должна работать, а она не работает. Это происходит из-за того, что разработчик не учитывает реальные задержки распространения сигнала внутри ПЛИС при работе на высоких скоростях, что на практике бывает очень часто. Поэтому рано или поздно придется воспользоваться данным средством — IP (CORE Generator & Architecture Wizard) и надо набраться терпения, чтобы понять как правильно использовать данное решение в своих разработках.
Вызываем Помощник создания нового источника (New Source Wizard) -> IP (CORE Generator & Architecture Wizard) и задаем имя файла – IP_CORE:
Выбираем необходимое для проекта IP-ядро из целого перечня готовых IP-ядер и следуем инструкциям Помощника создания нового источника:
Соглашаемся с выведенным в диалоговом окне отчетом о проделанной работе и нажимаем кнопку Finish:
Запускается диалоговое окно Генератора системных IP-ядер для выбранного IP-ядра – Distributed Memory Generator:
Далее разработчику предоставляется выбрать все необходимые параметры, которые должен сгенерировать Генератор системных IP-ядер, в частности ширину данных (Data Width), количество данных (Depth – глубина), тип создаваемой памяти (Memory Type):
В следующем окне предлагается выбрать входные и выходные параметры и опции, назначение которых более подробно можно посмотреть в описании на данное IP-ядро, которое вызывается при нажатии на кнопку View Data Sheet. Для простоты пока не будем ничего менять и продолжим работу дальше, нажав на кнопку Next:
А вот следующее диалоговое окно рассмотрим обязательно:
Данное окно предназначено для задания специального файла коэффициентов с расширением *.coe, в котором задаются постоянные или временные начальные данные. Прежде чем начать заполнение данного окна надо с начала создать в любом простом редакторе (Блокнот) сам файл с расширением *.coe по определенному правилу, который определяет правильный формат написания подобного файла:
После того, как файл rom_ip_core.coe будет сформирован и сохранен в рабочем каталоге разрабатываемого проекта, его можно добавлять с помощью кнопки Browse…, которая вызывает диалоговое окно Open:
Таким образом, добавляется Файл коэффициентов (Coefficients File):
Для проверки или просмотра заданных коэффициентов служит кнопка Show…, которая вызывает диалоговое окно для просмотра данных коэффициентов:
Для завершения работы Генератора системных IP-ядер для генерирования нашего IP-ядра нажимаем на кнопку Finish. Обращаю внимание на то, что процесс генерации IP-ядра требует некоторого времени, и в окне Сообщений нужно дождаться надписи – Successfully generated IP_CORE:
В САПР Xilinx ISE 10.1i осталась не большая недоработка, которая приводит к тому, что не всегда можно сразу добавить созданное IP-ядро в схемотехническом редакторе, даже если попытаться обновить изображение схемы нажатием по рабочему полю схемы ПЛИС. Поэтому рекомендую закрывать схемотехнический редактор. Проверить наличие создания IP-ядра в окне Источников (Sources for: ….):
Снова войти в схемотехнический редактор, и добавить IP-ядро в разрабатываемую схему ПЛИС точно так же, как добавляли новый символ — SYNC:
При этом, в редакторе символа можно уменьшить размер и переместить название символа как показано ниже:
При правильной работе САПР Xilinx ISE10.1i при создании IP-ядра выводит диалоговое окно, в котором нужно нажать кнопку Update, далее кнопку OK:
Реализация целочисленного БПФ на ПЛИС
Однажды меня спросили заказчики, нет ли у меня в проектах целочисленного БПФ, на что я всегда отвечал, что это уже сделано другими в виде готовых, хоть и кривых, но бесплатных IP-ядер (Altera / Xilinx) – берите и пользуйтесь. Однако, эти ядра не оптимальны, обладают набором «особенностей» и требуют дальнейшей доработки. В связи с чем, уйдя в очередной плановый отпуск, который не хотелось провести бездарно, я занялся реализацией конфигурируемого ядра целочисленного БПФ.
КДПВ (процесс отдладки ошибки переполнения данных)
В статье я хочу рассказать, какими способами и средствами реализуются математические операции при вычислении быстрого преобразования Фурье в целочисленном формате на современных кристаллах ПЛИС. Основу любого БПФ представляет узел, который носит название «бабочка». В бабочке реализуются математические действия – сложение, умножение и вычитание. Именно о реализации «бабочки» и её законченных узлов будет идти рассказ в первую очередь. За основу взяты современные семейства ПЛИС фирмы Xilinx – это серия Ultrascale и Ultrascale+, а также затрагиваются старшие серии 6- (Virtex) и 7- (Artix, Kintex, Virtex). Более старшие серии в современных проектах – не представляют интереса в 2018 году. Цель статьи – раскрыть особенности реализации кастомных ядер цифровой обработки сигналов на примере БПФ.
Введение
Ни для кого не секрет, что алгоритмы взятия БПФ прочно вошли в жизнь инженеров цифровой обработки сигналов, а следовательно, этот интрумент нужен постоянно. У ведущих производителей FPGA, таких как Altera / Xilinx уже есть гибкие конфигурируемые ядра FFT / IFFT, однако они имеют ряд ограничений и особенностей, в связи с чем мне уже не раз приходилось пользоваться собственными наработками. Вот и в этот раз мне пришлось реализовать БПФ в целочисленном формате по схеме Radix-2 на ПЛИС. В своей прошлой статье я уже делал БПФ в формате с плавающей точкой, и оттуда вы знаете, что в для реализации БПФ применяется алгоритм с двухкратным параллелизмом, то есть ядро может обрабатывать два комплексных отсчета на одной частоте. Это ключевая особенность БПФ, которая отсуствует в готовых ядрах FFT Xilinx.
Пример: требуется разработать узел БПФ, осуществляющий непрерывную работу входного потока комплексных чисел на частоте 800 МГц. Ядро от Xilinx такое не потянет (реально достижимые тактовые частоты обработки в современных ПЛИС порядка 300-400 МГц), либо потребует каким-то образом децимировать входной поток. Кастомное ядро позволяет без предварительного вмешательства тактировать два входных отсчета на частоте 400 МГц вместо одного отсчета на 800 МГц. Другой минус ядра FFT Xilinx связан с невозможностью принимать входной поток в бит-реверсном порядке. В связи с чем тратится огромный ресурс памяти кристалла ПЛИС для перестановки данных в нормальный порядок. Для задач быстрой свертки сигналов, когда два узла БПФ стоят друг за другом, — это может стать критичным моментом, то есть задача просто не ляжет в выбранный кристалл ПЛИС. Кастомное ядро БПФ позволяет принимать на входе данные в нормальном порядке, а выдавать – в бит-реверсном, а ядро обратного БПФ – наоборот, получает данные в бит-реверсном порядке, а выдает в нормальном. Экономится сразу два буфера на перестановку данных.
Поскольку большая часть материала этой статьи могла пересекаться с предыдущей, я решил сосредоточиться на раскрытии темы математических операции в целочисленном формате на FPGA для реализации БПФ.
Параметры ядра БПФ
- NFFT – количество бабочек (длина БПФ),
- DATA_WIDTH – разрядность входных данных (4-32),
- TWDL_WIDTH – разрядность поворачивающих множителей (8-27).
- SERIES – задает семейство ПЛИС, на которой реализуется БПФ (“NEW” – Ultrascale, “OLD” – 6/7 серия ПЛИС Xilinx).
Как и любые другие звенья в цепи, БПФ имеет входные порты управления – тактовый сигнал и сброс, а также входные и выходные порты данных. Кроме того, в ядре используется сигнал USE_FLY, который позволяет динамически выключать бабочки БПФ для процессов отладки или возжности посмотреть исходный входной поток.
В таблице ниже приведен объем занимаемых ресурсов FPGA в зависимости от длины БПФ NFFT для DATA_WIDTH = 16 и двух разрядностей TWDL_WIDTH = 16 и 24 бит.
Ядро при NFFT = 64K стабильно работает на частоте обработки FREQ = 375 MHz на кристалле Kintex-7 (410T).
Структура проекта
Схематичный граф узла БПФ показан на следующем рисунке:
- Ядра БПФ:
- int_fftNk – узел БПФ, схема Radix-2, децимация по частоте (DIF), входной поток – нормальный, выходной – бит-реверсный.
- int_ifftNk – узел ОБПФ, схема Radix-2, децимация по времени (DIT), входной поток – бит-реверсный, выходной – нормальный.
- int_dif2_fly – бабочка Radix-2, децимация по частоте,
- int_dit2_fly – бабочка Radix-2, децимация по времени,
- int_cmult_dsp48 – общий конфигурируемый умножитель, включает в себя:
- int_cmult18x25_dsp48 – умножитель для небольших разрядностей данных и поворачивающих множителей,
- int_cmult_dbl18_dsp48 – удвоенный умножитель, разрядность поворачивающих множителей до 18 бит,
- int_cmult_dbl35_dsp48 – удвоенный умножитель, разрядность поворачивающих множителей до 25* бит,
- int_cmult_trpl18_dsp48 – утроенный умножитель, разрядность поворачивающих множителей до 18 бит,
- int_cmult_trpl52_dsp48 – утроенный умножитель, разрядность поворачивающих множителей до 25* бит,
- mlt42x18_dsp48e1 – умножитель с разрядностями операндов до 42 и 18 бит на базе DSP48E1,
- mlt59x18_dsp48e1 – умножитель с разрядностями операндов до 59 и 18 бит на базе DSP48E1,
- mlt35x25_dsp48e1– умножитель с разрядностями операндов до 35 и 25 бит на базе DSP48E1,
- mlt52x25_dsp48e1– умножитель с разрядностями операндов до 52 и 25 бит на базе DSP48E1,
- mlt44x18_dsp48e2 – умножитель с разрядностями операндов до 44 и 18 бит на базе DSP48E2,
- mlt61x18_dsp48e2 – умножитель с разрядностями операндов до 61 и 18 бит на базе DSP48E2,
- mlt35x27_dsp48e2 – умножитель с разрядностями операндов до 35 и 27 бит на базе DSP48E2,
- mlt52x27_dsp48e2– умножитель с разрядностями операндов до 52 и 27 бит на базе DSP48E2.
- int_addsub_dsp48 – универсальный сумматор, разрядности операндов до 96 бит.
- int_delay_line – базовая линия задержки, обеспечивает перестановку данных между бабочками,
- int_align_fft – выравнивание входных данных и поворачивающих множителей на входе бабочки БПФ,
- int_align_fft – выравнивание входных данных и поворачивающих множителей на входе бабочки ОБПФ,
- rom_twiddle_int – генератор поворачивающих множителей, с определенной длины БПФ считает коэффициенты на базе DSP ячеек ПЛИС,
- row_twiddle_tay – генератор поворачивающих множителей с помощью ряда Тейлора (NFFT > 2K)**.
- inbuf_half_path – входной буфер, принимает поток в обычном режиме и выдает две последовательности отсчетов, сдвинутых на половину длины БПФ***,
- outbuf_half_path – выходной буфер, собирает две последовательности и выдает одну непрерывную равную длине БПФ,
- iobuf_flow_int2 – буфер работает в двух режимах: принимает поток в режиме Interleave-2 и выдает две последовательности сдвинутых на половину длины БПФ. Либо наоборот, в зависимости от опции «BITREV».
- int_bitrev_ord – простой преобразователь данных из натурального порядка в бит-реверсный.
Все, кто хотя бы раз сталкивался с алгоритмом быстрого преобразования Фурье, знают, что в основе этого алгоритма лежит элементарная операция – «бабочка». Она преобразует входной поток, умножая входные данные на поворачивающие коэффициенты (twiddle factor). Для БПФ есть две классические схемы преобразования – децимация по частоте (DIF, decimation-in-frequency) и децимация по времени (DIT, decimation-in-time). Для DIT алгоритма характерно разбиение входной последовательности на две последовательности половинной длительности, а для DIF алгоритма – на две последовательности четных и нечетных отсчетов длительностью NFFT. Кроме того, эти алгоритмы отличаются математическими действиями для операции «бабочка».
A, B – входные пары комплексных отсчетов,
X, Y – выходные пары комплексных отсчетов,
W – комплексные поворачивающие множители.Поскольку входные данные – комплексные величины, то бабочка требует один комплексный умножитель (4 операции умножения и 2 операции сложения) и два комплексных сумматора (4 операции сложения). Это и есть весь математический базис, который необходимо реализовать на ПЛИС.
Умножитель
Следует отметить, что все математические операции на FPGA зачастую выполняются в дополнительном коде (2’s complement). Умножитель на ПЛИС можно реализовать двумя способами — на логике, используя триггеры и таблицы LUT, или на специальных блоках вычисления DSP48, которые давно и прочно вошли в состав всех современных ПЛИС. На логических блоках умножение реализуется с помощью алгоритма Бута или его модификаций, занимает приличный объем логических ресурсов и далеко не всегда удовлетворяет временным ограничениям на высоких частотах обработки данных. В связи с этим, умножители на ПЛИС в современных проектах практически всегда проектируются на базе DSP48 узлов и лишь изредка – на логике. Узел DSP48 – это сложная законченная ячейка, которая реализует математические и логические функции. Основные операции: умножение, сложение, вычитание, накопление, счетчик, логические операции (XOR, NAND, AND, OR, NOR), возведение в квадрат, сравнение чисел, сдвиг и т.д. На следующем рисунке представлена ячейка DSP48Е2 для семейства ПЛИС Xilinx Ultrascale+.
Путем нехитрой конфигурации входных портов, операций вычисления в узлах и выставления задержек внутри узла – можно сделать скоростную математическую молотилку данных.
Отметим, у всех топовых вендоров FPGA в среде проектирования есть стандартные и бесплатные IP-ядра для вычисления математических функций на базе узла DSP48. Они позволяют вычислять примитивные математичекие функции и выставлять различные задержки на входе и выходе узла. У Xilinx это IP-Core «multiplier» (ver. 12.0, 2018), которое позволяет конфигурировать умножитель на любую разрядность входных данных от 2 до 64 бит. Кроме того, можно задать способ реализации умножителя – на логических ресурсах или встроенных примитивах DSP48.Оцените, сколько логики «кушает» умножитель с разрядностью входных данных на портах A и B = 64 бит. Если использовать узлы DSP48, то их потребуется всего 16.
Основное ограничение, накладываемое на ячейки DSP48 — разрядность входных данных. В узле DSP48E1, который является базовой ячейкой ПЛИС Xilinx 6 и 7 серии разрядность портов входа для умножения: «A» — 25 бит, «B» — 18 – бит, Следовательно, результат умножения представляет 43-битное число. Для семейства ПЛИС Xilinx Ultrascale и Ultrascale+ узел претерпел несколько изменений, в частности разрядность первого порта выросла на два бита: «A» — 27 бит, «B» — 18 — бит. Кроме того, сам узел называется DSP48E2.
Дабы не иметь привязку к конкретному семейству и кристаллу FPGA, обеспечить «чистоту исходного кода», и учесть все возможные разрядности входных данных, решено спроектировать собственный набор умножителей. Это позволит максимально эффективно реализовать комплексные умножители для «бабочек» БПФ, а именно — умножители и сумматор-вычитатель на базе DSP48 блоков. Первый вход умножителя — это входные данные, второй вход умножителя — поворачивающие множители (гармонический сигнал из памяти). Набор умножителей реализуется с помощью встроенной библиотеки UNISIM, из которой необходимо подключить примитивы DSP48E1 и DSP48E2 для дальнейшего их использования в проекте. Набор умножителей представлен в таблице. Следует отметить, что:
- Операция умножения чисел приводит к росту разрядности произведения как сумма разрядностей операндов.
- Каждый из умножителей 25×18 и 27×18 дублируются, по сути – это один компонент для разных семейств.
- Чем больше стадия параллельности операции – тем больше задержка на вычисление и больше объем занимаемых ресурсов.
- При меньшей разрядности на входе «B» можно реализовать умножители с бОльшей разрядностью на другом входе.
- Основное ограничение в наращивании разрядности вносит порт «B» (реальный порт примитива DSP48) и внутренний сдвиговый регистр на 17-бит.
Дальнейшее увеличение разрядности не представляет интереса в рамках поставленной задачи по причинам, описанным ниже:
Разрядность поворачивающих множителей
Известно, что чем больше разрядность гармонического сигнала – тем точнее представляется число (больше знаков в дробной части). Но рзрядность порта B < 25 бит в связи с тем, что для поворачивающих множителей в узлах БПФ этой разрядности вполне достаточно для обеспечения качественного перемножения входного потока с элементами гармонического сигнала в «бабочках» (для любых реально достижимых длин БПФ на современных ПЛИС). Типовое значение разрядности поворачивающих коэффициентов в реализуемых мною задачах равно 16 бит, 24 – реже, 32 – никогда.
Разрядность входных отсчетов
Разрядность данных типовых узлов приема и регистрации (АЦП, ЦАП) не велика – от 8 до 16 бит, и редко – 24 или 32 бита. Причем, в последнем случае эффективнее воспользоваться форматом данных с плавающей точкой по стандарту IEEE-754. С другой стороны, каждая стадия «бабочки» в БПФ прибавляет один разряд данных к выходным отсчетам из-за выполнения математических операций. Например, для длины NFFT = 1024 используется log2(NFFT) = 10 бабочек.
Следовательно, выходная разрядность будет на десять бит больше входной, WOUT = WIN + 10. В общем виде формула выглядит так:
WOUT = WIN + log2(NFFT);
Разрядность входного потока WIN = 32 бит,
Разрядность поворачивающих множителей TWD = 27,
Разрядность порта «А» из списка реализованных умножителей в данной статье не превышает 52 бита. Это означает, что максимальное количество бабочек БПФ = 52-32 = 20. То есть возможно реализовать БПФ длиной до 2^20 = 1М отсчетов. (Однако, на практике, прямыми средствами такое невозможно в связи с ограниченностью ресурсов даже у самых мощных кристаллов ПЛИС, но это относится к другой теме и в статье рассматриваться не будет).Как видно, это одна из главных причин, по которой я не стал реализовывать умножители с бОльшей разрядностью входных портов. Используемые умножители покрывают полный диапазон требуемых значений разрядности входных данных и поворачивающих множителей для задачи вычисления целочисленного БПФ. Во всех остальных случаях можно воспользоваться вычислением БПФ в формате с плавающей точкой!
Реализация «широкого» умножителя
На базе простого примера умножения двух чисел, не вписывающихся в разрядность стандартного узла DSP48, я покажу, как можно реализовать широкий умножитель данных. На следующем рисунке представлена его структурная схема. Умножитель реализует перемножение двух знаковых чисел в дополнительном коде, разрядность первого операнда X – 42 бита, второго Y – 18 бит. Она содержит два узла DSP48E2. Для выравнивания задержек в верхнем узле используется два регистра. Это сделано по той причине, что в верхнем сумматоре нужно правильно сложить числа из верхнего и нижнего узлов DSP48. Нижний сумматор фактически не используется. На выходе нижнего узла стоит дополнительная задержка произведения для выравнивания выходного числа по времени. Суммарная задержка – 4 такта.
Произведение складывается из двух компонент:
- Младшая часть: P1 = ‘0’ & X[16:0] * Y[17:0];
- Старшая часть: P2 = X[42:17] * Y[17:0] + (P1 >> 17);
Сумматор
Аналогично умножителю, сумматор может строиться на логических ресурсах, используя цепочку переноса, или на DSP48 блоках. Для достижения максимальной пропускной способности второй метод предпочтительнее. Один примитив DSP48 позволяет реализовать операцию сложения до 48 бит, два узла – до 96 бит. Для реализуемой задачи таких разрядностей вполне достаточно. Кроме того, примитив DSP48 обладает специальным режимом «SIMD MODE», который распараллеливает встроенный 48-битный АЛУ на несколько операций с разными данными небольшой разрядности. То есть, в режиме «ONE» используется полная разрядная сетка в 48 бит и два операнда, а в режиме «TWO» разрядная сетка разбивается на несколько параллельных потоков по 24 бита каждый (4 операнда). Этот режим, используя всего один сумматор, помогает сократить количество занимаемых ресурсов кристалла ПЛИС на небольших разрядностях входных отсчетов (на первых стадиях вычисления).
Рост разрядности данных
Операция умножения двух чисел с разрядностями N и M в двоичном дополнительном коде приводит к росту выходной разрядности до Р = N + M.
Пример: для перемножения трехбитовых чисел N = M = 3, максимальное положительное число равно +3 = (011)2, а максимально отрицательное число – 4 = (100)2. Старший бит отвечает за знак числа. Следовательно, максимально возможное число при умножении – это +16 = (010000)2, которое образуется в результате умножения двух максимально отрицательных чисел -4. Разрядность выходных данных равна сумме входных разрядностей Р = N+M = 6 бит.
Операция сложения двух чисел с разрядностями N и M в двоичном дополнительном коде приводит к росту выходной разрядности на один бит.
Пример: сложим два положительных числа, N = M = 3, максимальное положительное число равно 3 = (011)2, а максимальное отрицательное число – 4 = (100)2. Старший бит отвечает за знак числа. Следовательно, максимальное положительное число – это 6 = (0110)2, а максимальное отрицательное число – это -8 = (1000)2. Разрядность выходных данных увеличивается на один бит.
Учет особенностей алгоритма
0.003% и полностью приемлимо для реализации задачи.
Убрав в произведении двух чисел минимальное число, на каждой итерации можно сократить один неиспользуемый старший разряд. Пример: данные – 4 = (100)2, коэффициент +3 = (011)2. Результат умножения = -12 = (110100)2. Пятый бит можно отбросить, т.к. он дублирует соседний, четвертый — знаковый бит.
Усечение разрядности снизу:
Очевидно, что умножая в «бабочке» входной сигнал на гармоническое воздействие, не требуется тянуть выходную разрядность в следующие бабочки, а требуется округление или усечение. Поворачивающие множители представлены в удобном M-битном формате, однако на деле это обычный синус и косинус, нормированные к единице. То есть число 0x8000 = -1, а число 0x7FFF = +1. Следовательно, результат умножения обязательно усекается до исходной разрядности данных (то есть M бит от поворачивающих множителей усекаются снизу). Во всех реализациях БПФ, что мне довелось видеть, поворачивающие множители нормированы к 1 теми или иными способами. Следовательно, из результата умножения необходимо взять биты в следующей сетке [N+M-1-1: M-1]. Старший бит не используется (вычетаем дополнительную 1), младшие – усекаются.
Сложение/вычитание данных в операциях «бабочки» никак не подвергается минимизации и только эта операция вносит вклад в рост разрядности выходных данных на один бит на каждой стадии вычисления.
Отметим, что на первой стадии алгоритма FFT DIT или на последней стадии алгоритма FFT DIF данные необходимо умножить на поворачивающий множитель с нулевым индексом W0 =
= . В связи с тем, что умножение на единицу и ноль – примитивные операции, их можно не исполнять. В таком случае операция комплексного умножения вообще не требуется: вещественная и мнимая компоненты проходят «поворот» без изменений. На второй стадии используется два коэффициента: W0 = = и W1 = = . Аналогично, операции можно свести к элементарным преобразованиям и использовать мультплексор для выбора выходного отсчета. Это позволяет существенно сэкономить DSP48 блоки на первых двух бабочках. Комплексный умножитель строится аналогично – на базе умножителей и сумматора-вычитателя, однако для некоторых вариантов разрядности входных данных дополнительные ресурсы не потребуются, о чем будет рассказано ниже.
Входной буфер и линии задержки и кросс-коммутаторы аналогичны тем, что описаны в предыдущей статье. Поворачивающие множители стали целочисленными с конфигурируемой разрядностью. В остальном — глобальных изменений в проектировании ядра БПФ — нет.
Особенности ядра БПФ INT_FFTK
- Полностью конвейерная схема обработки данных.
- Длина преобразования NFFT = 8-512K точек.
- Гибкая настройка длины преобразования NFFT.
- Целочисленный формат входных данных, разрядность конфигурируется.
- Целочисленный формат поворачивающих множителей, разрядность конфигурируется.
- Компактное хранение поворачивающих коэффициентов через разложение в ряд Тейлора до первой производной.
- Рост разрядности на каждой стадии вычисления и на выходе ядра!
- Хранение четверти периода коэффициентов для экономии ресурсов кристалла.
- БПФ: данные на входе – в прямом порядке, на выходе в двоично-инверсном.
- ОБПФ: данные на входе в двоично-инверсном порядке, на выходе – в прямом.
- Конвейерная схема обработки с двухкратным параллелизмом. Radix-2.
- Минимальная неразрывная длина пачки данных равна NFFT отсчетов*.
- Высокая скорость вычислений и небольшой объем занимаемых ресурсов.
- Реализация на последних кристаллах ПЛИС (Virtex-6, 7-Series, Ultrascale).
- Частота работы ядра
Исходный код
Исходный код ядра БПФ INTFFTK на VHDL (включая базовые операции и набор умножителей) и m-скрипты для Matlab / Octave доступны в моем профиле на гитхабе.
Заключение
В ходе разработки спроектировано новое ядро БПФ, обеспечивающее большую производительность по сравнению с аналогами. Связка ядер БПФ и ОБПФ не требует перевода в натуральный порядок, а максимальная длина преобразования ограничена лишь ресурсами ПЛИС. Двукратный параллелизм позволяет обрабатывать входные потоки удвоенной частоты, чего не может делать IP-CORE Xilinx. Разрядность на выходе целочисленого БПФ линейно растет в зависимости от количества стадий преобразовани.
В прошлой статье я писал о дальнейших планах: ядра БПФ Radix-4, Radix-8, Ultra-Long FFT на много миллионов точек, FFT-FP32 (в формате IEEE-754). Если коротко, то они практически все разрешены, но по тем или иным причинам в настоящий момент не могут быть вынесены на публику. Исключение составляет алгоритм FFT Radix-8, к которому я даже не присупал (сложно и лень).
В очередной раз выражаю благодарность dsmv2014, который всегда приветствовал мои авантюрные идеи. Спасибо за внимание!
UPDATE 2018/08/22
В исходные коды добавил вариант SCALED FFT / IFFT. На каждой бабочке производится усечение разрядности на 1 бит (truncate LSB). Выходная разрядность данных = входной разрядности.
В дополнение приведу два графика прохождения реального сигнала через ПЛИС, чтобы показать свойство интегральности преобразования, то есть: как влияет усечение на результат накапливания ошибки на выходе БПФ. Из теории известно, что в результате преобразования Фурье ухудшается отношение сигнал-шум при усечении входного сигнала относительно варианта без усечения.
Пример: Размах входной амплитуды — 6 бит. Сигнал — синусоида на 128 отсчете ПФ. NFFT = 1024 отсчетов, DATA_WIDTH = 16, TWDL_WIDTH = 16.
Рис. 1 Отношение сигнал-шум слабое:
IP Cores For FPGA Designs
Intellectual property (IP) cores are standalone modules that can be used in any field programmable gate array (FPGA). These are developed using HDL languages like VHDL, Verilog and System Verilog, or HLS like C.
IP cores are part of the growing electronic design automation (EDA) industry. In this article, these will be discussed with respect to SRAM based FPGAs.
Let us take an example of universal asynchronous receiver-transmitter (UART) IP block, which is intended to be used in different applicatons. The developed UART IP core module should:
• Meet basic UART functionalities
• Be portable, so that it can be used in any vendor technologies; for example, Xilinx/Altera as plug and play
• Have user-configurable parameters (like baud rate)
• Have processor interface/generic parameter file to modify configurations as required
• Provide IP data sheetFig. 1: Xilinx Vivado – FPGA selection overview includes hard blocks
Types of IP cores
IP cores can be categorised as hard IP core, firm IP (semi-hard IP) core and soft IP core.
Hard IP cores.
These are part of the FPGA-independent modules; for example, PCIe or Ethernet IP modules available in Xilinx FPGA. You have to configure the location and provide interface connectivity with other modules, clocks and resets. Since these blocks are already part of the FPGA device, these will not be taken into account while calculating the utilisation of the slice logic report. In the utilisation summary, these will be counted as the number of PCIe/Ethernet blocks used. Because of a fixed location in the FPGA, these cores cannot be ported to other FPGAs. Neither can these be reused like HDL components, if already used in the FPGA.
Fig. 1 shows Xilinx Vivado tool – FPGA selection window for Virtex-7 FPGA with internal hard IP details for creating the project. The number of hard IPs may vary between different FPGA families.
Fig 2. shows the dedicated location of the hard IP in Virtex-7 FPGA.
Unmarked (not labelled) areas (between DSP slice and block RAM, or block RAM and PCIe, or PCIe and transceivers) in the FPGA (Fig. 2) contain large distribution of flip-flops, latches, multiplexers, LUTs, etc. Soft IP cores or custom logic are implemented in these areas.
Fig. 2: Xilinx Vivado – Virtex-7 FPGA hard IP core locations
Advantages include:
• Timing violations minimised
• No extra cost, that is, cost of the hard block is included in the FPGA; hence, can be considered as low-cost compared to other two types of IP cores
• No individual licence, except for compiler tool licence
• RTL code maintenance reduced
• No extra documentation required; for each IP level, documentation provided by vendor
• For slice/LUT summary, hard IP cores not considered
• Low dynamic and static power can be achieved if hard IP blocks are used in the designs
• Functionality and performance guaranteed
• Fully-tested and with known errors/limitations, if any, as per documents provided by vendorDisadvantages include:
• Not portable; these are highly optimised and targeted at specific FPGAs
• Fixed implementation with pre-defined constraints
• Limited availability of number of hard IPs with respect to particular FPGAsFirm IP cores.
Firm IP cores are also known as semi-hard IP cores. These are a form of gate-level netlist, where you have the flexibility to place the module in the FPGA as per usage and with minimal user-programmable configurations.
For example, if a third-party IP is targeted at Xilinx FPGA, then the IP provided will be .ngc file. You can integrate this file with your project and instantiate it as a component in the top level to interconnect with other modules, and then proceed with synthesis.
To use a firm IP core, you should have proper FPGA resource planning and requirement specifications before procuring the IP for the project.
Xilinx Coregen-generated IP cores (like FIFO, shift registers and memory interface cores) can be grouped into the firm IP core category. You have to include .ngc/.xco in the project directory (for Xilinx), and specify the instantiation in the top file. Instantiated components can be moved around within the FPGA to meet performance and timing.
Advantages are:
• Modifications allowed to some extent
• Functionality and performance are measurable
• Resource utilisation considers firm IP logic area
• Completely tested
• Documentation may be available up to some levelDisadvantages are:
• Limited portability
• Modifications to source not possible
• May be licensed based on cost
• Source utilisation matters while considering the FPGA
• Timing/performance may have impactSoft IP cores.
These are completely flexible and do not depend on vendor technology. These can be ported across various FPGA platforms. The IPs are developed using HDL languages and you are provided with source codes, so the IPs can be modified according to your application and easily integrated with your modules. These are reusable and can be targeted at many variants of FPGAs.
In Xilinx FPGAs, ARM, Zynq and PowerPC processors fall under hard IP category, whereas Microblaze falls in the soft IP group.
Similarly, in Altera, ARM, Intel ATOM processors come under hard IP core, and Nios-II processor under soft IP core.
Examples for other soft IP cores are 8051 microcontrollers IP, I2C controllers, SPI controllers, standard bus interfaces or any open core whose source can be modified.