Коллеги, приветствую, обзорная статья работы с Corgi SoM v1 на базе AMD (Xilinx) Zynq-7000. Статья охватывает работу модуля с Vivado IDE, Vitis IDE, подготовку базового образа, работу с Embedded Linux (u-boot, device tree, linux kernel, rootfs). Подготовку образа для SPI NOR памяти, SPI NAND памяти и сопутствующие моменты необходимые для старта работы с данным модулем. Дополнительно статья будет полезно для linux embedded разработчиков, которые делать будут bring-up плат на базе AMD(Xilinx) zynq платформ.
1. Описание Corgi SoM
Процессорный модуль Corgi SoM v1 на базе AMD Zynq-7000 (xc7z010, xc7z020) разработан белорусской компанией ШураКор (ShuraCore), модуль подходит для задач: робототехники, создание электроприводов/сервоприводов для управления электродвигателями/серводвигателями, создания силовой электроники DC/DC, AC/DC, DC/AC, разработки ПЛК (программируемых логических контроллеров), активных фильтров, систем сбора данных, I/O систем, системы машинного зрения, аудиосистем, а также находит применение в анализаторах качества сети и других промышленных решениях и не только.
Процессорный модуль представлен в одном температурном варианте: для промышленного использования. На модуле расположен один из вариантов AMD Zynq 7010/7020 в корпусе clg400, DDR3 память 512 МБайт, SPI NOR память, SPI NAND память, 4 физики Ethernet 10/100/1000 Мбит. Установлены 2 физики на PL части и 2 физики на PS части. На PL физиках возможно использование и реализация EtherCAT slave, а также реализации и поддержка других промышленных протоколов: PROFINET, EtherNet/IP, CC-Link, POWERLINK.


Модуль спроектирован с учетом использования либо полностью западных компонентов, либо полностью китайских компонентов.
Западный вариант SoM модуля и основная компонентная база:
Xilinx SoC Zynq-7010 (XC7Z010-2clg400i)/Xilinx SoC Zynq-7020 (XC7Z020-2clg400i)
Texas Instruments TPS51200DRCR Sink and Source DDR Termination Regulator
Texas Instruments TPS62085RLTR 3-A Step-Down Converter With Hiccup Short-Circuit Protection
Winbond Electronics W25M02GWZEIG Flash
Macronix International MX66L51235FZ2I-10G Flash
Alliance Memory AS4C256M16D3LC-12BCN DDR3
Realtek RTL8211FI-CG 10/100/1000 Ethernet
Восточный вариант SoM модуля и основная компонентная база:
Fudan Microelectronics fmql10s400/Fudan Microelectronics fmql20s400
SG Micro SGM2054XTD10G Sink and Source DDR Termination Regulator
SG Micro SGM61030BXTEP7G 3-A Step-Down Converter With Hiccup Short-Circuit Protection
GigaDevice GD5F2GQ5REYJG Flash
GigaDevice GD25B512MEYIG Flash
UniIC Semiconductors SCB13H4G160AF-13KI DDR3
Motorcomm YT8531H 10/100/1000 Ethernet
2. Vivado IDE
Базовый проект подготовлен для модуля Corgi SoM v1 в Vivado 2023.1. Проект Vivado можно скачать с github. Для запуска проекта, необходимо выполнить tcl команду, предварительно перейдя в директорию проекта, source ./base_corgi_som.tcl.

После выполнения команды source у вас должен появиться следующий блок дизайна, как на картинке 2.1.

На рисунке 2.2. показано базовое адресное пространство, которое используется по умолчанию для SoM.
Чтобы проект был полноценным необходимо в него добавить файл constraints.xdc. В файле constraints.xdc располагается распиновка для XC7Z020-2CLG400I привязка конкретного ввода/вывода модуля. Добавление файла показана на рисунке 2.3, рисунке 2.4.


Чтобы проект был готовым для синтеза и имплементации нужно добавить HDL wrapper, как на рисунке 2.5.

После подготовки проекта запустите процесс Synthesis → Implementation → Generate Bitstream. Это финальный шаг для получения прошивки (bitstream) FPGA (PL части SoC).

Для дальнейшей разработки в Vitis IDE необходимо:
выполнить экспорт аппаратной платформы;
включить в экспорт сгенерированный bitstream.
После этого можно переходить к разработке и отладке в Vitis IDE.
3. Vitis IDE
Для подготовки образа c Embedded Linux для платформы Xilinx Zynq 7020 от Vitis IDE нам нужно сформировать FSBL и сгенерировать Device Tree. Так как по умолчанию у нас нет Device Tree, поэтому первоначальным шагом работы с Vitis IDE будет добавление каталога работы с генератором Device Tree. Репозиторий для генератора можно скачать с github. Перейдите в Preferences → Vitis → Software Repositories → Global Repositories и добавьте свой путь к скаченному каталогу генератора Device Tree, как на рисунке 3.1 (показан путь к установленному на дополнительный внешний SSD диск IDE от Xilinx, для более комфортной работы с версионностью и значительным размером занимаемого места на диске данной IDE).

После добавления пути дерева устройств, переходим к созданию FSBL проекта. Вам необходимо выбрать создать новый проект, как показано на рисунке 3.2-3.6.





После успешной генерации проекта из шаблона, осталось только построить бинарный файл для FSBL. На текущем этапе работа с FSBL завершается и следующим шагом работы с Vitis IDE будет создание Device Tree для Corgi SoM. Для этого нужно выбрать создать новый платформенный проект и выбрать Device Tree в качестве ОС как это показано рисунках 3.7.-3.8.


