Опыт использования FPGA платы DE10-Standard и DMA PL330

в 8:07, , рубрики: Cyclone V, DMA, fpga, HPS, kernel, linux, SoC

Опыт использования FPGA платы DE10-Standard и DMA PL330 - 1

Получил в свое распоряжение плату Terasic DE10-Standard. На ней много всего интересного: встроенный JTAG программатор, светодиоды, переключатели, кнопки, разъемы Audio / VGA / USB / Ethernet. Думаю, что нет особой необходимости перечислять все ее возможности, ведь каждый желающий может прочитать спецификацию платы на сайте производителя.

Для меня важно, что на плате стоит FPGA чип Cyclone V SX – 5CSXFC6D6F31C6N. Эта микросхема содержит два процессора ARM Cortex-A9 и 110K логических элементов FPGA. Это уже настоящая SoC HPS: System-On-Chip, Hard Processor System. С такими ресурсами можно пробовать делать довольно сложные проекты. Далее расскажу о своем опыте использования платы.

Очень просто скачать образ Linux с сайта терасика и подготовить загрузочную SD карту для платы DE10-Standard. ОС Linux загружается с SD, плата оживает и демонстрирует свои возможности.

Опыт использования FPGA платы DE10-Standard и DMA PL330 - 2

Как только загрузился Linux, на рабочем столе можно отыскать иконку тестового приложения. Это тестовое GUI приложение позволяет включать и выключать отдельные светодиоды платы, смотреть состояние переключателей и кнопок, устанавливать значения на 7-сегментных индикаторах и так далее. Там много чего интересного. Когда поигрался с этой программой думаешь, что вот и сам сейчас быстро сможешь сделать свой проект и быстро его запустить.

Я не считаю себя новичком-разработчиком ПЛИС. Я делал проекты с софт процессорами и представляю себе, что такое Linux kernel. Я и сам участвую в разработке плат разработчиков с ПЛИС Altera/Intel. Однако, честно говоря, это мой первый опыт работы с SoC HPS-FPGA. Когда я начал делать свой собственный проект для этой платы, именно для этой ПЛИС, я понял, что на самом деле будет не просто.

Опыт использования FPGA платы DE10-Standard и DMA PL330 - 3

Конечно, плата DE10-Standard сама по себе не виновата.
Есть некоторая иллюзия легкости разработки: вот есть образ SD карты от терасика, есть исходники примера проекта для ПЛИС и сишные исходники для тестовых программ. Казалось бы, бери адаптируй под свои нужды и будет все работать. Но, нет.

Нужно понять, что первое впечатление легкости разработки — оно обманчиво. Что видно с первого взгляда — это всего лишь вершина айсберга.

Пришлось очень много читать и изучать, например:
1. К плате прилагается диск DE10-Standard_v.1.2.0_SystemCD и он содержит 38 PDF файлов различной документации, схем и руководств.

2. Просто техническое описание от компании Интел “Cyclone V Hard Processor System Technical Reference Manual” www.altera.com/en_US/pdfs/literature/hb/cyclone-v/cv_5v4.pdf содержит 3536 страниц.

Конечно, не обязательно их все сразу читать, я думал вообще их читать не буду, обойдусь своими знаниями и опытом, но, все таки пришлось и читать и разбираться.

Есть еще хороший ресурс: https://rocketboards.org/, который содержит еще больше документации, исходников примеров и даже форум. Это делает жизнь с одной стороны легче, а с другой — еще сложнее, ведь придется читать и впитывать еще больше информации… К сожалению даже на форуме rocketboard, не всегда удается найти ответы на свои вопросы.

Таким образом, разработка проекта получается довольно сложной, потому, что сам предмет разработки SoC HPS является сложным.

Опыт использования FPGA платы DE10-Standard и DMA PL330 - 4

