Русский
Русский
English
Статистика
Реклама

Zynq

Начинаем работу с Zynq 7000. Пособие для начинающих

29.05.2021 18:19:27 | Автор: admin

Совсем недавно мне в руки попался один из вариантов отладочной платы с SoC Zynq XC7Z020. Поискав в Интернете материалы, а-ля how-to, и попробовав накидать свой минимальный проект обнаружил, что есть целый ряд подводных камней. Именно об этом я и хотел бы рассказать в статье. Кому интересно - добро пожаловать под кат.

Важно! Перед началом повествования, хотелось бы заранее оговориться, что основная цель которую я преследую при написании этой статьи - показать любителям, с чего можно начать, при изучении отладочных плат на базе Zynq. Я не являюсь профессиональным разработчиком под ПЛИС и SoC Zynq и могу допускать какие-либо ошибки в использовании терминологии, использовать не самые оптимальные пути решения задач, etc. Но любая конструктивная и аргументированная критика только приветствуется. Что ж, поехали

Что за отладка такая? Покажи-расскажи...

Мне очень давно хотелось поиграться с SoC Zynq, но никак не доходили руки. Но в очередной раз погуглив - увидел, что за вполне вменяемый ценник продаётся отладка с Zynq на борту, от компании QMTech, называется она Bajie Board. Выпускается отладка в нескольких вариантах с разными вариантами SoC Zynq. Я выбрал для себя вариант на XC7Z020 и тут же ее заказал, через пару недель она у меня уже была в руках.

После распаковки я был приятно удивлен, комплект поставки порадовал. Это была сама отладочная плата, блок питания на 5В/2А, mini-USB кабель и microSD Flash-карта SanDisk на 16Гб с уже залитым на нее Linux. То есть, сразу после получения вы можете подключить к плате питание, воткнуть USB-шнурок, открыть Putty и получить в свое распоряжение полноценный mini-компьютер с Embedded Linux. О Linux для Zynq, я думаю, расскажу в другой статье, поэтому едем дальше...

Итак, рассматривая плату и попутно документацию на нее я увидел относительно не плохой набор всякого-разного:

  • SoC: XC7Z020-1CLG400C

  • (datasheet:https://www.xilinx.com/support/documentation/data_sheets/ds190-Zynq-7000-Overview.pdf);

  • Осциллятор на 33,333 МГц;

  • Оперативная память DDR3 на 512 Мб от компании Micron, MT41K256M16TW-107:P;

  • Встроенный слот micro SD;

  • Источник питания для FPGA TPS563201 с широким диапазоном входных напряжений (от 4.5V до 17V, 3A);

  • Один 50-пиновый и две Digilent PMOD совместимых, гребёнки с пинами, с шагом в 2,54 мм. для пользовательских кейсов (как заверяет производитель, все проводники до пинов выровнены по длине);

  • Кнопка для логического сброса процессорной системы (PS);

  • Гигабитный RGMII Ethernet-контроллер Realtek RTL8211E-VL, подключенный к PS;

  • Два пользовательских светодиода, один подключен к программируемой логике (PL) и другой подключен к процессорной системе (PS);

  • Встроенный HDMI-совместимый интерфейс дисплея TI TPD12S016;

  • Гребёнка для подключения JTAG-отладчика;

Для большинства задач начального уровня такого количества всего будет прям за глаза.

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

Установка необходимого набора ПО для разработки

Итак, прежде чем начать работу с платой мне было необходимо установить ПО Xilinx Vivado и Xilinx SDK. Насколько я понимаю, грубо говоря, Vivado используется для конфигурации аппаратной части используемой платы и для работы с программируемой логикой. А Xilinx SDK (ныне именуется Vitis) используется для создания кода непосредственно для процессорной системы.

Поскольку бОльшая часть примеров из документации и репозитория производителя и разнообразных примеров из роликов на YouTube делались в версии Vivado 2019.1 (видимо из-за того, что это последняя версия поддерживающая работу с Xilinx SDK) - я установил именно её, а не последнюю доступную 2020.2.

Все программные продукты необходимые для работы с Xilinx Zynq - можно взять на официальном сайте Xilinx, тут. Сразу же спешу обратить внимание, что те из вас, кто захочет установить самую новую версию Vivado - нужно скачивать версию 2020.2, а не 2020.3 т.к. последняя поддерживает только Versal SoC, и не поддерживает Zynq.

В моём случае, т.к. я работаю в операционной системе Linux - я перешел в меню Vivado Archive - 2019.1 и нажал на кнопку скачивания по ссылке Vivado HLx 2019.1: WebPACK and Editions - Linux Self Extracting Web Installer в списке Vivado Design Suite - HLx Editions - 2019.1. Для пользователей Windows - выбирайте Windows Self Extracting Web Installer.

После скачивания открываем инсталлятор, установив права на исполнение:

chmod +x ~/Downloads/Xilinx_Vivado_SDK_Web_2019.1_0524_1430_Lin64.bin~/Downloads/Xilinx_Vivado_SDK_Web_2019.1_0524_1430_Lin64.bin 

Вся установка состоит из набора стандартных шагов.

  1. Вводим авторизационные данные, которые мы указывали при регистрации;

  2. Принимаем условия лицензионных соглашений;

  3. Выбираем Vivado HL WebPACK;

  4. Удостоверяемся в том, что выбран SoC Zynq в списке предложенного оборудования.

  5. Далее программа скачает порядка 16Гб всякого-разного, установит это и на Рабочем столе появятся иконки нужных нам приложений.

После установки Vivado необходимо установить драйвер для JTAG-программатора. В Linux это делается так:

cd Xilinx2019.1/Vivado/2019.1/data/xicom/cable_drivers/lin64/install_script/install_drivers/sudo ./install_drivers 

Подключаем все 6 пинов от JTAG-программатора в соответствии с шелкографией на плате. И проверяем установлены ли драйвера и определяется ли наша отладочная плата:

cd ~/Xilinx2019.1/Vivado/2019.1/bin./xsdb xsdb% connect -host localhost   xsdb% jtag targets                                                                                                                                                             1  Platform Cable USB 13724327082b01     2  arm_dap (idcode 4ba00477 irlen 4)     3  xc7z020 (idcode 23727093 irlen 6 fpga)

На этом подготовительных этап можно считать завершенным.

Hello, world или Баяны подъехали

Не будем отходить от традиции и попробуем поморгать LED-иком который подключен к программируемой логике.

Запускаем Vivado и создаем новый проект. Нажимаем File - Project - New

Откроется мастер создания нового проекта, нажимаем Next > и пишем название нашего проекта PL-Blink.

Выбираем RTL Project и ставим галочку у пункта Do not specify sources at this time.

Далее в списке ищем наш процессор xc7z020clg400-1.

Жмём на кнопку Finish.

Перед нами открывается главное окно программы Vivado и мы можем приступать к реализации намеченной нами цели!

Первым делом, мы добавим необходимые указания программе, на то, какие физические ножки мы задействуем в нашей моргающей вакханалии =)

Находим меню Sources и нажимаем кнопку Add Sources.

Откроется мастер импорта и нам нужно выбрать Add or create constraints.

В следующем меню нажимаем Create file и пишем название нашему файлу physical_constr. Именно в этом файле мы опишем какие ножки и в каком режиме должны работать.

Нажимаем кнопку Finish и в дереве Sourсes ищем только что созданный нами файл и открываем его:

Обратимся к схеме, которую любезно предоставил нам производитель и найдем какая ножка отвечает за тактирование, а какая за наш светодиод.Бегло поискав, я отметил для себя, что из Ethernet-контроллера RTL8211E-VL выведен опорный тактовый сигнал с его внутреннего PLL, частотой в 125МГц и заведен в ножку H16 (IO_L13P_T2_MRCC_35).Так почему бы нам его и не задействовать в нашем примере? =)

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

Тут же, рядом, на ножке H17 (IO_L13N_T2_MRCC_35) расположен светодиод, которым мы будем моргать.

Итак. Открыв наш constraints-файл запишем в него следующие строки:

# User LED and Clockset_property IOSTANDARD LVCMOS33 [get_ports led_h17_d4]set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]set_property PACKAGE_PIN H17 [get_ports led_h17_d4]set_property PACKAGE_PIN H16 [get_ports sys_clk]

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

В квадратных скобках, после get_ports необходимо указать логическое имя ножки (на ваше усмотрение). Имена стоит придумать осмысленно, потому что мы его будем использовать в Verilog-коде.

Кстати, подробнее о Physical Constraints можно почитать тут в главе 8.

Добавим в наш проект таким же образом Design Source. Находим меню Sources и нажимаем кнопку Add Sources.

Откроется мастер импорта и нам нужно выбрать Add or create design sources. Далее нажимаем Create File, смотрим, что выбран язык Verilog. Нажимаем ОК и Finish.

В следующем меню всё оставляем без изменений и нажимаем ОК и Yes.

Открываем созданный файл и видим небольшую заготовку:

Здесь вместо предложенного кода пишем наш Verilog-код и прокомментируем что значит каждая из строк:

// Директива компилятора, которая определяет единицу времени и точность для моделирования Verilog.// В целом, не очень интересный пункт для нас.`timescale 1ns / 1ps // Определяем стандартный блок-модуль (как класс в С++)module pl_blink(input sys_clk, output led_h17_d4);    // Задаем регистр для хранения записи о текущем состоянии светодиодаreg r_led; // Задаем регистр для хранения значения счётчика, использующегося в задержкеreg [31:0] counter;// Тут мы задаем действия которые должны быть выполнены при старте программыinitial begin    counter <= 32'b0;//  Обнуляем счётчик    r_led <= 1'b0;//  Делаем запись о состоянии светодиодаend// Тут описываем поведенческий блок, который будет реагировать на ниспадающий фронт тактовой частотыalways@(posedge sys_clk)begin    counter <= counter + 1'b1;// Увеличиваем счетчик        if(counter > 12000000)// Если счетчик больше некоторого условного значения    begin        r_led <= !r_led;// Инвертируем запись о значении состоянии светодиода        counter <= 32'b0;// Сбрасываем счетчик    end       endassign led_h17_d4 = r_led;          // Присваиваем текущее состояние ножке (условно)           endmodule