Редактированием сгенерированного дерева устройства для целевой платформы мы займемся в отдельном разделе про Device Tree.
4. Описание структуры разделов QSPI Flash
В данном разделе опишем структуру разделов QSPI Flash памяти. Конфигурация определяет разметку памяти QSPI Flash для встраиваемой системы Corgi SoM на базе Xilinx Zynq 7020. Разделы содержат ключевые компоненты для загрузки ОС Linux и работы системы.
1. Раздел qspi-fsbl-uboot
Адрес: 0x00000000
Размер: 8 МБ (0x800000)
-
Назначение:
FSBL (First Stage Boot Loader) – первичный загрузчик, инициализирующий аппаратную часть.
Bitstream – прошивка (конфигурация) PL(FPGA) части SoC Zynq 7020.
U-Boot – основной загрузчик, обеспечивающий запуск ядра Linux.
Примечание - этот раздел критически важен для старта системы и должен быть защищен от случайной перезаписи.
2. Раздел qspi-linux
Адрес: 0x00800000
Размер: 10 МБ (0xA00000)
-
Назначение:
Ядро Linux (Image / uImage) – исполняемый образ операционной системы.
Размер раздела позволяет хранить ядро с дополнительными драйверами и модулями.
3. Раздел qspi-device-tree
Адрес: 0x01200000
Размер: 256 КБ (0x40000)
-
Назначение:
Device Tree Blob (DTB) – описание аппаратной конфигурации системы.
DTB определяет, какие устройства доступны в Linux.
4. Раздел qspi-rootfs
Адрес: 0x01240000
Размер: 13 МБ 768 КБ (0xDC0000)
-
Назначение:
Корневая файловая система (rootfs.cpio.uboot) – сжатый архив, содержащий /bin, /lib, /etc и другие системные каталоги.
Формат cpio.uboot оптимизирован для загрузки через U-Boot.
Сводная таблица по разделам
Раздел |
Адрес начала |
Размер |
Содержимое |
qspi-fsbl-uboot |
0x00000000 |
8 МБ |
FSBL, Bitstream, U-Boot |
qspi-linux |
0x00800000 |
10 МБ |
Ядро Linux |
qspi-device-tree |
0x01200000 |
256 КБ |
Device Tree (DTB) |
qspi-rootfs |
0x01240000 |
13 МБ 768 КБ |
Корневая ФС (cpio.uboot) |
Примечание
Общий объем памяти: ~32 МБ (сумма всех разделов).
5. Настройка минимального FSBL для целей программирования флеш-памяти
В рамках проектов на базе Zynq 7000 часто возникает необходимость использования FSBL (First Stage Boot Loader) исключительно для инициализации подсистемы PS (Processing System - процессорная часть SoC), например, при программировании внешней флеш-памяти через Vitis IDE (наш случай).
В стандартной реализации FSBL, при использовании режима загрузки с QSPI, загрузчик пытается автоматически считать и загрузить все разделы, определенные в образе.
Чтобы сформировать необходимую последовательность загрузки необходимо создать специализированную версию FSBL, задача которой — исключительно выполнить процедуру аппаратной инициализации, в частности, вызвать функцию ps7_init(), не выполняя при этом загрузку других разделов из памяти.
Для этого следует:
Создать новый проект FSBL в среде разработки Vitis IDE.
Внести изменения в исходный файл main.c, удалив или закомментировав всю логику, связанную с обработкой и загрузкой разделов.
Оставить только базовую инициализацию, включая вызов ps7_init() и, при необходимости, базовую отладочную информацию.
Такой минималистичный FSBL можно использовать как вспомогательный загрузчик при программировании QSPI флеш, исключая любые попытки загрузки лишнего содержимого, что обеспечивает стабильность и предсказуемость процесса.
Этот подход особенно полезен при ручной работе с образами флеш-памяти, отладке загрузочной цепочки, либо при создании пользовательских процедур инициализации на низком уровне.
Для этого модифицируемый вариант загрузки, выбрав загрузку через JTAG и предварительно нужно инициализировать QSPI.
/*
* Read bootmode register
*/
BootModeRegister = Xil_In32(BOOT_MODE_REG);
BootModeRegister &= BOOT_MODES_MASK;
//add this line to trick boot mode to JTAG
BootModeRegister = JTAG_MODE;
fsbl_printf(DEBUG_GENERAL,"Set JTAG mode to manual\n\r");
InitQspi();
fsbl_printf(DEBUG_INFO,"QSPI Init Done \r\n");

Только эти изменения нам нужны для Corgi SoM.