Представьте себе часовой механизм с множеством шестеренок. Каждая шестеренка должна в точности подходить к следующей — иначе ничего не будет крутиться. То же самое и в случае с системой HPS-FPGA. Система состоит из очень многих программных и аппаратных компонентов: Preloader, U-BOOT, Linux kernel и драйвера, DTB файл генерируется из DTS файла, затем еще нужно создать RootFS и, конечно, разработать саму аппаратную систему в ПЛИС: FPGA SoC проект будет содержать несколько IP блоков, аппаратные регистры отображаемые в память, тактовые частоты и домены, сигналы ввода/вывода и прочее и прочее…

Я предполагаю, что знаю, как создать проект для FPGA для моей SoC, и я думаю, что должно работать ну где-то на 80% так как я не вижу очевидных ошибок в проекте. Так же я думаю, что примерно знаю, как написать DTS файл, который описывает мою аппаратную платформу. Предположим, я уверен, что я написал DTS файл правильно на 80%. Из DTS файла генерируется DTB файл. Потом, к своей аппаратуре ПЛИС я должен написать kernel driver. Это не просто, но я умею писать драйвера. Надеюсь я там не наделал много ошибок? Надеюсь мой драйвер корректен ну хотя бы на 80%. Но что там насчет Preloader? Preloader – это первая программа, которая будет считана и запущена с SD карты и она должна запрограммировать нужные аппаратные конфигурационные регистры системы на кристалле. Сделал ли я Preloader правильно? Ну допустим, я уверен на 80%. Теперь если подумать, то какова вероятность, что моя система заработает? Я думаю, что где-то вот так: 0.8*0.8*0.8*0.8=0.4096… Чем больше компонентов в системе, тем хуже. Если что-то не работает или не работает вообще ничего (например, kernel panic), то довольно трудно понять где проблема — она может быть везде.

Цель моей работы была сделать проект HPS-FPGA который использует DMA транзакции для переноса данных из системной памяти в ПЛИС и обратно из ПЛИС в системную память. Использование DMA должно разгрузить процессор. На хабре уже была статья про реализацию DMA в FPGA Cyclone V, однако, мне не хотелось идти путем создания собственного контроллера, как это сделал Des333… Я хотел использовать уже имеющийся в системе контроллер PL330.

Работая с платой DE10-Standard Board некоторое время я получил бесценный опыт. Если позволите, хочу дать несколько советов тем, кто решится заняться разработкой SoC HPS в ПЛИС.

Подготовьте плату к разработке.

Вероятно это совет из разряда очевидных. Существует образ SD карты, который содержит нужные файлы для старта системы: образ FPGA, файл DTB, U-BOOT и Linux kernel zImage. Дополнительные разделы содержат Preloader и RootFS. Если я разрабатываю SoC HPS проект для ПЛИС, я его компилирую в среде САПР Quartus Prime и результат (RBF, Raw Binary File) должен записать на SD карту. Потом я компилирую Linux kernel и мой драйвер в составе ядра. Мне так же нужно записать получившиеся файлы на SD Card.

Нет никакого смысла вытаскивать SD карту из платы, вставлять в кардридер компьютера или ноутбука, чтобы записать файлы на карту. Это может занимать слишком много времени. К тому же, частый plug/unplug может привести к поломке разъема SD карты в плате или ноутбуке. Я рекомендую сконфигурировать U-BOOT таким образом, чтобы нужные файлы скачивались из сети с TFTP сервера.

Плата имеет разъем UART-to-USB для ее подключения через USB кабель к компьютеру разработчика. Открываю программу терминала, например, PUTTY, и включаю плату. Сразу видно, как в терминале побежали сообщения из U-BOOT. Загрузку можно прервать, если сразу нажать любую клавишу в терминале.

Я добавил несколько переменных в окружение U-BOOT:

ethaddr=fe:cd:12:34:56:67
ipaddr=10.8.0.97
serverip=10.8.0.36
xfpga=tftpboot 100 socfpga.rbf; fpga load 0 100 $filesize; run bridge_enable_handoff; tftpboot 100 socfpga.dtb
xload=run xfpga; tftpboot 8000 zImage; bootz 8000 – 100