Нажимаем сочетание клавиш Ctrl + S чтобы сохранить код. Смотрим, не подсвечены ли где возможные ошибки. Если нет - то можем приступить к синтезированию, имплементации и генерации бинарного файла который мы потом зальем в нашу плату Zynq и будем наблюдать за морганием светодиода.

Нажимаем кнопку Run Synthesis и дожидаемся завершения синтеза. После окончания программа нам скажет, что синтез успешно завершен и мы можем переходить к следующему шагу:

Выбираем Run implementation и дожидаемся окончания. После выбираем пункт Generate Bitstream для запуска финального этапа:

Тут так же дожидаемся сигнала о том, что всё прошло успешно, выбираем Open Hardware Manager и можем приступать к заливке результата компиляции в нашу плату:

В открывшемся меню Hardware Manager нажимаем кнопку Auto connect, дожидаемся когда произойдет успешное соединение и откроется меню со списком устройств:

В меню слева или через нажатие правой кнопкой по xc7z020_1 в меню Hardware нажимаем пункт Program Device.

В следующем окне убеждаемся, что правильно указан путь к bitstream-файлу и нажимаем кнопку Program.

Программа заливается на нашу плату

И через мгновение на плате загорается светодиод D2, который сообщает нам, что FPGA DONE и в другом конце платы мы видим весело моргающий светодиод. =)

В целом задачу можно считать выполненной. Думаю, что по мере освоения мной языка Verilog и решения всяких новых задач с использованием этой платы - я смогу порадовать вас ещё.

Подробнее..

Что такое Zynq? Краткий обзор

28.06.2020 14:08:19 | Автор: admin
В далеком 2011 году компания Xilinx представила свои первые микросхемы SoC (System On Chip) Zynq-7000. SoC Zynq сочетает в себе программируемую логику, процессорный модуль и некоторую периферию. Статья не является максимально полным описанием SoC Zynq, а лишь отражает те или иные характеристики, с которыми пришлось столкнуться при разработке устройств на базе SoC Zynq.

Содержание


1 Общие сведения
2 Порты
2.1 MIO
2.2 EMIO
2.3 GP
2.4 HP
2.5 ACP
3 Периферия
3.1 Static Memory Controller
3.2 Quad-SPI Flash Controller
3.3 SD/SDIO Controller
3.4 General Purpose I/O (GPIO)
3.5 USB Host, Device, and OTG Controller
3.6 Gigabit Ethernet Controller
3.7 SPI Controller
3.8 CAN Controller
3.9 UART Controller
3.10 I2C Controller
3.11 XADC Interface
4 Процессорный модуль
5 Программируемая логика
6 Производительность
6.1 Теоретическая
6.2 Практическая
6.2.1 Обработка сетевых пакетов
6.2.2 Производительность порта HP
7 Передача данных между процессорным модулем и программируемой логикой
8 Загрузка SoC
9 Проблемы
10 Создание файла конфигурации
11 Заключение
12 Используемые источники

1 Общие сведения



Структурная схема Zynq.

Каждый Zynq состоит из одного или двух ядер ARM Cortex-A9 (ARM v7), кэш L1 у каждого ядра свой, кэш L2 общий. Поддерживаемая оперативная память имеет стандарты DDR3, DDR3L, DDR2, LPDDR-2. Максимальный объем оперативной памяти равен 1 Гбайт (2 микросхемы по 4 Гбит). Максимальная тактовая частота оперативной памяти 525 МГц. Операционные системы: Standalone (bare-metal) и Petalinux. Процессорный модуль общается с внешним миром и программируемой логикой с помощью портов, объединенных в группы:

  • MIO (multiplexed I/O);
  • EMIO (extended multiplexed I/O);
  • GP (General-Purpose Ports);
  • HP (High-Performance Ports);
  • ACP (Accelerator coherency port).


Схема интерфейсов Zynq.
Буквы S и M у порта обозначают соответственно Slave и Master.

Так как, в одном корпусе Zynq реализованы и процессорный модуль и программируемая логика, есть выводы, которые относятся к процессорному модулю и выводы, которые относятся к программируемой логике.

2 Порты


2.1 MIO


Порты MIO подключены к выводам процессора. С помощью MIO могут быть подключены следующие периферийные устройства процессорного модуля:

  • USB-контроллер 2 шт;
  • Gigabit Ethernet контроллер 2 шт;
  • SD/SDIO контроллер 2 шт;
  • UART 2 шт;
  • CAN 2 шт;
  • I2C 2 шт;
  • SPI 2 шт;
  • GPIO. Все выводы можно использовать как выводы общего назначения.

Так же, к MIO могут быть подключены следующие устройства памяти процессорного модуля:

  • QSPI контроллер;
  • ONFI контроллер;
  • SRAM/NOR контроллер.

Количество MIO портов равно 54 (за исключением некоторых микросхем в корпусе CLG225, там еще меньше). Поэтому все сразу включить не удастся. Для решения этой проблемы существует группа портов EMIO.

2.2 EMIO


EMIO представляет собой проброс периферийного устройства из процессорного модуля в программируемую логику и подключение периферийного устройства к выводам программируемой логики. Однако не все периферийные устройства можно перенести через EMIO, какие-то не переносятся совсем (USB), какие-то с изменением функциональности (Ethernet через EMIO имеет интерфейс GMII для подключения внешней микросхемы физического уровня). Между процессорным модулем и программируемой логикой по порту EMIO можно подключить 192 сигнала (64 входа и 128 выходов).

2.3 GP


Порты GP используют протокол Axi-Lite. Протокол подразумевает работу с данными шириной до 32 бит. За одну транзакцию осуществляется передача/прием данных, размером не больше 32 бит. Используется, как правило, для управления блоками в программируемой логике или при низкоскоростном обмене. Существует 2 порта Master и 2 порта Slave.

2.4 HP


Порты HP используют протокол AXI4. Протокол подразумевает работу с данными шириной 32 или 64 бита. Протокол поддерживает boost транзакции. За одну транзакцию осуществляется передача/прием произвольного размера данных. Используется, как правило, для организации высокоскоростного обмена данными между процессорным модулем и программируемой логикой. Существует 4 порта Slave.

Путь передачи данных с помощью порта HP

2.5 ACP


Порт ACP использует протокол AXI4. Протокол подразумевает работу с данными 64 бита. Протокол поддерживает boost транзакции. За одну транзакцию осуществляется передача/прием произвольного размера данных. Используется, как правило, для организации высокоскоростного обмена данными между процессорным модулем и программируемой логикой, причем данные дополнительно помещаются в L2 кэш. Существует один порт Slave.

Путь передачи данных с помощью порта ACP

3 Периферия


3.1 Static Memory Controller


SMC используется как контроллер памяти с технологией NAND или как контроллер памяти с параллельным интерфейсом. Поддерживает следующие типы памяти:

  • NAND flash;
  • NOR flash;
  • асинхронная SRAM.

Контроллер памяти с технологией NAND характеризуется следующими пунктами:

  • поддержка спецификации ONFI 1.0;
  • поддержка микросхем памяти емкостью до 1 Гбайт;
  • 1 бит ECC, считаемый аппаратно (используется в случае, если микросхема Flash-памяти не поддерживает ECC).
  • 8 или 16 линий обмена данными и одна линия выбора микросхемы (Chip Select);
  • асинхронный режим работы.

Контроллер памяти с параллельным интерфейсом характеризуется следующими пунктами:

  • 8 линий обмена данными;
  • 2 линии выбора микросхемы (Chip Select);
  • 25 линий адреса;
  • Асинхронный режим работы.


Подключение микросхемы NOR Flash с параллельным интерфейсом


Подключение микросхемы SRAM с параллельным интерфейсом


Подключение микросхемы NAND Flash

3.2 Quad-SPI Flash Controller


Реализует контроллер SPI для подключения внешних микросхем памяти по технологии NOR. Основные характеристики:

  • поддержка Flash-памяти от Micron и Spansion;
  • настраиваемая ширина шины (1x, 2x, 4x, 8x);
  • тактовая частота до 100 МГц

Возможные режимы работы:

  • один сигнал выбора микросхемы (Chip Select / Slave Select), 4 двунаправленных линии для данных;
  • два сигнала выбора микросхемы (Chip Select / Slave Select), 8 двунаправленных линий для данных (параллельное включение);
  • два сигнала выбора микросхемы (Chip Select / Slave Select), двунаправленных 4 линии для данных (общее включение);
  • один сигнал выбора микросхемы (Chip Select / Slave Select), одна линия для передачи, одна линия для приема.

image
Одна микросхема, 4 двунаправленные линии для обмена данными

image
Две микросхемы, 8 двунаправленных линий обмена данными

image
Две микросхемы, 4 двунаправленные линии

image
Одна микросхема, каждая линия однонаправленная

3.3 SD/SDIO Controller


Заявлена поддержка устройств eMMC, но без гарантии, что все будет работать корректно (The Zynq-7000 SoC is expected to work with eMMC devices because the protocol is the same as SD, but this has not been extensively verified). Основные характеристики:

  • поддержка спецификации SD Host Controller 2.0;
  • поддержка карт стандартов SDHS и SDHC;
  • поддержка стандарта MMC3.31;
  • режим только Host;
  • максимальная тактовая частота 50 МГц;
  • режим SPI не поддерживается.

image
Подключение SD карты

3.4 General Purpose I/O (GPIO)


Каждый из портов можно использовать в следующе режиме:

  • вход;
  • выход;
  • выход с третьим состоянием;
  • вход, изменение данных на котором, генерирует прерывание.

Каждый порт можно перенастроить отдельно от других.
image
Структура блока GPIO