Для вывода отладочной информации необходимо использовать FSBL_DEBUG, FSBL_DEBUG_INFO.
FSBL_DEBUG
Установите этот флаг для включения базового режима отладки в FSBL. При активации этого флага FSBL начнёт выводить диагностические сообщения и события выполнения, такие как запуск и завершение этапов инициализации, статусы загрузки и ошибки. Это позволяет отслеживать общий процесс загрузки и помогает в базовой диагностике.
FSBL_DEBUG_INFO
Этот флаг активирует расширенный режим отладки. При его установке FSBL дополнительно выводит подробную отладочную информацию: дампы регистров, содержимое заголовков разделов, параметры загрузки и другие внутренние данные.
Загрузка файла во FLASH
Загрузка осуществляется с помощью подготовленных bin файлов с u-boot zynq_qspi_x4_single.bin/zynq_qspi_x1_single.bin расположенные (путь)/xilinx/2023.1/Vitis/2023.1/data/xicom/cfgmem/uboot.
Загрузить bin файл можно также с инструмента program_flash. Пример команды выполнения.
program_flash -f /home/mcuby/corgi_bin/eth/BOOT.bin -offset 0 -flash_type qspi-x4-single -fsbl /home/mcuby/corgi_bin/fsbl_info_debug.elf -url TCP:127.0.0.1:3121 -target_id 3
Момент запуска, custom FSBL для загрузки образа через JTAG показан ниже.
Xilinx First Stage Boot Loader
Release 2023.1 Sep 9 2025-14:11:37
Devcfg driver initialized
Silicon Version 3.1
Set JTAG mode to manual
Single Flash Information
FlashID=0xC2 0x20 0x1A
MACRONIX 512M Bits
QSPI is in single flash connection
QSPI is in 4-bit mode
QSPI Init Done
Boot mode is JTAG
U-Boot 2023.01-g8624651 (Mar 14 2023 - 23:15:54 -0600)
Model: Zynq CSE QSPI SINGLE Board
DRAM: 256 KiB
WARNING: Caches not enabled
Core: 8 devices, 7 uclasses, devicetree: embed
Loading Environment from <NULL>... OK
In: dcc
Out: dcc
Err: dcc
Zynq> sf probe 0 0 0
SF: Detected mx66l51235l with page size 256 Bytes, erase size 64 KiB, total 64 MiB
Zynq> Sector size = 65536.
f probe 0 0 0
Performing Erase Operation...
sf erase 0 7D0000
….
device 0 offset 0x7b0000, size 0x10000
SF: 65536 bytes @ 0x7b0000 Written: OK
Zynq> 100%
sf write FFFC0000 7C0000 57C
device 0 offset 0x7c0000, size 0x57c
SF: 1404 bytes @ 0x7c0000 Written: OK
Zynq> Program Operation successful.
INFO: [Xicom 50-44] Elapsed time = 288 sec.
6. Buildroot
Buildroot - это инструмент для сборки Embedded Linux систем. Buildroot автоматизирует процесс построения пользовательского дистрибутива на основе кросс-компиляции. Ключевые преимущества для быстрого старта работы с этим инструментом - это простота и минимальная конфигурация, единый инструмент для настройки всего (целевой архитектуры, инструментов компиляции, ядра, базовой системы и пакетов). Автоматизация сборки: скачивает все исходные коды, применяет патчи, собирает тулчейн, корневую файловую систему и образ ядра Linux. Высокая скорость первой сборки, генерирует рабочую систему за небольшое количество времени. Интеграция с популярными библиотеками: поддерживает множество готовых пакетов (библиотек и утилит), которые можно легко добавить в систему. Создание готовых образов, которые можно сразу записать на носитель и загрузить целевое устройство.
Базовая конфигурация которая используется в проекте - corgi_som_v1_defconfig, данную конфигурацию можно скачать с github проекта. Скачайте данный конфиг и помещаем в ваш buildroot проект (/buildroot/configs/). Для сборки проекта необходимо выполнить команду make corgi_som_v1_defconfig. Внимание, рекомендуется дочитать до конца статью и применить изменения с добавлением необходимого Device Tree. Далее перейдем к непосредственной настройки Device Tree (dts, настройка интерфейсов целевой платформы), U-Boot (загрузчик), Linux Kernel (ядро) и rootfs (корневая файловая система). Рекомендуется использовать команду make source, команда позволяет скачать исходники без непосредственного сбора файлов, до момента полной конфигурации системы, а также релизные версии Buildroot.
7. Device Tree
В разделе Vitis IDE мы создали файлы необходимые для Device Tree. В этом разделе опишем, какие узлы мы настраиваем, какие значения задаем для устройств.
Узел SPI1 используется для работы NAND SPI памяти, сформированное описание задает разделы работы для нашей UBIFS. Память разделена на 4 области, 16/80/80/80 MB соответственно. Предполагается использовать раздел "nand-reserved” в качестве резервной области под пользовательские задачи, раздел “ubia” как основной раздел, раздел “ubib” как дополнительный раздел. Раздел “nand-recovery” может использоваться, как раздел восстановления образа с Linux.
Узел QSPI был описан ранее в разделе “описание структуры разделов QSPI Flash”.
Узел gpio0, can0, can1, i2c0, i2c1, uart0, uart1, sdhci0 является базовыми узлами процессорной периферии и реализует конфигурацию работы с GPIO, CAN, I2C, UART соответственно.
Работа с 4-мя физиками (приемопередатчиками) Ethernet и конфигурация MAC-ов описаны в узлах gem0, gem1, axi_ethernet_0, axi_ethernet_1. Gigabit Ethernet MAC (GEM) узлы определяют работу PS (CPU) подключенных физик, а AXI Ethernet подсистема определяют работу PL (FPGA) части Zynq. Для них мы задаем MAC адрес отличный на единицу в номере.
Данный Device Tree является одинаковым как для U-Boot, так и для Linux. Далее показано полное описание файл dts для Cori SoM v1.
/dts-v1/;
#include "zynq-7000.dtsi"
#include <dt-bindings/gpio/gpio.h>
/ {
model = "Xilinx ZC702 board";
compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000";
aliases {
ethernet0 = &gem0;
ethernet1 = &gem1;
ethernet2 = &axi_ethernet_0;
ethernet3 = &axi_ethernet_1;
i2c0 = &i2c0;
i2c1 = &i2c1;
serial0 = &uart0;
serial1 = &uart1;
spi0 = &qspi;
spi1 = &spi1;
};
memory@0 {
device_type = "memory";
reg = <0x0 0x20000000>;
};
chosen {
bootargs = "console=ttyPS0,115200 root=/dev/ram rw earlyprintk";
stdout-path = "serial0:115200n8";
};
amba_pl: amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_ethernet_0: ethernet@41000000 {
axistream-connected = <&axi_ethernet_0_dma>;
axistream-control-connected = <&axi_ethernet_0_dma>;
clock-frequency = <100000000>;
clock-names = "s_axi_lite_clk", "axis_clk", "gtx_clk", "ref_clk";
clocks = <&clkc 15>, <&clkc 15>, <&clkc 16>, <&clkc 17>;
compatible = "xlnx,axi-ethernet-7.2", "xlnx,axi-ethernet-1.00.a";
device_type = "network";
interrupt-names = "mac_irq", "interrupt", "mm2s_introut", "s2mm_introut";
interrupt-parent = <&intc>;
interrupts = <0 33 1 0 34 4 0 35 4 0 36 4>;
local-mac-address = [00 0a 35 00 00 02];
phy-mode = "rgmii";
reg = <0x41000000 0x40000>;
xlnx = <0x0>;
xlnx,axiliteclkrate = <0x0>;
xlnx,axisclkrate = <0x0>;
xlnx,channel-ids = "1";
xlnx,clockselection = <0x0>;
xlnx,enableasyncsgmii = <0x0>;
xlnx,gt-type = <0x0>;
xlnx,gtinex = <0x0>;
xlnx,gtlocation = <0x0>;
xlnx,gtrefclksrc = <0x0>;
xlnx,include-dre ;
xlnx,instantiatebitslice0 = <0x0>;
xlnx,num-queues = /bits/ 16 <0x1>;
xlnx,phyaddr = <0x1>;
xlnx,phyrst-board-interface-dummy-port = <0x0>;
xlnx,rable = <0x0>;
xlnx,rxcsum = <0x0>;
xlnx,rxlane0-placement = <0x0>;
xlnx,rxlane1-placement = <0x0>;
xlnx,rxmem = <0x1000>;
xlnx,rxnibblebitslice0used = <0x0>;
xlnx,tx-in-upper-nibble = <0x1>;
xlnx,txcsum = <0x0>;
xlnx,txlane0-placement = <0x0>;
xlnx,txlane1-placement = <0x0>;
xlnx,versal-gt-board-flow = <0x0>;
zclock-names = "NULL";
zclocks = "NULL";
phy-handle = <&phy2>;
axi_ethernet_0_mdio: mdio {
#address-cells = <1>;
#size-cells = <0>;
phy2: phy@3 {
device_type = "ethernet-phy";
reg = <3>;
};
};
};
axi_ethernet_0_dma: dma@40400000 {
#dma-cells = <1>;
axistream-connected = <&axi_ethernet_0>;
axistream-control-connected = <&axi_ethernet_0>;
clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>;
compatible = "xlnx,eth-dma";
interrupt-names = "mm2s_introut", "s2mm_introut";
interrupt-parent = <&intc>;
interrupts = <0 35 4 0 36 4>;
reg = <0x40400000 0x10000>;
xlnx,addrwidth = /bits/ 8 <0x20>;
xlnx,include-dre ;
xlnx,num-queues = /bits/ 16 <0x1>;
};
axi_ethernet_1: ethernet@41040000 {
axistream-connected = <&axi_ethernet_1_dma>;
axistream-control-connected = <&axi_ethernet_1_dma>;
clock-frequency = <100000000>;
clock-names = "s_axi_lite_clk", "axis_clk", "gtx_clk", "gtx_clk90";
clocks = <&clkc 15>, <&clkc 15>, <&misc_clk_0>, <&misc_clk_0>;
compatible = "xlnx,axi-ethernet-7.2", "xlnx,axi-ethernet-1.00.a";
device_type = "network";
interrupt-names = "mac_irq", "interrupt", "mm2s_introut", "s2mm_introut";
interrupt-parent = <&intc>;
interrupts = <0 52 1 0 53 4 0 54 4 0 55 4>;
local-mac-address = [00 0a 35 00 00 03];
phy-mode = "rgmii";
reg = <0x41040000 0x40000>;
xlnx = <0x0>;
xlnx,axiliteclkrate = <0x0>;
xlnx,axisclkrate = <0x0>;
xlnx,channel-ids = "1";
xlnx,clockselection = <0x0>;
xlnx,enableasyncsgmii = <0x0>;
xlnx,gt-type = <0x0>;
xlnx,gtinex = <0x0>;
xlnx,gtlocation = <0x0>;
xlnx,gtrefclksrc = <0x0>;
xlnx,include-dre ;
xlnx,instantiatebitslice0 = <0x0>;
xlnx,num-queues = /bits/ 16 <0x1>;
xlnx,phyaddr = <0x1>;
xlnx,phyrst-board-interface-dummy-port = <0x0>;
xlnx,rable = <0x0>;
xlnx,rxcsum = <0x0>;
xlnx,rxlane0-placement = <0x0>;
xlnx,rxlane1-placement = <0x0>;
xlnx,rxmem = <0x1000>;
xlnx,rxnibblebitslice0used = <0x0>;
xlnx,tx-in-upper-nibble = <0x1>;
xlnx,txcsum = <0x0>;
xlnx,txlane0-placement = <0x0>;
xlnx,txlane1-placement = <0x0>;
xlnx,versal-gt-board-flow = <0x0>;
zclock-names = "NULL";
zclocks = "NULL";
phy-handle = <&phy3>;
axi_ethernet_1_mdio: mdio {
#address-cells = <1>;
#size-cells = <0>;
phy3: phy@1 {
device_type = "ethernet-phy";
reg = <1>;
};
};
};
misc_clk_0: misc_clk_0 {
#clock-cells = <0>;
clock-frequency = <125000000>;
compatible = "fixed-clock";
};
axi_ethernet_1_dma: dma@40410000 {
#dma-cells = <1>;
axistream-connected = <&axi_ethernet_1>;
axistream-control-connected = <&axi_ethernet_1>;
clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>;
compatible = "xlnx,eth-dma";
interrupt-names = "mm2s_introut", "s2mm_introut";
interrupt-parent = <&intc>;
interrupts = <0 54 4 0 55 4>;
reg = <0x40410000 0x10000>;
xlnx,addrwidth = /bits/ 8 <0x20>;
xlnx,include-dre ;
xlnx,num-queues = /bits/ 16 <0x1>;
};
axi_gpio_0: gpio@41200000 {
#gpio-cells = <2>;
#interrupt-cells = <2>;
clock-names = "s_axi_aclk";
clocks = <&clkc 15>;
compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";
gpio-controller ;
interrupt-controller ;
interrupt-names = "ip2intc_irpt";
interrupt-parent = <&intc>;
interrupts = <0 56 4>;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x1>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x1>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0xFFFFFFFF>;
xlnx,gpio-width = <0x2>;
xlnx,gpio2-width = <0x4>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x1>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
};
};
&gem0 {
phy-mode = "rgmii-id";
status = "okay";
phy-handle = <&phy0>;
local-mac-address = [00 0a 35 00 00 00];
phy0: phy@1 {
reg = <1>;
device_type = "ethernet-phy";
};
};
&gem1 {
phy-mode = "rgmii-id";
phy-handle = <&phy1>;
status = "okay";
local-mac-address = [00 0a 35 00 00 01];
phy1: phy@1 {
reg = <1>;
device_type = "ethernet-phy";
};
};
&gpio0 {
emio-gpio-width = <64>;
gpio-mask-high = <0x0>;
gpio-mask-low = <0x5600>;
};
&can0 {
status = "okay";
};
&can1 {
status = "okay";
};
&i2c0 {
clock-frequency = <400000>;
status = "okay";
};
&i2c1 {
clock-frequency = <400000>;
status = "okay";
};
&uart0 {
cts-override ;
device_type = "serial";
port-number = <0>;
status = "okay";
};
&uart1 {
cts-override ;
device_type = "serial";
port-number = <1>;
status = "okay";
};
&intc {
num_cpus = <2>;
num_interrupts = <96>;
};
&qspi {
u-boot,dm-pre-reloc;
is-dual = <0>;
num-cs = <1>;
status = "okay";
nor: flash@0 {
spi-tx-bus-width = <4>;
spi-rx-bus-width = <4>;
spi-max-frequency = <50000000>;
/* MX66L51235F, 512M-bit = 32M-Byte */
compatible = "jedec,spi-nor";
reg = <0>;
#address-cells = <1>;
#size-cells = <1>;
/* 8MB, fsbl, bitstream, u-boot*/
partition@0 {
label = "qspi-fsbl-uboot";
reg = <0x0 0x800000>;
};
/* 10MB, linux*/
partition@800000 {
label = "qspi-linux";
reg = <0x800000 0xA00000>;
};
/* 256KB, device-tree */
partition@1200000 {
label = "qspi-device-tree";
reg = <0x1200000 0x40000>;
};
/* 13MB 768KB, rootfs.cpio.uboot */
partition@1240000 {
label = "qspi-rootfs";
reg = <0x1240000 0xDC0000>;
};
};
};
&spi1 {
is-decoded-cs = <0>;
num-cs = <1>;
status = "okay";
nand: flash@0 {
status = "okay";
compatible = "spi-nand";
reg = <0>;
spi-max-frequency = <50000000>;
/* W25M02GW, 2x1G-bit = 2x128M-Byte */
#address-cells = <1>;
#size-cells = <1>;
/* 16MB */
partition@0 {
label = "nand-reserved";
reg = <0 0x1000000>;
};
/* recovery / install environment, 80MB */
partition@1000000 {
label = "nand-recovery";
reg = <0x1000000 0x5000000>;
};
/* ubia (first image) - a/b names to prevent confusion with ubi0/1/etc, 80MB */
partition@6000000 {
label = "ubia";
reg = <0x6000000 0x5000000>;
};
/* ubib (second image), 80MB */
partition@B000000 {
label = "ubib";
reg = <0xB000000 0x5000000>;
};
};
};
&sdhci0 {
status = "okay";
xlnx,has-cd = <0x0>;
xlnx,has-power = <0x0>;
xlnx,has-wp = <0x0>;
};
&clkc {
fclk-enable = <0x7>;
ps-clk-frequency = <33333333>;
};
8. U-Boot
Изменения в U-Boot коснуться поддержки RGMII, AXI Ethernet, для поддержки AXI Ethernet и RGMII необходимо перейти в Device Drivers > Network device support и включить поддержку драйверов Enable RGMII, Xilinx AXI Ethernet.
Для работы именно с U-Boot, в инфраструктуре Buildroot существует команда:
make uboot-menuconfig