На компьютере разработчика IP 10.8.0.36 я установил сервер TFTP. В папке /tftpboot я храню socfpga.rbf (Raw Binary File) – результат компиляции проекта SoC FPGA в среде Quartus Prime. Кроме того, там же в папке храню socfpga.dtb – соответствующий Device Tree Blob и Linux kernel zImage файл.

Теперь, когда я включаю питание на плате, я тут же прерываю обычную загрузку нажатием любой клавиши в терминале и ввожу команду:
>run xload

По этой команде U-BOOT загружает нужные файлы из TFTP сервера, инициализирует FPGA последним скомпилированным образом проекта и загружает мой последний zImage. Быстро и просто. Когда делаю изменение в проекте ПЛИС, компилирую проект квартусом, результат копирую в папку /tftpboot. Точно так же, компилирую ядро Linux, результат компиляции копирую в папку /ftfpboot. Перезагружаю плату, делаю “run xload” и вот уже можно пробовать вести отладку новой системы.

2. Постарайтесь найти opensource пример проекта SoC-HPS максимально похожий на то, что вы собираетесь делать.

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

Изначально DE10-Standard_v.1.2.0_SystemCD содержит два примера проектов для HPS-FPGA. Первый проект — это DE10_Standard_GHRD, представляет собой минимальные возможности, Linux console, простую отображаемую в память периферию вроде портов ввода вывода для светодиодов, кнопочек и переключателей. Второй пример, DE10_Standard_FB, — сложнее. Здесь уже в FPGA реализован framebuffer, видеоконтроллер, устройство захвата и декодирования видеосигнала и ряд других возможностей. Это позволяет запустить полноценный десктопный Linux. Если вас устраивают эти примеры — тогда все нормально, берите и пользуйтесь.

Лично мне хотелось найти пример с использованием DMA контроллера, так как я хотел разгрузить CPU во время передачи данных из системной памяти в FPGA и назад из FPGA в системную память. Я поискал такой пример и нашел его на сайте rocketboards: https://rocketboards.org/foswiki/view/Documentation/SampleDmaQuartusProjectForFpgaDmaCInTheKernel313

Пример вообще-то не очень, но хоть что-то, можно пробовать что-то сделать. Cyclone V HPS имеет встроенный DMA контроллер PL330 и я хотел бы попытаться использовать его. Я взял IP корку из проекта примера Loopback_FIFO и вставил его с помощью Quartus Prime QSYS в мой клон проекта DE10_Standard_GHRD. К сожалению, я потратил очень много времени на написание правильного DTS файла к моему проекту, DTS файла не было в архиве примера. Так же, я не сразу понял, что в Linux kernel уже есть пример DMA драйвера в arch/arm/mach-socfpga/fpga-dma.c. Я понял это слишком поздно, когда уже почти написал свой собственный драйвер.

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

3. Используйте исходники Linux kernel как документацию.

С FPGA Cyclone V HPS я разрабатываю новую аппаратную платформу. С большой вероятностью мне придется писать свой собственный драйвер к новой аппаратуре в ПЛИС. В интернете очень много статей как писать драйвера уровня ядра Linux. Но учтите, что очень многие из этих статей уже давным давно устарели, содержат не верные примеры, вызывают старый kernel API.

Если выбрать конкретную версию ядра Linux для проекта, то всю информацию о том, как писать драйвера можно почерпнуть конкретно из исходников этой версии ядра и это будет самая актуальная информация. Примеры драйверов в папке ./drivers, дествующая документация в папке ./Documentation, примеры написания *.DTS файлов в папке ./arch/arm/boot/dts