3.5 USB Host, Device, and OTG Controller


Требует внешнюю микросхему физического уровня с интерфейсом ULPI. Основные характеристики:

  • поддержка интерфейса 2.0;
  • до 12 конечных точек/ Endpoint (одна управляющая и 11 настраиваемых);
  • OTG 1.3;
  • все типы транзакций (Control, Bulk, Interrupt, Isochronous);
  • поддерживаются режимы OTG, USB host, USB device.

image
Подключение контроллера USB

3.6 Gigabit Ethernet Controller


Требует внешнюю микросхему физического уровня с интерфейсом RGMII. Основные характеристики:

  • поддержка работы со скоростями 10/100/1000 Мбит/с;
  • поддержка режимов Full duplex и half duplex;
  • подсчет контрольной суммы для IP, TCP, UDP в версиях протокола IPv4 и IPv6;
  • Jumbo Frames не поддерживаются;
  • Gigabit Half Duplex не поддерживается;
  • поддержка VLAN;
  • поддержка Wake-on-LAN.

У каждого контроллера есть свой интерфейс MDIO. Однако при использовании двух контроллеров нельзя использовать два MDIO интерфейса, поскольку они занимают одинаковые выводы MIO 52 и MIO 53. Возможно использование одного интерфейса, подключенного через MIO, а второго через EMIO.
image
Подключение сетевого контроллера.

3.7 SPI Controller


Основные характеристики:

  • работа в режиме Master, Slave, Multi-Master;
  • поддержка работы с тремя устройствами в режиме Master, по 3 линии SS (Slave Select);
  • поддержка одновременной передачи и приема;
  • тактовый сигнал до 50МГц при использовании MIO, до 25МГц при использовании EMIO;
  • настраиваемые режимы фазы тактового сигнала и его полярности (CPHA, CPOL).

image
Фазы тактового сигнала и полярность

image
Подключение контроллера SPI в режиме Master

image
Подключение контроллера SPI в режиме Slave

3.8 CAN Controller


Основные характеристики:

  • поддержка стандартов CAN 2.0A, and CAN 2.0B;
  • поддержка кадров с длиной идентификатора в 11 и 29 бит;
  • скорость до 1 Мбит/с.

Требует внешней микросхемы физического уровня.

3.9 UART Controller


Основные характеристики:

  • настраиваемая скорость передачи;
  • настраиваемое количество бит данных 6, 7, 8;
  • настраиваемое количество стоп бит 1, 1.5, 2;
  • контроль четности;
  • передача данных в режимах Normal, Automatic Echo, Local Loopback, Remote Loopback.

image
Режимы работы контроллера UART

3.10 I2C Controller


Основные характеристики:

  • скорость обмена до 400 Кбит/с;
  • поддержка спецификации версии 2;
  • работа в режиме Master и Slave.


3.11 XADC Interface


The Xilinx analog mixed signal module. Аналого-цифровой преобразователь. Имеет в своем составе 2 двенадцатиразрядных АЦП с частотой дискретизации 1 MSPS, аналоговый мультиплексор (до 17 внешних аналоговых каналов), и встроенные датчики температуры и напряжения микросхемы Zynq. Аналоговые входы поддерживают полосу пропускания сигнала до 500кГц при частоте дискретизации в 1 MSPS.

4 Процессорный модуль


Каждый Zynq, как уже было сказано выше, содержит в своем составе одно или два процессорных ядра. Максимальная тактовая частота ядер зависит от класса скорости микросхемы (Speed Grade) [1]. Возможно 5 классов скорости: -1, -1L, -2, -2L, -3. Классы с индексом L характеризуются пониженным напряжением питания.

Количество ядер и зависимость максимальной частоты от класса скорости микросхемы
Микросхема Количество ядер Максимальная тактовая частота, МГц
-1 -2 -3
Z-7007S 1 667 766 -
Z-7012S 1 667 766 -
Z-7014SS 1 667 766 -
Z-7010 2 667 766 866
Z-7015 2 667 766 866
Z-7020 2 667 766 866
Z-7030 2 667 800 1000
Z-7035 2 667 800 1000
Z-7045 2 667 800 1000
Z-7100 2 667 800 -

5 Программируемая логика


Программируемая логика, используемая в Zynq основа на двух семействах FPGA: Artix-7 и Kintex-7. Artix позиционируется как менее производительное и с меньшим потреблением. Соответственно Kintex более производительно и с большим потреблением. На практике они различаются количеством доступных ресурсов и типом высокоскоростных приемопередатчиков (GTP со скоростью до 6,25 Гбит/с в Artix и GTX со скоростью до 12,5 Гбит/с в Kintex) [2].

Характеристики программируемой логики
Микросхема Семейство Логические ячейки LUT Триггеры BRAM DSP GTP/GTX
Z-7007S Artix 23K 14400 28800 50 66 -
Z-7012S Artix 55K 34400 68800 72 120 4
Z-7014S Artix 65K 40600 81200 107 170 -
Z-7010 Artix 28K 17600 35200 60 80 -
Z-7015 Artix 74K 46200 92400 95 160 4
Z-7020 Artix 85K 53200 106400 140 220 -
Z-7030 Kintex 125K 78600 157200 265 400 4
Z-7035 Kintex 275K 171900 343800 500 900 16
Z-7045 Kintex 350K 218600 437200 545 900 16
Z-7100 Kintex 444K 277400 554800 755 2020 16

LUT (Look-Up Tables) используются для реализации логических функций.
BRAM (Block RAM) -используются для хранения данных. Размер одного BRAM = 36Кбит.
Примечание. Количество доступных GTP/GTX так же зависит от используемого корпуса. В таблице указано максимальное значение.


6 Производительность


6.1 Теоретическая


В таблицу ниже сведена теоретическая пропуская способность между процессорным модулем и программируемой логикой, а так же пропускная способность памяти. Следует понимать, производительность получена умножением тактового сигнала на ширину интерфейса и не отражает реальную картину передачи данных.
Теоретическая пропускная способность между процессорным модулем и программируемой логикой
Интерфейс GP GP HP ACP DDR OCM
Тип PS Slave PS Master PS Slave PS Slave Внешняя память Внутренняя память
Ширина шины, бит 32 32 64 64 32 64
Тактовый сигнал, МГц 150 150 150 150 1066 222
Чтение, Мбайт/с 600 600 1200 1200 4264 1779
Запись, Мбайт/с 600 600 1200 1200 4264 1779
Чтение + запись, Мбайт/с 1200 1200 2400 2400 4264 3557
Количество интерфейсов 2 2 4 1 1 1
Общая пропускная способность, Мбайт/с 2400 2400 9600 2400 4264 3557
Теоретическая пропускная способность каналов DMA в процессорном модуле
DMA DMAC Контроллер Ethernet Контроллер USB Контроллер SD
Тип ARM PL310 PS Master PS Master PS Master
Ширина шины, бит 64 4 8 4
Тактовый сигнал, МГц 222 250 60 50
Чтение, Мбайт/с 1776 125 60 25
Запись, Мбайт/с 1776 125 60 25
Чтение + запись, Мбайт/с 3552 250 60 25
Количество интерфейсов 1 2 2 2
Общая пропускная способность, Мбайт/с 3552 500 120 50
Теоретическая пропускная способность интерконнектов в процессорном модуле
Interconnect Тактовый домен Ширина, бит Тактовый сигнал, МГц Чтение, Мбайт/с Запись, Мбайт/с Чтение + запись, Мбайт/с
Central Interconnect CPU_2x 64 222 1776 1776 3552
Masters CPU_1x 32 111 444 444 888
Slaves CPU_1x 32 111 444 444 888
Master Interconnect CPU_2x 32 222 888 888 1776
Slave Interconnect CPU_2x 32 222 888 888 1776
Memory Interconnect DDR_2x 64 355 2840 2840 5680
Из таблицы следует вывод: память DDR или OCM не может быть полностью загружена одним устройством, исключая случаи использования низкой тактовой частоты для DDR. Пропускная способность чтения DDR ограничена на скорости 2840 Мбайт/с из-за использования Memory Interconnect[3].

6.2 Практическая


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

6.2.1 Обработка сетевых пакетов


Производительность обработки сетевых пакетов
Схема включения Протокол MTU TX, Мбит/с Загрузка процессора, % RX, Мбит/с Загрузка процессора, %
PL Ethernet TCP 1500 824 70.7 651 89.5
TCP 8192 988 45.8 818 55.3
UDP 1500 583 54.4 565 66.2
UDP 8192 737 33.6 876 85.8
PS EMIO TCP 1500 801 - 756 -
PS MIO TCP 1500 678 - 691 -
При использовании схемы включения PL Ethernet, используется контроллер, реализованный в программируемой логике. Подключение приведено на схеме ниже. Подсчет контрольных сумм для протоколов IP, TCP, UDP выполняется ядром Axi Ethernet. Процессор используется для генерации и обработки сетевых пакетов. Для теста использовалась операционная система Petalinux с версией ядра 4.19. Замер выполнялся на плате ZC706, на которой установлена микросхема Z-7045 с классом скорости -2. Согласно таблице выше, процессорный модуль содержит 2 ядра и работает на тактовой частоте до 800МГц [4].

image
Подключение PL Ethernet

При использовании схемы включения PS EMIO, используется контроллер Ethernet, реализованный в процессорном модуле. Подключение приведено на схеме ниже. Подсчет контрольных сумм для протоколов IP, TCP, UDP выполняется ядром в процессорном модуле. Программируемая логика используется с целью преобразования интерфейсов для подключения внешней микросхемы физического уровня. Для теста использовалась операционная система Petalinux с версией ядра 4.6. Замер выполнялся на плате ZC706, на которой установлена микросхема Z-7045 с классом скорости -2. Согласно таблице выше, процессорный модуль содержит 2 ядра и работает на тактовой частоте до 800МГц [5].

image
Подключение PS EMIO