Важным моментом корректности, в будущем старта образа, будут адреса смещения dtb и скрипта загрузки (boot.src). Задайте адреса смещения, как на рисунке 8.2. Boot.src используется для автоматизации загрузки.
CONFIG_XILINX_OF_BOARD_DTB_ADDR=0x1200000 CONFIG_BOOT_SCRIPT_OFFSET=0x7c0000
Для сборки пакета U-Boot в Buildroot можно использовать команды make uboot-build и make uboot-rebuild. Первая команда выполняет сборку только тех частей, которые считаются «устаревшими», тогда как вторая игнорирует эту проверку и всегда запускает полный процесс пересборки компонента U-Boot.
9. Linux Kernel
Изменения в Linux коснуться поддержки UBIFS и поддержки драйвера NAND SPI (W25M02GWZEIG). Далее по тексту раскрывается подробности того, какие изменения были сделаны относительно отладочной платы zc702, которая была выбрана за базу настроек Linux Kernel для Corgi SoM v1. Для работы именно с Linux Kernel, в инфраструктуре buildroot существует отдельная команда:
make linux-menuconfig
Выполнив эту команду, мы перейдем в окружение Linux Kernel.

VFS (Virtual File System) в Linux является абстрактным слоем (рисунок 9.1, A), который унифицирует работу с различными файловыми системами, включая UBIFS. На нижнем уровне NAND/NOR флеш (D) управляется через драйвер MTD (Memory Technology Device) (C), предоставляющий операции чтения/записи и стирания блоков. Над MTD располагается слой UBI (Unsorted Block Images) (B), который реализует управление износом, исправление ошибок и динамическое распределение логических томов поверх физических блоков. На базе UBI работает UBIFS (UBI File System) — журнальная файловая система, оптимизированная под NAND, обеспечивающая поддержку каталогов, индексов и метаданных. UBIFS напрямую интегрируется с VFS, предоставляя стандартные файловые интерфейсы и скрывая особенности управления флеш-памятью. Таким образом, взаимодействие строится по цепочке: VFS → UBIFS → UBI → MTD → физическая NAND/NOR.
Первым шагом добавления UBIFS, является добавление уровня UBI. Для этого перейдем в Device Drivers > Memory Technology Device (MTD) support и включим поддержку уровня UBI, как на рисунке 9.2. Также используемая NAND память работает по SPI интерфейсу. Необходимо добавить поддержку NAND SPI перейдя в Device Drivers > Memory Technology Device (MTD) support > NAND и включить соответствующий драйвер устройства, как на рисунке 9.3.