В моем случае для моего проекта с DMA контроллером документация о написании драйверов DMA получена из файлов ./Documentation/dmaengine/*.

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

Как я понял, теоретически разработка должна идти вот так:

  1. Разрабатываем аппаратную систему в среде САПР Quartus Prime QSYS, настраиваем параметры HPS, добавляем в систему компоненты и IP ядра, соединяем компоненты. Генерируем систему с помощью QSYS и получаем результат soc_system.qsys и soc_system.sopsinfo файлы.
  2. Создаем DTS файл из *.sopsinfo файла используя командную строку:
    >sopc2dts --input soc_system.sopcinfo --output socfpga.dts --board soc_system_board_info.xml --board hps_clock_info.xml
  3. Создаем DTB из DTS файла:
    >dtc -I dts -O dtb -o socfpga.dtb socfpga.dts

Я прочитал такую инструкцию на страницах рокетбоардс, но этот метод как-то не очень работает (совсем не работает). Для себя я понял, что единственно работающий метод — это вручную исправлять существующий пример DTS файла адаптируя его под свой аппаратный проект.

Как я уже писал, исходники ядра могут помочь в написании DTS файла. Я правда сам не сразу понял это, но когда понял, то дело пошло быстрее. Нужно использовать исходники ядра как документацию!

Дайвайте посмотрим пример DMA драйвера из ./driver/dma/fpga-dma.c

Драйвер вызывает API функцию platform_driver_probe() и передает в качестве аргумента указатель на структуру:

#ifdef CONFIG_OF
static const struct of_device_id fpga_dma_of_match[] = {
	{.compatible = "altr,fpga-dma",},
	{},
};

MODULE_DEVICE_TABLE(of, fpga_dma_of_match);
#endif

static struct platform_driver fpga_dma_driver = {
	.probe = fpga_dma_probe,
	.remove = fpga_dma_remove,
	.driver = {
		   .name = "fpga_dma",
		   .owner = THIS_MODULE,
		   .of_match_table = of_match_ptr(fpga_dma_of_match),
		   },
};

static int __init fpga_dma_init(void)
{
	return platform_driver_probe(&fpga_dma_driver, fpga_dma_probe);
}

Это значит, что в DTS файле должна быть соответствующая секция с совместимым именем устройства:
fpga_dma: fpga_dma@0x10033000 {
compatible = "altr,fpga-dma";

То есть, видимо функция platform_driver_probe будет просматривать DTB файл искать устройство с именем fpga-dma от производителя altr.

Если драйвер вызывает функции

csr_reg  = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
data_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "data");

то это значит, что DTS файл должен содержать именованные регистры с такими же точно именами “csr” и “data”. В противном случае драйвер не сможет стартовать.

Точно так же, драйвер ядра может запрашивать каналы DMA по имени:

static int fpga_dma_dma_init(struct fpga_dma_pdata *pdata)
{
	struct platform_device *pdev = pdata->pdev;

	pdata->txchan = dma_request_slave_channel(&pdev->dev, "tx");
	if (pdata->txchan)
		dev_dbg(&pdev->dev, "TX channel %s %d selectedn",
			dma_chan_name(pdata->txchan), pdata->txchan->chan_id);
	else
		dev_err(&pdev->dev, "could not get TX dma channeln");

	pdata->rxchan = dma_request_slave_channel(&pdev->dev, "rx");
	if (pdata->rxchan)

А вот соответствующий фрагмент DTS файла, который отображает кореляцию исходников драйвера ядра и DTS файла:

fpga_dma: fpga_dma@0x10033000 {
compatible = "altr,fpga-dma";
reg = <0x00000001 0x00033000 0x00000020>,
<0x00000000 0x00034000 0x00000010>;
reg-names = "csr", "data";
dmas = <&hps_0_dma 0 &hps_0_dma 1>;
dma-names = "rx", "tx";

Таким образом, DTS файл должен быть написан с учетом того, как драйвер запрашивает из него ресурсы. Если используются именованные регистры и канала DMA, то имена должны совпадать и в исходнике ядра и в DTS файле. Только так две шестереночки системы: драйвер ядра и DTS/DTB могут работать вместе.

4. Помните, что ваши исходники могут быть не самыми свежими.

Мне нужен был компилятор и исходники Linux kernel, чтобы я смог начать разрабатывать свой драйвер и компилировать ядро для моей FPGA системы. Именно поэтому я выкачал последний (на тот момент) Intel SoC FPGA Embedded Development Suite v17.0 и установил его.

После полной установки я увидел новую папку ~/intelFPGA/17.0/embedded/embeddedsw/sources, где лежал скрипт git_clone.sh. Я запустил этот скрипт и получил исходники ядра вот здесь ~/intelFPGA/17.0/embedded/embeddedsw/sources/linux-sources.

Git branch получился вот такой: sockfpga-4.1.22-ltsi-16.1-release. Версия ядра 4.1.22 — ну пусть.

Я принял версию 4.1.22 как данность и начал работать с этой веткой над этими исходниками. Я собрал ядро и нашел, что есть драйвер DMA, называемый fpga-dma, и этот драйвер в общем-то работает с моим LoopbackFIFO IP core в моем FPGA проекте. Однако, я заметил, что производительность по передаче данных из памяти системы в FPGA и назад очень мала — передача осуществляется одиночными передачами, по одному слову за несколько тактов. Я перепроверил мой FPGA проект сотню раз, и я перепроверил fpga-dma.c драйвер сотню раз, но я так и не мог понять почему на шине не происходит burst transfers. Я уже было начал разбираться с исходниками самого драйвера DMA контроллера PL330. Так же, мне пришлось таки читать Cyclone V Hard Processor System Technical Reference Manual про HPS PL330 DMA контроллер. Этот DMA контроллер очень сложный, у него у самого есть свой набор инструкций, к нему нужно писать свою программу. Программа на языке ассемблера для PL330 DMA контроллера может выглядеть вот так:

DMAMOV CCR, SB4 SS64 DB4 DS64
DMAMOV SAR, 0x1000
DMAMOV DAR, 0x4000
DMALP 16
DMALD
DMAST
DMALPEND
DMAEND

В результате всех моих изыскания я понял, что драйвер ./drivers/dma/pl330.c никогда не инициализирует регистр CCR контроллера DMA для burst передачи. Я не понимал, что же мне делать, но позже я обнаружил, что более новые версии ядра уже содержат исправление этого недоразумения: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=848e9776fee424b9368c72377de5d3509b17937c

Я вручную добавил патч на исходники DMA драйвера и получил burst transfers! Вот здесь снимок с экрана SignalTap, где я захватываю DMA Mem-to-Device transfer:

Опыт использования FPGA платы DE10-Standard и DMA PL330 - 5

Таким образом, если однажды вы столкнетесь с технической проблемой, которую не знаете, как решить, перепроверьте: вдруг к вашей проблеме уже есть исправление в более свежих исходниках ядра Linux? Какя понял, проблема с блочной передачей DMA контроллера PL330 решена в ядре 4.6.

5. Внимательно относитесь к отдельным деталям системы.

Конечно, разработка FPGA SoC системы требует специфических знаний. Сейчас я не хочу касаться особенностей и методов разработки IP ядер или синтаксиса Verilog / VHDL. Конечно, разработчик должен многое знать. Однако, я хочу обратить внимание, что заставить все части системы работать вместе — это не очень простая задача. Слишком много шестеренок, которые должны синхронно вращаться.

Попробую привести пример и моей практики.
Я пытался заставить работать драйвер контроллера PL330 DMA с моим IP core. Я встретил такую проблему: операции записи в устройство проходят успешно, а вот операции чтения всегда завершаются таймаутом. Я пытался найти решение в интернете и видел, что многие разработчики так же спрашивают про эту проблему, но решения нет. В системном логе я вижу сообщение из драйвера fpga-dma “Timeout waiting for RX DMA!”. Но в чем проблема? — не понятно. Почему с TX передачей все нормально, а с RX передачей — нет? Я поменял местами каналы RX и TX в FPGA проекте, и получил наоборот “Timeout waiting for TX DMA!”. Что же не правильного в моем втором канале DMA?

Я использую Quartus Prime Qsys для редактирования моей SoC. Один из наиболее важных компонентов системы SoC это hps_0, “Arria V / Cyclone V Hard Processor System”. Я отредактировал свойства этого компонента и убедился, что оба канала DMA у меня включены, и RX и TX:

Опыт использования FPGA платы DE10-Standard и DMA PL330 - 6

Достаточно ли этого? На самом деле, конечно, нет! Qsys генерирует компоненты системы soc_system для Quartus Prime, но так же он создает программные компоненты в папке ./hps_isw_handoff/soc_system_hps_0.

Тут есть файл hps.xml в котором видно следующее:

<hps>
  <system>
    <config name='DEVICE_FAMILY' value='Cyclone V' />
    <config name='DMA_Enable' value='Yes Yes No No No No No No' />
    <config name='dbctrl_stayosc1' value='true' />
    <config name='main_pll_m' value='73' />
    <config name='main_pll_n' value='0' />
    <config name='main_pll_c0_internal' value='1' />

Это значит, что позже я должен сгенерировать компонент Preloader, и для его компиляции используется этот XML файл. Скомпилированный Preloader должен быть записан в специальный раздел SD карты. Когда стартует система стартует и Preloader. Именно он включает все необходимые компоненты системы делая нужные записи в специальные аппаратные регистры.

Регистры Cyclone V HPS Reset Manager расположены по физическому адресу 0xFFD05000 (Cyclone V Hard Processor System Technical Reference Manual). Некоторые биты в регистрах Reset Manager, должны быть сброшены, чтобы включить DMA на отдельных каналах.

Ну хорошо. Я изменяю свойства компонента hps_0 в Qsys и теперь я знаю, что вероятно я должен перекомпилировать Preloader и записать его на SD.

Но и это еще не вся история!
Если я использую два канала DMA, то мне нужно и два прерывания для этих двух каналов и их еще нужно вручную объявить в DTS файле.
hps_0_dma: dma@0xffe01000 {
compatible = "arm,pl330-16.1", "arm,pl330", "arm,primecell";
reg = <0xffe01000 0x00001000>;
interrupt-parent = <&hps_0_arm_gic_0>;
interrupts = <0 104 4>, <0 105 4>;
clocks = <&l4_main_clk>;

Откуда такие странные числа 104 и 105?
Пришлось читать Cyclone V HPS Reference Manual. Я вижу, что Generic Interrupt Controller имеет зарезервированные линии запросов DMA IRQ 136 and 137:

Опыт использования FPGA платы DE10-Standard и DMA PL330 - 7

Однако, почему-то нумерация начинается “32”. Таким образом я решаю, что 136-32=104 и 137-32=105 — это правильные числа. Вот такие магические подсчеты дают правильные значения для DTS файла в секции interrupts. Без объявления второго IRQs для PL330 в DTS файле второй канал DMA всегда получал ошибку таймаута в драйвере ядра… Получается, что я изменяю в Qsys свойства HPS и из-за этого мне может потребоваться одновременное изменение и Preloader и DTS файла — и об этом нужно все время помнить.

Заключение.

У меня был исходный проект с примером DMA проекта, который я нашел на страницах сайта rocketboard. Однако, я адаптировал его и заставил работать на DE10-Standard board и с ядром Linux 4.1.
Вероятно, это не очень большое достижение, однако:

  1. Я написал DTS файл, которого не было в исходном проекте. Это было довольно трудно.
  2. Понял, что требуется делать патч ядра, чтобы получить блочную передачу (burst transfer).
  3. Подключил анализатор SignalTap к FPGA проекту и теперь могу видеть сигналы на шине в момент DMA передачи
  4. Научился писать DMA драйвер ядра
  5. Надеюсь, что понял весь road-map разработчика для Cyclone V HPS

Если кто-то захочет поэкспериментировать с DMA в SoC я рекомендую начинать эксперименты с альтеровского драйвера fpga-dma. Он использует DebugFS, это позволяет использовать прямо в консоли терминала простые команды “cat”, “echo” для выполнения транзакций в DMA канале:

Опыт использования FPGA платы DE10-Standard и DMA PL330 - 8

Надеюсь эта статья окажется полезной тем, кто только начинает работы с FPGA SoC HPS Cyclone V.

Посмотреть исходники проекта можно здесь: https://github.com/marsohod4you/DE10_Standard_GHRD_w_FIFO

Автор: nckma

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js