При использовании схемы включения PS MIO, используется контроллер Ethernet, реализованный в процессорном модуле. Подключение приведено на схеме ниже. Подсчет контрольных сумм для протоколов IP, TCP, UDP выполняется ядром в процессорном модуле. Подсчет контрольных сумм для протоколов IP, TCP, UDP выполняется ядром в процессорном модуле. Для теста использовалась операционная система Petalinux с версией ядра 4.0. Замер выполнялся на плате Zybo, на которой установлена микросхема Z-7010 с классом скорости -1. Согласно таблице выше, процессорный модуль содержит 2 ядра и работает на тактовой частоте до 667МГц.
image
Подключение PS MIO

Производительность сетевого контроллера в режиме Rx+Tx
Схема включения Протокол MTU TX, Мбит/с RX, Мбит/с
PS EMIO TCP 1500 470 218
Для теста использовалась операционная система Petalinux с версией ядра 4.14. Замер выполнялся на плате ZC706, на которой установлена микросхема Z-7045 с классом скорости -2. Согласно таблице выше, процессорный модуль содержит 2 ядра и работает на тактовой частоте до 800МГц [6].

Итого: если необходимо обеспечить обработку сетевых пакетов со скоростью 1Гбит/с, то Zynq не подходит для этой задачи.

6.2.2 Производительность порта HP


Теоретическая пропускная способность порта HP составляет 9,6Гбит/с. Для проверки пропускной способности использовалась плата Mini-ITX, на которой установлена микросхема Z-7100 с классом скорости -2. Согласно таблице выше, процессорный модуль содержит 2 ядра и работает на тактовой частоте до 800МГц. Операционная система Petalinux с версией ядра 3.19. Подключение приведено на схеме ниже.

image
Схема подключения для оценки скорости порта HP

Root Complex PCI-Express был реализован в программируемой логике с помощью ядра Axi PCIE. Процессорный модуль используется для обработки полученных пакетов. При таком подключении скорость чтения данных с флешки составляла чуть больше 100Мбайт/с.
При использовании порта HP для соединения двух микросхем Zynq по схеме ниже, скорость передачи данных скорость передачи достигает 2,5Гбит/с (ограничение блока Interface).

image
Схема соединения двух Zynq

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

7 Передача данных между процессорным модулем и программируемой логикой


Методы передачи данных
Метод Преимущества Недостатки Используется для Расчетная пропускная способность
CPU Programmed I/O (GP) Простое программное обеспечение. Небольшое использование ресурсов в PL. Простые устройства в PL Низкая пропускная способность Функций управления <25Мбайт/с
PS DMAC Небольшое использование ресурсов в PL. Средняя пропускная способность. Несколько каналов. Простые устройства в PL Несколько сложное программирование DMA Организации DMA при ограниченном количестве ресурсов в PL 600 Мбайт/с
PL AXI_HP DMA Высокая пропускная способность. Несколько интерфейсов Доступ только к OCM/DDR. Более сложные устройства в PL. Высокопроизводительного DMA для больших наборов данных. 1200 Мбайт/с на каждый канал
PL AXI_ACP DMA Высокая пропускная способность. Низкая задержка. Поддержка когерентности кэша. Большой объем данных может переполнить кэш. Использует пропускную способность с CPU Interconnect. Более сложные устройства в PL Высокопроизводительного DMA для небольших когерентных наборов данных. Уменьшения загрузки процессора. 600 Мбайт/с
PL AXI_GP DMA Средняя пропускная способность Более сложные устройства в PL. Функций управления из PL в PS.Доступ к I/O. 600 Мбайт/с

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

image
Путь передачи данных через Master GP (CPU Programmed I/O)

image
Путь передачи данных через Master GP (PS DMAC)

image
Путь передачи данных через HP (PL AXI_HP DMA)

image

Путь передачи данных для ACP (PL AXI_ACP DMA)

Из всех возможных методов передачи данных между процессорным модулем и программируемой логикой, мы используем только 2: CPU Programmed I/O и PL AXI_HP DMA о которых будет рассказано в отдельной статье.

8 Загрузка SoC


После снятия сигнала сброса (PS_POR_B), процессорный модуль считывает со специальных входов режим загрузки и запускает на выполнение код BootROM. BootROM предназначен для начальной настройки системы, копирования следующего загрузчика в OCM (On Chip Memory) и передачи управления следующему загрузчику.

Возможные режимы загрузки:

  • Quad-SPI;
  • SD Memory Card;
  • NAND;
  • NOR;
  • JTAG.

Хорошая статья от Xilinx, в которой перечислены виды загрузки и протестированные варианты www.xilinx.com/support/answers/50991.html

Если режим загрузки не JTAG, то BootROM ищет на устройстве загрузки следующий загрузчик FSBL (First State Boot Loader). FSBL используется для дальнейшей настройки системы (инициализация DDR, инициализация MIO). Также, FSBL используется для загрузки файла конфигурации в программируемую логику, если это необходимо. Файл конфигурации считывается с устройства загрузки и через специальный интерфейс(PCAP) в режиме DMA передается в программируемую логику. После загрузки программируемой логики, FSBL ищет следующее приложение для передачи управления. Возможны несколько вариантов:

  • следующего приложения нет, FSBL останавливается;
  • следующее приложение приложение пользователя (Bare Metal). Оно вычитывается в DDR, и ему передается управление;
  • следующее приложение U-BOOT. U-BOOT вычитывается в DDR и ему передается управление.

Таким образом, без процессорного модуля нельзя загрузить файл конфигурации в программируемую логику за исключением случая использования JTAG.

image
Временная шкала включения Zynq

Время выполнения BootROM сильно зависит от пропускной способности интерфейса устройства, на котором хранится файл конфигурации.
Сигнал INIT_B используется для указания, что начато конфигурирование программируемой логики (загрузка файла конфигурации).
Сигнал DONE используется для указания, что конфигурирование программируемой логики завершено.


image
Загрузка конфигурации в программируемую логику

9 Проблемы


При разработке устройств на базе Zynq столкнулись с несколькими проблемами. Возможно, в этом виновато плохое чтение документации.

  1. При работе с контроллером Ethernet, реализованном в процессорном модуле, напряжение питания банка должно быть +1,8В или +2,5В.
  2. При использовании Petalinux возможна загрузка только с использованием SD-карты.
  3. Не только Zynq, а вся седьмая серия от Xilinx (Artix, Kintex). Невозможно сформировать дифференциальный сигнал в банке с напряжением питания +3,3В.

10 Создание файла конфигурации


Как создать прошивку для Zynq? Для этого потребуется два инструмента от Xilinx: Vivado и Vitis. Так же добавим bare-metal приложение, которое передаст привет миру.

  1. Запустить Vivado. Создать проект для выбранной микросхемы. В примере будет использоваться плата Zybo, на которой установлена микросхема XC7Z010-1CLG400C.
  2. В меню слева (если его нет, то включить можно в View => Show Flow Navigator) выбрать Create Block Design => ввести имя файла => OK.

    image
  3. В открывшемся окне Diagram нажать на + или воспользоваться сочетанием клавиш Ctrl + I. В появившимся окне выбрать блок Zynq7 Processing System.

    image
  4. Вверху окна нажать на Run Block Automation, появившимся окне нажать OK.
  5. Двойной клик на Zynq7 Processing System (или правой кнопкой и пункт Customize Block). Если создается прошивка для покупной платы, то в комплекте с ней есть xml или tcl файл с настройками процессорного модуля. Его можно загрузить через пункт Import XPS Settings и указав путь к файлу. Иначе, необходимо по принципиальной схеме на устройство выяснить, какие MIO используются и куда подключены, какой тип памяти DDR и выставить соответствующие настройки во вкладках Peripheral I/O Pins, MIO Configuration, Clock Configuration и DDR Configuration.

    image
  6. В принципе, этого достаточно. Но если сейчас запустить синтез, Vivado выдаст критическую ошибку о неподключенном входе. Потому что по умолчанию включен вход GP0. Снова двойной клик на Zynq7 Processing System => PS-PL Configuration => Axi Non Secure Enablement => GP Master AXI Interface => снять галку с M AXI GP0 Interface => OK.
  7. File => Save Block Design. Вкладку можно закрыть.
  8. В вкладке Sources выбрать созданный файл, правый клик, Create HDL Wrapper. В окне выбрать нужный пункт (включить автообновление файла или нет, неважно), OK.

    image
  9. В меню слева выбрать Generate Bitstream => Yes => OK.
  10. После некоторого времени появится окно Bitstream Generation Completed, которое нужно закрыть.
  11. Необходимо создать каталог, в котором будут храниться FSBL и bare-metal приложение. Можно хранить их в папке с проектом или в другом месте.
  12. В Vivado File => Export => Export Hardware. Указать путь к созданному каталогу, галку Include bitsteam ставить не обязательно. В созданном каталоге появится файл .xsa
  13. Запустить Vitis (можно из Vivado => Tools => Launch Vitis), указать путь к каталогу с .xsa файлом.

    image
  14. В Vitis File => New => Application Project. В поле Project Name задать, например, Zybo_fsbl, нажать Next, выбрать вкладку Create a new platform form hardware (XSA), нажать +, указать путь к файлу .xsa, в поле Platform name ввести название, чтобы потом можно было отличить созданную платформу от платформ по-умолчанию, например ProcessingSystem_wrapper_hw, Next, снять галку Generate boot components => Next => в списке шаблонов выбрать Zynq FSBL => Finish.
  15. В окне слева (Explorer) развернуть каталог src, открыть файл main.c. Можно добавить define, чтобы видеть, что процессорный модуль запустился.
    #define FSBL_DEBUG#include "fsbl.h"#include "qspi.h"
    
  16. Выбрать файл Zybo_fsbl, меню Project => Build Project.

    image
  17. FSBL создан. Теперь приложение. File => New => Application Project. В поле Project Name задать, например, Zybo_hello, Next, во вкладке Select a platform from repository выбрать созданную ранее платформу ProcessingSystem_wrapper_hw, Next, Next, в списке шаблонов выбрать Hello World, Finish.
  18. Выбрать файл Zybo_hello, меню Project => Build Project.
  19. Приложение создано. Теперь необходимо собрать итоговый файл. В Vitis выбрать меню Xilinx => Create Boot Image. Оставить переключатель в положении Create new BIF file. В поле Output BIF file path с помощью кнопки Browse указать место хранение файла и имя файла.
  20. Выбрать выходной формат файла, mcs или bin и путь для хранения файла
  21. Нажать кнопку Add и указать путь к файлу Zybo_fsbl.elf (исполняемый файл FSBL). Он расположен в папке с проектом Zybo_fsbl, каталог Debug, остальные поля оставить без изменений.
  22. Нажать кнопку Add и указать путь к файлу bit. Если была установлена галка Include bitsteam, то bit файл можно найти в каталоге с файлом .xsa, далее каталог с названием платформы, далее каталог bitstream. Если галки не было, то bit файл расположен в каталоге проекта, каталог .runs, каталог impl_1. Остальные поля оставить без изменений.
  23. Нажать кнопку Add и указать путь к файлу Zybo_hello.elf, который расположен по аналогии с FSBL в каталоге Debug. Остальные поля оставить без изменений.
  24. Нажать кнопку Create Image. Файл конфигурации создан.

    image