После включения драйверов описанных выше, нам доступен уровень UBIFS. Перейдите в File systems > Miscellaneous filesystems в включите поддержку UBIFS как на рисунке 9.4.
Заключительным этапом работы с разделом Linux Kernel будет коррекция драйвера NAND от вендора Winbond. По умолчанию поддержки 1.8V SPI NAND памяти W25M02GW нет в drivers/mtd/nand/spi/winbond.c, поэтому необходимо добавить SPINAND_INFO в winbond.c:
(ваш путь)/buildroot/output/build/linux-custom/drivers/mtd/nand/spi/winbond.c и внесите ниже представленный код в драйвер.
SPINAND_INFO("W25M02GW",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbb, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
Для сборки пакета Linux Kernel в buildroot вы можете воспользоваться командами make linux-build или make linux-rebuild. make linux-build соберет только то, что считается «устаревшим», make linux-rebuild явно отменяет эту проверку, гарантируя полный процесс пересборки компонента Linux Kernel в Buildroot.
10. rootfs
Ключевые пакеты корневой файловой системы сосредоточим вокруг работы с UBIFS (NAND) и базового тестирования Ethernet (GEM, AXI Ethernet).
Для работы с утилитами командной строки мы используем BusyBox. BusyBox — это компактный набор утилит, объединяющий более 300 стандартных UNIX-команд в одном исполняемом файле. Он работает в POSIX-совместимых средах, включая Linux, Android и FreeBSD, и особенно востребован во встраиваемых системах с крайне ограниченными ресурсами. Благодаря своей универсальности и минимализму BusyBox часто называют «швейцарским ножом» для Embedded Linux, поскольку он заменяет целый арсенал отдельных программ, обеспечивая базовую функциональность операционной системы в условиях нехватки памяти и места на диске.
Добавим поддержку работы с некоторыми сетевыми утилитами: phytool, tcpdump, ifupdown scripts. Перейдите Target packages → Networking applications и включить их поддержку, как на рисунке 10.1.

Для тестирования SPI и PS части SoC можно воспользоваться утилитами (coremark, coremark-pro - не рекомендуется для сборки rootfs.cpio.uboot требует Perl в rootfs, spidev_test, stress, stress-ng), опция необязательная может использоваться по желанию. Для добавление данных пакетов перейдите в Target packages → Debugging, profiling and benchmark (рисунок 10.2) и включите указанные опции.

Для максимального тестирования и проверки работы NAND предлагаем воспользоваться всеми утилитами, которые представлены в меню для mtd (Target packages → Filesystem and flash utilities). Выберите все пакеты вложенные в mtd, jffs2 and ubi/ubifs tools, как это показано на рисунке 10.3.

Завершение работы с rootfs будет создание образов файловой системы UBIFS, initramfs/cpio и настройка Kernel.
Cpio — это утилита-архиватор и формат файлов, который используется для работы с файловыми системами. В контексте Embedded Linux, cpio используется для создания initramfs начальной файловой системы в оперативной памяти, которая представляет собой архив в формате cpio, содержащий модули ядра и другие файлы, необходимые для загрузки операционной системы, и монтируемый в оперативную память. Такой способ один из самых простых способов для быстрого bring-up систем с Embedded Linux.
Настроить образы файловой системы и включить опции для них необходимо, как на рисунке 10.4 перейдя в раздел Filesystem images.

Для Kernel нужно внести изменение в путь расположения конфигурационного файла (рисунок 10.5) и выбрать Using a custom (def)config file. Данные изменения в файле содержат описание из раздела Linux Kernel. Custom конфигурационный файл находится в том же github репозитории, что и предыдущие файлы проекта (файл xilinx_zynq_defconfig можно скачать с github и поместите его в /buildroot/output/build/linux-custom/arch/arm/configs/).

11. Запуск образа с Embedded Linux
Пришло время создать bin файл с fsbl, bitstream, u-boot, boot.src для старта загрузчика. Файл boot.src используется для автоматизации загрузки системы целиком. Файл автоматизации qspiboot.cmd находится в (ваш путь)/buildroot/output/build/uboot-custom/board/xilinx/bootscripts/, с помощью этого файла необходимо сгенерировать boot.src используя инструмент mkimage выполнив команду ./tools/mkimage -c none -A arm -T script -в qspi boot.cmt boot.scr (укажите пути относительно вашей системы). Примечание: 1) размеры fdt, kernel, rootfs корректируйте относительно того, какого размера вы собрали файлы; 2) адреса для bootm можно задать свои.
# This example only target for qspi boot, sameway it can be created for boot
# devices like nand.
# Generate boot.scr:
# ./tools/mkimage -c none -A arm -T script -d qspiboot.cmd boot.scr
#
# It requires a list of environment variables to be defined before load:
# fdt_addr, fdt_offset, fdt_size, kernel_addr, kernel_offset, kernel_size
#
#sf probe 0 0 0 && sf read $fdt_addr $fdt_offset $fdt_size && sf read $kernel_addr $kernel_offset $kernel_size && booti #$kernel_addr - $fdt_addr
setenv fdt_addr 0x15000000
setenv fdt_offset 0x1200000
setenv fdt_size 0x4304
setenv kernel_addr 0x10000000
setenv kernel_offset 0x800000
setenv kernel_size 0x513358
setenv rootfs_addr 0x18000000
setenv rootfs_offset 0x1240000
setenv rootfs_size 0x596662
setenv bootargs console=ttyPS0,115200 ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs rw rootwait earlyprintk
#sf probe 0 0 0 && sf read $fdt_addr $fdt_offset $fdt_size && sf read $kernel_addr $kernel_offset $kernel_size && sf read $rootfs_addr $rootfs_offset $rootfs_size && bootm $kernel_addr $rootfs_addr $fdt_addr
sf probe 0 0 0 && sf read $fdt_addr $fdt_offset $fdt_size && sf read $kernel_addr $kernel_offset $kernel_size && sf read $rootfs_addr $rootfs_offset $rootfs_size && bootm $kernel_addr - $fdt_addr