Если полученный файл конфигурации прошить в плату, подключиться монитором com-порта к отладочном порту Zynq, то в мониторе com-порта можно увидеть следующее:

Xilinx First Stage Boot Loader
Release 2019.2 Jun 26 2020-09:16:22
Silicon Version 3.1
Boot mode is QSPI
SUCCESSFUL_HANDOFF
FSBL Status = 0x1
Hello World

11 Заключение


Вывод: SoC Zynq подходит для решения задач, которые не требуют высокой производительности от процессорного модуля. Идеальный случай, когда алгоритм обработки данных реализован в программируемой логике, а процессорный модуль используется для загрузки данных, или обработки исключительных ситуаций, реализация которых трудоемка в программируемой логике.

12 Используемые источники


  1. www.xilinx.com/support/documentation/data_sheets/ds190-Zynq-7000-Overview.pdf
  2. www.xilinx.com/support/documentation/selection-guides/zynq-7000-product-selection-guide.pdf
  3. www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf
  4. xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842485/Linux+AXI+Ethernet+driver
  5. xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841613/XAPP1082+2017.4+Performance
  6. www.xilinx.com/support/answers/73065.html
Подробнее..

Zynq. Передача данных между процессорным модулем и программируемой логикой

27.12.2020 18:22:59 | Автор: admin
Как и обещал в предыдущей статье (Что такое Zynq? Краткий обзор), поговорим о передаче данных между процессорным модулем и программируемой логикой. В предыдущей статье упоминалось четыре способа передачи данных, в статье будут рассмотрены два способа, которые нашли большее применение. Подробности под катом. Осторожно, много картинок!

Содержание


1 Общие сведения
2 Передача данных в режиме PIO
2.1 Аппаратная часть
2.2 Программная часть
2.3 Результаты
3 Передача данных в режиме DMA
3.1 Аппаратная часть
3.2 Программная часть
3.3 Результаты
4 Заключение
5 Используемые источники

1 Общие сведения


В общем случае, передача данных между процессорным модулем и программируемой логикой возможна в двух режимах:

  • PIO, используется порт GP.
  • DMA, используется порт HP.

2 Передача данных в режиме PIO


В режиме PIO процессорный модуль работает с программируемой логикой как с набором регистров. Чтобы записать или прочитать определенный объем данных, нужно постоянное участие процессорного модуля. В режиме PIO инициатором всех транзакций является процессорный модуль. Подключение программируемой логики предполагает использование порта GP, где Master это процессорный модуль, Slave программируемая логика.


Структура проекта при использовании PIO

2.1 Аппаратная часть


  1. Создаем проект для Zybo в Vivado, тип микросхемы xc7z010clg400-1.
  2. Создаем block design. Во Flow Navigator => Create Block Design => имя ProcessingSystem => OK.
  3. Используя кнопку + на поле или сочетания клавиш Ctrl + I добавим процессорное ядро.

  4. Подключим необходимые выводы, нажав кнопку Run Block Automation => OK.
  5. Импортируем настройки процессорного модуля. Для этого выполним двойной клик на Zynq7 Processing System => Import XPS Setting => Укажем путь к файлу => OK => OK.
  6. Создаем периферийное ядро, которое будет реализовывать доступ к регистрам в программируемой логике. Tools => Create and Package New IP => Next => Create a new AXI4 peripheral => Next => Задаем имя ядра, например PIO_registers и указываем путь к каталогу для сохранения => Next => В этом окне можно выбрать количество регистров (4 хватит), тип интерфейса, в данном случае это Lite => Next => Add IP to the repository => Finish.

  7. После этих действий, созданное ядро появится в списке доступных для работы в IP каталоге. Можно его увидеть, если выбрать в Flow Navigator => IP Catalog.

  8. Добавим созданное ядро на рабочую область. Ctrl + I => PIO_registers.

  9. Отредактируем созданное ядро, добавив в него работу с перемычками и светодиодами. Для этого на блоке PIO_registers правой кнопкой мыши => Edit in IP Packager => OK. Откроется новое окно Vivado с созданным ядром.
  10. В файле PIO_registers_v1_0.vhd добавим входные и выходные порты и подключим их к внутреннему модулю:

    iSwitches: instd_logic_vector( 3 downto 0);oLeds: outstd_logic_vector( 3 downto 0);...iSwitches=> iSwitches,oLeds=> oLeds,
    

  11. В файле PIO_registers_v1_0_S_AXI.vhd добавим входные и выходные порты:

    iSwitches: instd_logic_vector( 3 downto 0);oLeds: outstd_logic_vector( 3 downto 0);
    

  12. Опишем обработку входов и выходов:

    signalSwitchesReg: std_logic_vector(31 downto 0);...process (SwitchesReg, slv_reg1, slv_reg2, slv_reg3, axi_araddr, S_AXI_ARESETN, slv_reg_rden)variable loc_addr :std_logic_vector(OPT_MEM_ADDR_BITS downto 0);begin    -- Address decoding for reading registers    loc_addr := axi_araddr(ADDR_LSB + OPT_MEM_ADDR_BITS downto ADDR_LSB);    case loc_addr is      when b"00" =>        reg_data_out <= SwitchesReg;      when b"01" =>        reg_data_out <= slv_reg1;      when b"10" =>        reg_data_out <= slv_reg2;      when b"11" =>        reg_data_out <= slv_reg3;      when others =>        reg_data_out  <= (others => '0');    end case;end process;process (S_AXI_ACLK) beginif (rising_edge(S_AXI_ACLK)) thenif (S_AXI_ARESETN = '0') thenSwitchesReg <= (others => '0');elseSwitchesReg( 3 downto 0) <= iSwitches;end if;end if;end process;process (S_AXI_ACLK) beginif (rising_edge(S_AXI_ACLK)) thenif (S_AXI_ARESETN = '0') thenoLeds <= (others => '0');elseoLeds <= slv_reg1( 3 downto 0);end if;end if;end process;
    

  13. Сохраняем vhd файлы, открываем вкладку Package IP PIO_registers. Обновим ядро с учетом изменённых файлов. Вкладка Compatibility меняем Life Cycle на Production. Вкладка File Groups => Merge changes from File Group Wizard. Вкладка Customization Parameters => Merge changes from Customization Parameters Wizard. Вкладка Review and Package => Re-Package IP => Yes. Vivado с созданным ядром закроется.
  14. В окне Block Design выбираем Report IP Status, в появившимся внизу окне выбираем Upgrade Selected => OK => Skip => OK.

  15. Подключим созданное ядро к процессору. Для этого нажимаем Run Connection Automation => OK.

  16. Подключим порты созданного ядра к портам block designa. Для этого выделяем порт, нажимаем правую кнопку => Make External.

  17. Переименуем полученные порты для удобства подключения iSwitches_0 => iSwitches. oLeds_0 => oLeds.

  18. Проверим полученный дизайн => Tools => Validate Design => Ok.
  19. File => Save Block Design.
  20. Переключаемся из режима работы с block design в режим работы с проектом, нажав во Flow Navigator => Project Manager.
  21. Смотрим, как описаны порты у полученного block designa. Для этого выбраем файл ProcessingSystem.bd, правой кнопкой => View Instantiation Template.

  22. Создаем vhd файл top-уровня и подключаем в нем полученный block design. File => Add Sources => Add or create design sources => Next => Create File => вводим имя файла и указываем путь до каталога => OK => Finish => OK => Yes.
  23. Заполняем файл:

    entity PioTransfer isport(DDR_addr: inout std_logic_vector(14 downto 0 );DDR_ba: inout std_logic_vector( 2 downto 0 );DDR_cas_n: inout std_logic;DDR_ck_n: inout std_logic;DDR_ck_p: inout std_logic;DDR_cke: inout std_logic;DDR_cs_n: inout std_logic;DDR_dm: inout std_logic_vector( 3 downto 0 );DDR_dq: inout std_logic_vector(31 downto 0 );DDR_dqs_n: inout std_logic_vector( 3 downto 0 );DDR_dqs_p: inout std_logic_vector( 3 downto 0 );DDR_odt: inout std_logic;DDR_ras_n: inout std_logic;DDR_reset_n: inout std_logic;DDR_we_n: inout std_logic;FIXED_IO_ddr_vrn: inout std_logic;FIXED_IO_ddr_vrp: inout std_logic;FIXED_IO_mio: inout std_logic_vector( 53 downto 0 );FIXED_IO_ps_clk: inout std_logic;FIXED_IO_ps_porb: inout std_logic;FIXED_IO_ps_srstb: inout std_logic;-- ControliSwitches: instd_logic_vector( 3 downto 0 );oLeds: outstd_logic_vector( 3 downto 0 ) );end PioTransfer;architecture Behavioral of PioTransfer isbeginPS : entity WORK.ProcessingSystemport map(DDR_addr=> DDR_addr,DDR_ba=> DDR_ba,DDR_cas_n=> DDR_cas_n,DDR_ck_n=> DDR_ck_n,DDR_ck_p=> DDR_ck_p,DDR_cke=> DDR_cke,DDR_cs_n=> DDR_cs_n,DDR_dm=> DDR_dm,DDR_dq=> DDR_dq,DDR_dqs_n=> DDR_dqs_n,DDR_dqs_p=> DDR_dqs_p,DDR_odt=> DDR_odt,DDR_ras_n=> DDR_ras_n,DDR_reset_n=> DDR_reset_n,DDR_we_n=> DDR_we_n,FIXED_IO_ddr_vrn=> FIXED_IO_ddr_vrn,FIXED_IO_ddr_vrp=> FIXED_IO_ddr_vrp,FIXED_IO_mio=> FIXED_IO_mio,FIXED_IO_ps_clk=> FIXED_IO_ps_clk,FIXED_IO_ps_porb=> FIXED_IO_ps_porb,FIXED_IO_ps_srstb=> FIXED_IO_ps_srstb,-- ControliSwitches=> iSwitches,oLeds=> oLeds );end Behavioral;
    

  24. Добавляем используемые входы и выходы в файл пользовательских ограничений. File => Add sources => Add or create constrains => Next => Create File => вводим имя файла и указываем путь до каталога => OK => Finish.

  25. Заполняем файл согласно принципиальной схеме:

    #Switchesset_property PACKAGE_PIN G15 [get_ports {iSwitches[0]}]set_property PACKAGE_PIN P15 [get_ports {iSwitches[1]}]set_property PACKAGE_PIN W13 [get_ports {iSwitches[2]}]set_property PACKAGE_PIN T16 [get_ports {iSwitches[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {iSwitches[*]}]#LEDs#IO_L23P_T3_35set_property PACKAGE_PIN M14 [get_ports {oLeds[0]}]set_property PACKAGE_PIN M15 [get_ports {oLeds[1]}]set_property PACKAGE_PIN G14 [get_ports {oLeds[2]}]set_property PACKAGE_PIN D18 [get_ports {oLeds[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {oLeds[*]}] 
    

  26. Соберем проект. Для этого во Flow Navigator => Generate Bitstream => OK. Появившиеся окно, что создание прошивки успешно завершено, закрываем.
  27. Экспортируем полученные файлы для разработки приложения на процессорном модуле. Для этого выбираем File => Export => Export Hardware => вводим имя файла и указываем путь до каталога => OK. Получаем на выходе файл .xsa


2.2 Программная часть


Теперь нужно написать приложение, работающее на процессорном модуле, которое будет читать данные из программируемой логики и писать данные в программируемую логику. Необходимо запустить среду разработки Vitis и создать приложение по шаблону Hello World, пример этого показан в предыдущей статье[1].

Адрес созданного ядра для обращения со стороны процессорного модуля можно посмотреть в Vivado. В Flow Navigator => Open Block Design => Вкладка Address Editor. В данном случае адрес равен 0x43C0_0000. По этому адресу расположен регистр, в котором хранится признак, в каком состоянии находятся переключатели. Соответственно, по адресу 0x43C0_0004 расположен регистр, который подключен к светодиодам.

В Vitis откроем файл helloworld.c и заполним:

int main(){init_platform();u32 Status = 0x00;u32 Command = 0x00;xil_printf("Hello World\n\r");while (1){Status = Xil_In32(0x43C00000);xil_printf("Status %x\n\r", Status);if (Status == 0x01 || Status == 0x02 || Status == 0x04 || Status == 0x08){Command = 0x01;}else if (Status == 0x03 || Status == 0x5 || Status == 0x06 || Status == 0x9 || Status == 0xA || Status == 0x0C){Command = 0x03;}else if (Status == 0x7 || Status ==  0x0B || Status == 0x0D || Status == 0x0E){Command = 0x7;}else if (Status == 0x0F){Command = 0x0F;}else{Command = 0x00;}xil_printf("Command %x\n\r", Command);Xil_Out32(0x43C00004, Command);usleep(1000000);}cleanup_platform();return 0;} 

Где функция Xil_In32 используется для чтения 4х байт данных из программируемой логики, а Xil_Out32 соответственно для записи 4х байт данных в программируемую логику.

2.3 Результаты


Собираем приложение, создаем файл прошивки и заливаем в плату. Описано в предыдущей статье[1].

Запускаем, смотрим в мониторе com-порта:

Xilinx First Stage Boot Loader Release 2019.2Dec  9 2020-15:16:52Silicon Version 3.1Boot mode is QSPISUCCESSFUL_HANDOFFFSBL Status = 0x1Hello WorldStatus 0Command 0Status 8Command 1Status CCommand 3Status DCommand 7Status FCommand F

Все работает корректно.

Таким образом, для доступа в режиме PIO к программируемой логике, необходимо в программируемой логике реализовать один из интерфейсов связи с процессорным модулем, где инициатором является процессорный модуль. Такой интерфейс представлен только портом GP.

Посмотрим, как быстро обрабатываются запросы к программируемой логике через порт GP. Для этого в приложении, работающем на процессорном модуле, добавим подряд несколько записей в регистр в программируемой логике, и измерим время между транзакциями в программируемой логике с помощью вытащенных в отладчик сигналов шины.

Когда шина Axi-Lite работает на частоте 100 МГц, пауза между запросами составляет в среднем 23 такта. Изменим частоту работы шины до 200 МГц. Пауза между запросами становится равной в среднем 33 такта.

Итого, 4 байта данных передаются на частоте 100 МГц 23 такта. Скорость составляет: 32/(23*10нс)= 139 130 434 байт/с 135 869 Кбайт/с 132 Мбайт/с.
Итого, 4 байта данных передаются на частоте 200 МГц 33 такта. Скорость составляет 32/(33*5нс)= 193 939 393 байт/с 189 393 Кбайт/с 184 Мбайт/с.
Таким образом, можно достичь скорости в 184 Мбайт/с, но при постоянном участии процессорного модуля.

Проект: github.com/Finnetrib/PioTransfer

3 Передача данных в режиме DMA


Передача данных в режиме DMA подразумевает, что процессорный модуль настраивает параметры обмена данными и не участвует непосредственно в обмене. Таким образом, достигается две цели: снижение нагрузки на процессорный модуль и увеличение скорости обработки данных. Платой за это является усложнение аппаратуры.

В Zynq возможно использование нескольких ip-ядер, реализующих функции DMA. В данной статье будет рассмотрено ядро AXI DMA [2].

В AXI DMA есть два канала MM2S и S2MM. Канал MM2S (Memory-mapped to stream) используется для передачи данных из процессорного модуля в программируемую логику. Канал S2MM (Stream to memory-mapped) используется для передачи данных из программируемой логики в процессорный модуль. Каналы работают независимо друг от друга.

AXI DMA подразумевает два варианта использования:

  • Direct Register Mode
  • Scatter / Gather Mode

В Direct Register Mode используется один набор регистров, который позволяет передать один буфер из программируемой логики в процессорный модуль и наоборот. Например, для передачи буфера данных из программируемой логики в процессорный модуль, необходимо заполнить поля адреса и размера буфера и запустить DMA. В результате DMA заполнит один буфер в процессорном модуле и остановится.

В Scatter / Gather Mode используется список дескрипторов. DMA обрабатывает буфер, описанный в дескрипторе, и переходит к обработке буфера, описанного в следующем дескрипторе.

3.1 Аппаратная часть



Структура проекта при использовании DMA