В Vitis IDE необходимо создать BOOT.bin файл, который будет зашиваться в SPI NOR. Создайте порядок файлов, как на рисунке 11.1. Для файлов fsbl, bitstream, uboot смещение не указывается. Для файла boot.src укажите смещение 0x7c0000 (datafile - partition type). Далее выбираем прошить флеш память, как на рисунке 11.2. В качестве init файла выберите созданный custom FSBL, как в разделе “настройка минимального FSBL для целей программирования флеш-памяти”.


После старта U-Boot (рисунок 11.3) необходимо выполнить следующие команды, для настройки сети и запуска через TFTP (протокол передачи файлов, который позволяет быстро передавать небольшие файлы, такие как файлы конфигурации и прошивки, без аутентификации и шифрования):
setenv ethact ethernet@e000b000
setenv ipaddr 192.168.100.2
setenv serverip 192.168.100.7
Команды setenv в U-Boot используются для настройки сетевого окружения перед загрузкой образов. Первая команда setenv ethact ethernet@e000b000 задаёт активный Ethernet-контроллер, который будет использоваться для сетевых операций — в данном случае это встроенный GEM MAC. Команда setenv ipaddr 192.168.100.2 назначает IP-адрес самой платы в локальной сети, чтобы обеспечить её доступность для сетевого обмена. Команда setenv serverip 192.168.100.7 указывает IP-адрес TFTP сервера, с которого плата будет загружать необходимые файлы, ядро Linux, device tree или rootfs.
tftpboot 0x10000000 uImage
tftpboot 0x15000000 zynq-corgi-som-v1.dtb
tftpboot 0x18000000 rootfs.cpio.uboot
bootm 0x10000000 0x18000000 0x15000000
Последовательность команд загружает образы системы по сети с помощью TFTP и запускает ядро Linux. Команда tftpboot 0x10000000 uImage загружает образ ядра Linux (uImage) в оперативную память по адресу 0x10000000. Затем tftpboot 0x15000000 zynq-corgi-som-v1.dtb загружает device tree файл (.dtb), описывающий аппаратную конфигурацию платы, в адрес 0x15000000. Следующей командой tftpboot 0x18000000 rootfs.cpio.uboot загружается корневая файловая система в формате initramfs по адресу 0x18000000. После этого команда bootm 0x10000000 0x18000000 0x15000000 передаёт управление загруженному ядру, указывая адреса ядра, initramfs и device tree, в результате чего система Linux стартует с указанными параметрами (рисунок 11.4).
Далее сделаем запись недостающих данных в SPI NOR (linux kernel, dtb, rootfs):
Для Linux
sf probe 0 0 0
sf erase 0x800000 0xA00000
tftpboot 0x10000000 uImage
sf write 0x10000000 0x800000 0x513358
Для Device Tree
sf probe 0 0 0
sf erase 0x1200000 0x40000
tftpboot 0x15000000 zynq-corgi-som-v1.dtb
sf write 0x15000000 0x1200000 0x4304
Для rootfs
sf probe 0 0 0
sf erase 0x1240000 0xDC0000
tftpboot 0x18000000 rootfs.cpio.uboot
sf write 0x18000000 0x1240000 0x596662
Эта последовательность команд используется для прошивки образа в SPI-flash через U-Boot. Сначала выполняется команда sf probe 0 0 0, которая инициализирует SPI-контроллер и подключенную флеш-память. Затем с помощью sf erase стирается участок флеша размером n байт, начиная с адреса 0x#######, чтобы освободить место под запись. Команда tftpboot 0x####### #имя загружает файл #имя по сети с TFTP-сервера в оперативную память по адресу 0x#######. В завершение выполняется sf write, которая записывает загруженный образ из RAM во флеш-память, начиная с адреса 0x#######, на участок размером m байт. Разметка памяти описана в разделе “описание структуры разделов QSPI Flash”.

Строка в переменной bootargs = "console=ttyPS0,115200 root=/dev/ram rw earlyprintk" определяет параметры запуска ядра Linux. Она задаёт использование UART-порта ttyPS0 со скоростью 115200 бод в качестве системной консоли для ввода и вывода, указывает на размещение корневой файловой системы в оперативной памяти (/dev/ram) и её монтирование в режиме чтения и записи, а также включает режим earlyprintk, позволяющий ядру выводить отладочные сообщения ещё на ранних стадиях загрузки до инициализации основных драйверов.
Для тестирования сетевой части, настроем IP-адреса сетевых интерфейсов:
ip addr add 192.168.100.20/24 dev eth0
ip addr add 192.168.100.30/24 dev eth1
ip addr add 192.168.100.40/24 dev eth2
ip addr add 192.168.100.50/24 dev eth3
Каждая команда присваивает статический IP-адрес соответствующему сетевому интерфейсу в подсети 192.168.100.0/24. 192.168.100.X/24 — IP-адрес с маской 255.255.255.0, dev ethX — интерфейс, которому назначается IP-адрес.
Для активации сетевых интерфейсов выполним следующие команды:
ip link set eth0 up
ip link set eth1 up
ip link set eth2 up
ip link set eth3 up
Данные команды включают (активируют) указанные сетевые интерфейсы, переводя их в состояние UP.
Для деактивация сетевых интерфейсов, необходимо выполнить ниже перечисленные команды:
ip link set eth0 down
ip link set eth1 down
ip link set eth2 down
ip link set eth3 down
Данные команды выключают (деактивируют) указанные сетевые интерфейсы, переводя их в состояние DOWN.
Проверим работу сетевых интерфейсов, для этого отправим ICMP-эхо (ping) с конкретного интерфейса:
ping -I eth0 -i 0.1 192.168.100.7
ping -I eth1 -i 0.1 192.168.100.7
ping -I eth2 -i 0.1 192.168.100.7
ping -I eth3 -i 0.1 192.168.100.7
Описание команды: ping — отправка ICMP-запросов к хосту 192.168.100.7, -I ethX — использование указанного интерфейса для отправки пакетов, -i 0.1 — интервал между отправками пакетов: 0.1 секунды. Команда позволяет проверить доступность хоста 192.168.100.7 с каждого интерфейса по отдельности.
Следующим этапом после работы с NOR SPI памятью будет конфигурация NAND SPI памяти. Для SPI NAND памяти сделаем возможность старта системы с UBIFS.
Creating 4 MTD partitions on "spi1.0":
0x000000000000-0x000001000000 : "nand-reserved"
0x000001000000-0x000006000000 : "nand-recovery"
0x000006000000-0x00000b000000 : "ubia"
0x00000b000000-0x000010000000 : "ubib"
4 fixed-partitions partitions found on MTD device spi0.0
Creating 4 MTD partitions on "spi0.0":
0x000000000000-0x000000800000 : "qspi-fsbl-uboot"
0x000000800000-0x000001200000 : "qspi-linux"
0x000001200000-0x000001240000 : "qspi-device-tree"
0x000001240000-0x000002000000 : "qspi-rootfs"
Получить информацию о mtd можно с помощью команды mtdinfo, пример далее.
# mtdinfo /dev/mtd2 -u
mtd2
Name: ubia
Type: nand
Eraseblock size: 131072 bytes, 128.0 KiB
Amount of eraseblocks: 640 (83886080 bytes, 80.0 MiB)
Minimum input/output unit size: 2048 bytes
Sub-page size: 2048 bytes
OOB size: 64 bytes
Character device major/minor: 90:4
Bad blocks are allowed: true
Device is writable: true
Default UBI VID header offset: 2048
Default UBI data offset: 4096
Default UBI LEB size: 126976 bytes, 124.0 KiB
Maximum UBI volumes count: 128
Для загрузки rootfs.ubi необходимо использовать команду ubiformat /dev/mtd2 -f rootfs.ubi. Скачать rootfs.ubi можно командой tftp -gr rootfs.ubi 192.168.100.7. После загрузки rootfs перезагружаем систему, boot.src загружает rootfs с NAND SPI (рисунок 11.5)

Строка (bootargs console=ttyPS0,115200 ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs rw rootwait earlyprintk) задаёт параметры ядру Linux через переменную bootargs. Параметр ubi.mtd=2 сообщает ядру, что UBI должен быть подключён к разделу номер 2 в памяти MTD. Далее root=ubi0:rootfs задаёт корневую файловую систему, расположенную в томе rootfs на UBI-устройстве с номером 0. Опция rootfstype=ubifs указывает тип файловой системы — UBIFS. Ключ rw разрешает монтировать rootfs в режиме чтения-записи, а rootwait заставляет ядро ждать появления устройства с корневой файловой системой перед монтированием.
В итоге подготовлен образ с поддержкой NAND SPI и NOR SPI с Embedded Linux (u-boot, device tree, linux kernel, rootfs), собран FSBL для полноценного функционирования системы.
megalloid
Где купить? :) Сколько стоит? Гербера, схематики, трассировки не open source?