Рассмотрим вариант, когда список дескрипторов хранится в программируемой логике. У блока DMA есть порт управления, который подключается к порту GP процессорного модуля. Также имеется порт HP, используемый для обращения к ОЗУ процессорного модуля. Список дескрипторов хранится в памяти дескрипторов. Доступ к памяти дескрипторов возможен как из DMA, так и из процессорного модуля. Процессорный модуль заполняет дескрипторы, DMA вычитывает дескрипторы.

  1. Создаем block design. Во Flow Navigator => Create Block Design => имя ProcessingSystem => OK.
  2. Используя кнопку + на поле или сочетания клавиш Ctrl + I добавим процессорное ядро.
  3. Подключим необходимые выводы, нажав кнопку Run Block Automation => OK.
  4. Импортируем настройки процессорного модуля. Для этого выполним двойной клик на Zynq7 Processing System => Import XPS Setting => Укажем путь к файлу => OK => OK
  5. Добавим на поле одно ядро AXI Direct Memory Access, два ядра AXI BRAM Controller, одно ядро Block Memory Generator.

  6. Двойной клик на ядре AXI Direct Memory Access, настроим ядро. Галка Enable Scatter Gather Engine включит режим работы по списком дескрипторов, оставляем. Галка Enable Control / Status Stream используется для работы совместно с ядром AXI Ethernet, снимаем. Поле With of Buffer Length Register задает количество разрядов, используемых при обработке размера буфера. Запишем в поле число 20, что даст максимальный размер буфера в 2^20 = 1 048 576 байт. Поле Address With задает разрядность адреса. Значения в 32 будет достаточно. Галки Enable Read Channel и Enable Write Channel позволяют включить каждый из каналов независимо друг от друга. Переключатель Enable Single AXI4 Data interface позволяет сократить количество линий связи на диаграмме, поэтому включим его. Нажимаем OK после настройки.

  7. Двойной клик на ядре AXI BRAM Controller. В поле Number of BRAM Interfaces выбираем значение 1. Нажимаем OK после настройки.

  8. Повторяем для второго ядра AXI BRAM Controller.
  9. Двойной клик на ядре Block Memory Generator. В поле Memory Type выбираем True Dual Port RAM. Нажимаем OK после настройки.

  10. Подключим полученный блок памяти к контроллерам памяти. Для этого нажимаем Run Connection Automation => Галку на axi_bram_ctrl_0 BRAM_PORTA => Галку на axi_bram_ctrl_1 BRAM_PORTA => OK.

  11. Подключим нулевой контроллер памяти к процессорному модулю. Для этого нажимаем Run Connection Automation => Галку на axi_bram_ctrl_0 S_AXI => Выбрать Master Interface /processing_system7_0/M_AXI_GP0 => OK. Таким образом, процессорный модуль получит доступ к памяти дескрипторов.

  12. Подключим первый контроллер памяти к ядру DMA. Для этого нажимаем Run Connection Automation => Галку на axi_bram_ctrl_1 S_AXI => Выбрать Master Interface /axi_dma_0/M_AXI_SG => OK. Таким образом, DMA контроллер также получит доступ к памяти дескрипторов.

  13. Подключим управление DMA к процессорному модулю. Для этого нажимаем Run Connection Automation => Галку на axi_dma_0 S_AXI_LITE => OK.

  14. Включим в процессорном модуле порт для доступа к оперативной памяти процессорного модуля HP и прерывания из программируемой логики. Для этого двойной клик на ядре Zynq7 Processing System => Вкладка PS-PL Configuration => Развернем HP Slave AXI Interface => Галку на S AXI HP0 Interface.


    Вкладка Interrupts => Развернем Fabric Interrupts => Развернуть PL-PS Interrupts Ports => Галку на Fabric Interrupts => Галку на IRQ_F2P => OK.

  15. Подключим DMA к порту для доступа к оперативной памяти. Для этого нажимаем Run Connection Automation => Галку на processing_system7_0 S_AXI_HP0 => Выбираем Master Interface /axi_dma_0/M_AXI => OK.

  16. Подключим прерывания от DMA к процессорному модулю. Для этого добавим на поле блок Concat через кнопку + или сочетание клавиш Ctrl + I.
  17. Наведем курсор на вывод mm2s_introut DMA, курсор примет форму карандаша. Кликаем на вывод mm2s_introut и не отпуская кнопку мыши тянем линию к входу In0 блока Concat. Доводим до блока, после появления зеленой галочки, отпускаем.

  18. Повторяем также для вывода s2mm_introut, который подключим к входу In1 блока Concat.
  19. Выход dout блока Concat таким же образом подключим к входу IRQ_F2P ядра Zynq7 Processing System.
  20. DMA подключено. Теперь необходимо подключить вход и выход DMA для данных. Так как обработка данных планируется за пределами Block Design, вытащим наружу тактовый сигнал и сигнал сброса. Для этого правый клик на свободном месте поля и выбираем Create Port или сочетание клавиш Ctrl + K. Заполняем имя порта, направление и тип => OK.

  21. Наведем курсор мыши на созданный порт и после появления карандаша подключим порт к выводу FCLK_CLK0 ядра Zynq7 Processing System.
  22. Подключим сигнал сброса. Для этого выделяем вывод peripheral_reset ядра Processor System Reset => правой кнопкой мыши => Make External.
  23. Клик на полученный порт, зададим новое имя порта и укажем, на каком тактовом сигнале необходимо обрабатывать данный сигнал.

  24. Подключим вход данных DMA. Для этого выделим порт S_AXIS_S2MM блока AXI Direct Memory Access => правой кнопкой мыши => Make External.
  25. Клик на полученный порт, зададим новое имя порта и укажем, на каком тактовом сигнале необходимо обрабатывать данный сигнал.

  26. Подключим выход данных DMA. Для этого выделим порт M_AXIS_MM2S блока AXI Direct Memory Access => правой кнопкой мыши => Make External.
  27. Клик на полученный порт, зададим новое имя порта и укажем, на каком тактовом сигнале необходимо обрабатывать данный сигнал.

  28. Подключим входы тактового сигнала для портов S_AXIS_S2MM и M_AXIS_MM2S у блока AXI Direct Memory Access. Для этого нажимаем Run Connection Automation => Галку на m_axi_mm2s_aclk и m_axi_s2mm_aclk => OK
  29. Теперь необходимо изменить адресное пространство так, чтобы при обращении к блоку памяти от процессорного модуля и от блока DMA адреса были одинаковы. Заодно и увеличим размер памяти для хранения дескрипторов. Вкладка Address Editor => processing_system7_0 / Data / axi_bram_ctrl_0 => Offset Address 0x4000_0000 => Range 32K. Далее axi_dma_0 / Data_SG / axi_bram_ctrl_1 => Offset Address 0x4000_0000 => Range 32K.

  30. Tools => Validate Design => OK. Полученная схема:

  31. File => Save Block Design.
  32. Переключаемся из режима работы с block design в режим работы с проектом, нажав во Flow Navigator => Project Manager.
  33. Смотрим, как описаны порты у полученного block designa. Для этого выбираем файл ProcessingSystem.bd, правой кнопкой => View Instantiation Template.
  34. Создаем vhd файл top-уровня и подключаем в нем полученный block design. File => Add Sources => Add or create design sources => Next => Create File => вводим имя файла и указываем путь до каталога => OK => Finish => OK => Yes.


  35. Заполняем файл:
    entity DmaTransfer isport(DDR_addr: inout std_logic_vector(14 downto 0);DDR_ba: inout std_logic_vector( 2 downto 0);DDR_cas_n: inout std_logic;DDR_ck_n: inout std_logic;DDR_ck_p: inout std_logic;DDR_cke: inout std_logic;DDR_cs_n: inout std_logic;DDR_dm: inout std_logic_vector( 3 downto 0);DDR_dq: inout std_logic_vector(31 downto 0);DDR_dqs_n: inout std_logic_vector( 3 downto 0);DDR_dqs_p: inout std_logic_vector( 3 downto 0);DDR_odt: inout std_logic;DDR_ras_n: inout std_logic;DDR_reset_n: inout std_logic;DDR_we_n: inout std_logic;FIXED_IO_ddr_vrn: inout std_logic;FIXED_IO_ddr_vrp: inout std_logic;FIXED_IO_mio: inout std_logic_vector(53 downto 0);FIXED_IO_ps_clk: inout std_logic;FIXED_IO_ps_porb: inout std_logic;FIXED_IO_ps_srstb: inout std_logic );end DmaTransfer;architecture Behavioral of DmaTransfer issignalRxData: std_logic_vector(31 downto 0);signalRxKeep: std_logic_vector( 3 downto 0);signalRxLast: std_logic;signalRxValid: std_logic;signalRxReady: std_logic;signalTxData: std_logic_vector(31 downto 0);signalTxKeep: std_logic_vector( 3 downto 0);signalTxLast: std_logic;signalTxValid: std_logic;signalTxReady: std_logic;signalclk: std_logic;signalrst: std_logic;signalFifoDataW: std_logic_vector(36 downto 0);signalFifoWrite: std_logic;signalFifoRead: std_logic;signalFifoDataR: std_logic_vector(36 downto 0);signalFifoEmpty: std_logic;signalFifoFull: std_logic;beginPS : entity WORK.ProcessingSystemport map(DDR_addr=> DDR_addr,DDR_ba=> DDR_ba,DDR_cas_n=> DDR_cas_n,DDR_ck_n=> DDR_ck_n,DDR_ck_p=> DDR_ck_p,DDR_cke=> DDR_cke,DDR_cs_n=> DDR_cs_n,DDR_dm=> DDR_dm,DDR_dq=> DDR_dq,DDR_dqs_n=> DDR_dqs_n,DDR_dqs_p=> DDR_dqs_p,DDR_odt=> DDR_odt,DDR_ras_n=> DDR_ras_n,DDR_reset_n=> DDR_reset_n,DDR_we_n=> DDR_we_n,FIXED_IO_ddr_vrn=> FIXED_IO_ddr_vrn,FIXED_IO_ddr_vrp=> FIXED_IO_ddr_vrp,FIXED_IO_mio=> FIXED_IO_mio,FIXED_IO_ps_clk=> FIXED_IO_ps_clk,FIXED_IO_ps_porb=> FIXED_IO_ps_porb,FIXED_IO_ps_srstb=> FIXED_IO_ps_srstb,-- Dma ChanneliDmaRx_tdata=> RxData,iDmaRx_tkeep=> RxKeep,iDmaRx_tlast=> RxLast,iDmaRx_tready=> RxReady,iDmaRx_tvalid=> RxValid,oDmaTx_tdata=> TxData,oDmaTx_tkeep=> TxKeep,oDmaTx_tlast=> TxLast,oDmaTx_tready=> TxReady,oDmaTx_tvalid=> TxValid,-- SystemoZynqClk=> clk,oZynqRst(0)=> rst );FifoDataW(31 downto  0) <= not TxData;FifoDataW(35 downto 32) <= TxKeep;FifoDataW(    36) <= TxLast;FifoWrite <= TxValid and not FifoFull;TxReady <= not FifoFull;EchFifo : entity WORK.SyncFifoBram37x1024port map(clk=> clk,srst=> rst,din=> FifoDataW,wr_en=> FifoWrite,rd_en=> FifoRead,dout=> FifoDataR,full=> open,empty=> FifoEmpty,prog_full=> FifoFull );RxData <= FifoDataR(31 downto  0);RxKeep <= FifoDataR(35 downto 32);RxLast <= FifoDataR(36);RxValid <= not FifoEmpty;FifoRead <= RxReady;end Behavioral; 
    
  36. Соберем проект. Для этого во Flow Navigator => Generate Bitstream => OK. Появившиеся окно, что создание прошивки успешно завершено, закрываем.
  37. Экспортируем полученные файлы для разработки приложения на процессоре. Для этого выбираем File => Export => Export Hardware => вводим имя файла и указываем путь до каталога => OK. Получаем на выходе файл .xsa



3.2 Программная часть


Теперь нужно написать приложение, работающее на процессорном модуле. Необходимо запустить среду разработки Vitis и создать приложение по шаблону Hello World, пример этого показан в предыдущей статье.

Формат дескрипторов для Axi DMA описан в документе на ядро [2]. Дескриптор имеет размер 52 байта, однако, адрес, по которому расположен дескриптор, должен быть выровнен на 64 байта.

Кратко по формату дескриптора:

  • NXTDESC адрес следующего дескриптора;
  • NXTDESC_MSB старшие 32 бита адреса следующего дескриптора;
  • BUFFER_ADDRESS адрес буфера;
  • BUFFER_ADDRESS_MSB старшие 32 бита адреса буфера;
  • RESERVED не используется;
  • RESERVED не используется;
  • CONTROL задает размер буфера, признаки начала и конца пакета;
  • STATUS показывает, сколько байт принято/передано, обработан/не обработан;
  • APP0 используется для работы с каналом Control/Status Stream;
  • APP1 используется для работы с каналом Control/Status Stream;
  • APP2 используется для работы с каналом Control/Status Stream;
  • APP3 используется для работы с каналом Control/Status Stream;
  • APP4 используется для работы с каналом Control/Status Stream.

Адреса в программируемой логике для обращения со стороны процессорного модуля можно посмотреть в Vivado. В Flow Navigator => Open Block Design => Вкладка Address Editor. В данном случае адрес DMA равен 0x4040_0000. Адрес начала области памяти для дескрипторов равен 0x4000_0000.

  1. В Vitis откроем файл helloworld.c и подключим следующие библиотеки

    #include <xil_io.h>#include "sleep.h"#include "xil_cache.h"#include "xil_mem.h"
    
  2. Каждый дескриптор должен начинаться с адреса, кратного 64 байтам. Следовательно, в 32Кбайта поместятся 32 768 / 64 = 512 дескрипторов. По 256 на прием и 256 на передачу.

    #define DESC_COUNT 256... /** Descriptors for receive */struct SGDesc RxDesc[DESC_COUNT];/** Descriptors for transmit */struct SGDesc TxDesc[DESC_COUNT];
    
  3. Выключим работу кэша, чтобы не думать, когда его сбросить.

    /** Flush Cache */Xil_DCacheFlush();/** Disable Cache */Xil_DCacheDisable();
    

  4. Заполним буферы, которые будут передаваться в программируемую логику.

    for (u16 desc = 0; desc < DESC_COUNT; desc++){for (u32 i = 0; i < BUFFER_SIZE; i++){TxBuffer[desc][i] = desc + i;}}
    
  5. Заполним список дескрипторов для передачи.

    for (u16 i = 0; i < DESC_COUNT; i++){TxDesc[i].NXTDESC = &TxDesc[i];TxDesc[i].NXTDESC_MSB = 0x0;TxDesc[i].BUFFER_ADDRESS = &TxBuffer[i][0];TxDesc[i].BUFFER_ADDRESS_MSB = 0x0;TxDesc[i].RESERVED0 = 0x0;TxDesc[i].RESERVED1 = 0x0;TxDesc[i].CONTROL = 0xC000000 + sizeof(TxBuffer[i]);TxDesc[i].STATUS = 0x0;TxDesc[i].APP0 = 0x0;TxDesc[i].APP1 = 0x0;TxDesc[i].APP2 = 0x0;TxDesc[i].APP3 = 0x0;TxDesc[i].APP4 = 0x0;}
    
  6. Скопируем дескрипторы передачи в память дескрипторов, которая расположена в программируемой логике.

    DescAddr = 0x40000000;for (u16 i = 0; i < DESC_COUNT; i++){Xil_MemCpy(DescAddr, &TxDesc[i], sizeof(TxDesc[i]));DescAddr += 0x40;}
    
  7. Запишем указатель на следующий элемент в списке дескрипторов.
    /** Write pointer to next pointer */DescAddr = 0x40000000;for (u16 i = 0; i < DESC_COUNT - 1; i++){Xil_Out32(DescAddr, DescAddr + 0x40);DescAddr += 0x40;}/** Write pointer for last descriptor */Xil_Out32(DescAddr, DescAddr);
    
  8. Повторим для списка дескрипторов приема.

    /** Fill descriptor to receive */for (u16 i = 0; i < DESC_COUNT; i++){RxDesc[i].NXTDESC = &RxDesc[i];RxDesc[i].NXTDESC_MSB = 0x0;RxDesc[i].BUFFER_ADDRESS = &RxBuffer[i][0];RxDesc[i].BUFFER_ADDRESS_MSB = 0x0;RxDesc[i].RESERVED0 = 0x0;RxDesc[i].RESERVED1 = 0x0;RxDesc[i].CONTROL = sizeof(RxBuffer[i]);RxDesc[i].STATUS = 0x0;RxDesc[i].APP0 = 0x0;RxDesc[i].APP1 = 0x0;RxDesc[i].APP2 = 0x0;RxDesc[i].APP3 = 0x0;RxDesc[i].APP4 = 0x0;}/** Copy receive descriptor for memory of descriptors */DescAddr = 0x40000000 + 0x4000;for (u16 i = 0; i < DESC_COUNT; i++){Xil_MemCpy(DescAddr, &RxDesc[i], sizeof(RxDesc[i]));DescAddr += 0x40;}/** Write pointer to next pointer */DescAddr = 0x40000000 + 0x4000;for (u16 i = 0; i < DESC_COUNT - 1; i++){Xil_Out32(DescAddr, DescAddr + 0x40);DescAddr += 0x40;}/** Write pointer for last descriptor */Xil_Out32(DescAddr, DescAddr); 
    
  9. Запустим DMA на передачу. DMA начинает обрабатывать данные после записи в регистр значения хвоста списка дескрипторов.

    /** Reset DMA and setup *//** MM2S */Xil_Out32(0x40400000, 0x0001dfe6);Xil_Out32(0x40400000, 0x0001dfe2);/** S2MM */Xil_Out32(0x40400030, 0x0001dfe6);Xil_Out32(0x40400030, 0x0001dfe2);/** PL => PS */Xil_Out32(0x4040003c, 0x00000000);Xil_Out32(0x40400038, 0x40004000);Xil_Out32(0x40400030, 0x0001dfe3);Xil_Out32(0x40400044, 0x00000000);Xil_Out32(0x40400040, 0x40007FC0);/** PS => PL */Xil_Out32(0x4040000C, 0x00000000);Xil_Out32(0x40400008, 0x40000000);Xil_Out32(0x40400000, 0x0001dfe3);Xil_Out32(0x40400014, 0x00000000);Xil_Out32(0x40400010, 0x40003FC0); 
    
  10. Подождем, пока будет обработан последний дескриптор на приеме и посчитаем время обработки. Конечно, можно использовать прерывания, но для тестовой задачи это излишне.

    /** Wait ready in last descriptor */while (1){status = Xil_In32(0x40003FDC);if ((status & 0x80000000) == 0x80000000){break;}else{countWait++;usleep(100);}}xil_printf("Time %x \n\r", countWait);
    

3.3 Результаты


Собираем приложение, создаем файл прошивки и заливаем в плату. Описано в предыдущей статье[1].

Запускаем, смотрим в мониторе com-порта:

Xilinx First Stage Boot LoaderRelease 2019.2  Dec 16 2020-15:11:44Silicon Version 3.1Boot mode is QSPISUCCESSFUL_HANDOFFFSBL Status = 0x1Hello WorldTime 10F

Таким образом, для обмена данными между процессорным модулем и программируемой логикой, в программируемой логике необходимо реализовать один из интерфейсов связи с процессорным модулем, где инициатором является программируемая логика. Такие интерфейсы представлены портами GP, HP, ACP. В предыдущей статье [1] они все были рассмотрены.

Посчитаем скорость передачи данных: (256 раз * 102400 байт) / (271 * 100 мкс) 967 321 033 байт/с 944 649 Кбайт/с 922 Мбайт/с.
Битовая скорость 7 738 568 264 бит/с.
Теоретическая скорость составляет 32 бита * 250 МГц = 8 000 000 000 бит/с.

Также, существует возможность хранить дескрипторы не в памяти программируемой логики, а в оперативной памяти, подключенной к процессорному модулю. В таком случае порт M_AXI_SG подключается к порту HP Zynq.

Рассмотрим первый вариант, когда для доступа DMA к данными и к дескрипторам в оперативной памяти процессорного модуля используются разные порты HP. Модифицируем прошивку в программируемой логике, чтобы получилась следующая схема:


Доступ к данными и дескрипторами через разные порты

Исходный код приложения приводить не будем. Отличия лишь в том, что дескрипторы не нужно копировать в память программируемой логики. Однако, необходимо учесть условие, чтобы адрес каждого дескриптора был выровнен на 64 байта.

После запуска приложения, в мониторе com-порта увидим, что время выполнения копирования буфера данных не поменялось, также 271 * 100 мкс.

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


Доступ к данными и дескрипторами через один и тот же порт

Исходный код приложения не поменялся относительно предыдущего варианта.
После запуска приложения, в мониторе com-порта увидим новое время выполнения операции копирования буфера: 398 * 100 мкс.

В результате, скорость обработки составит: (256 раз * 102400 байт) / (398 * 100 мкс) 658653266 байт/с 643216 Кбайт/с 628 Мбайт/с.
Битовая скорость 5269226128 бит/с.

Проект: github.com/Finnetrib/DmaTransfer

4 Заключение


В этой статье мы рассмотрели два реализации обмена данными между процессорным модулем и программируемой логикой. Режим PIO прост в реализации и позволяет получить скорость до 184 Мбайт/с, режим DMA несколько посложнее, но и скорость выше до 628 Мбайт/с.

5 Используемые источники


  1. habr.com/ru/post/508292
  2. www.xilinx.com/support/documentation/ip_documentation/axi_dma/v7_1/pg021_axi_dma.pdf
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru