Цели статьи
Автоматизировать скриптами сборку загрузчиков и ядра Linux с заданными конфигурационными файлами в автоматическом режиме без необходимости запуска gui Vivado;
Автоматизировать установку операционной системы Debian из бинарных файлов с заданными пакетами и первоначальными настройками;
Загружать образы ядра linux + dts по tftp, а корневую файловую систему по NFS для сохранения ресурса карточки и времени на копирование файлов с рабочей станции на устройство;
Добавить в U-Boot меню для быстрого выбора источника загрузки: всё с карточки, всё по сети и разные комбинации этих вариантов;
При необходимости данные скрипты можно адаптировать практически под любую встраиваемую платформу.
Введение
Операционная система Debian была выбрана по причине использования окружения для активной разработки, где основными критериями являются: стабильность, легковесность, а главное - возможность быстрой установки пакетов при необходимости. По завершению разработки в случаях, когда под хранение выделяется только nand или nor флешка ограниченного объёма, делаем сборку buildroot с уже определённым минимальным набором пакетов.
Установка необходимых программ
Вся работа проводилась на компьютере с операционной системой Debian, для установки используется стандартный менеджер пакетов. Для сборки fsbl и pmu необходимо установить Vivado 2020 и старше. Всё отлично работает с самой последней 2023.2
-
Клонируем репозиторий
Все необходимые сборочные скрипты выложил на гитхаб:
git clone https://github.com/Igorbunow/build-uboot-linux-debian.git
Устанавливаем кросскомпилятор
apt install gcc-aarch64-linux-gnu
Если вы используете внешний toolchain, то его настройка будет описана ниже
Устанавливаем debootstrap, qemu-static, tftp и nfs сервер
apt install debootstrap atftpd nfs-kernel-server qemu-user-static fdisk
настраиваем tftpd
mkdir /srv
mkdir /srv/tftp
chown 777 /srv/tftp
vim /etc/default/atftpd
## Options for atftpd:
USE_INETD=false
OPTIONS="--port 69 --tftpd-timeout 300 --retry-timeout 5 --mcast-port 1758 --mcast-addr 239.239.239.0-255 --mcast-ttl 1 --maxthread 100 --verbose=5 /srv/tftp"
systemctl restart atftpd
настраиваем NFS
mkdir /srv/nfs
mkdir /srv/nfs/root
vim /etc/exports
#/srv/nfs *(no_root_squash,no_subtree_check,rw)
/srv/nfs 192.168.10.1/24(no_root_squash,no_subtree_check,rw)
#/tmp/root *(no_root_squash,no_subtree_check,rw)
Крайне не рекомендуется давать всем хостам права (указывать *), т.к. кто угодно может из сети может положить исполняемый файл от локального администратора с установленным битом SUID с опцией no_root_squash, которая отключает понижение прав root при подключении к NFS, но она необходима для корректной работы подключенной папки файловой системы в качестве корневой.
Собираем загрузчик FSBL+U-Boot и ядро Linux
Все скрипты для сборки загрузчиков и ядра находятся в папке ./boot репозитория:
configs |
папка с defconfig для linux и uboot |
all.sh |
Скрипт, включающий все скрипты ниже и делающий полную сборку |
download_orig.sh |
Скрипт, выкачивающий исходники из официальных репозиториев для сборки. При необходимости в нём можно зафиксировать интересующий коммит. Также при необходимости можно указать свои репозитории |
fsbl_pmu_2020.sh |
Скрипт, создающий проект по умолчанию в Vitis и собирающий первичный загрузчик и pmu (Требуется Vivado, Vitis от 2020) |
dts.sh |
Скрипт, собирающий утилиту для работы с деревом устройств для ядра linux и u-boot (для компьютера), необходимую для дальнейшей сборки |
atf.sh |
Скрипт для сборки ARM trusted firmware (требуется кросскомпилятор или тулчейн) |
u-boot.sh |
Скрипт для сборки загрузчика U-Boot |
kernel.sh |
Скрипт для сборки ядра Linux |
bootbin.sh |
Копирование собранного первичного загрузчика, U-Boot, pmu и bl31 с последующей сборкой готового загрузочного образа BOOT.bin |
bootscript.sh |
Генерирование скрипта для загрузчика UBoot из boot.script |
image.sh |
Копирование образа загрузчика BOOT.bin, ядра linux, device-tree, скрипта UBoot, модулей ядра в виде, пригодном для переноса на загрузочный раздел SD карточки. Device tree файл при этом переименовываем в system.dtb. На этот же раздел кладём system.bit, собранный под вашу задачу. Модули ядра переносятся в rootfs |
Настойка ядра Linux и UBoot
Конфигурации defconfig
При сборке система берет defconfig, расположенные в папках ./configs/linux/xilinx_ulrascale_debug_defconfig и ./configs/uboot/xilinx_ulrascale_debug_defconfig и создаёт из них соответствующие .config файлы. Если Вам необходимо сделать настройки под свою задачу, заходим в папку u-boot-xlnx или linux-xlnx, настраиваем:
make ARCH=arm64 menuconfig
И сохраняем. Затем создаём из полученного .config -> defconfig:
make ARCH=arm64 savedefconfig
Забираем из корня u-boot-xlnx или linux-xlnx defconfig, переименовываем в зависимости от того, что у нас и кладём соответственно в /xilinx_ulrascale_debug_defconfig или ./configs/uboot/xilinx_ulrascale_debug_defconfig.
Дерево устройств Device tree
Device tree files (dts) общие для ядра и UBoot с необходимыми overlay берутся из ./configs/dts, куда при необходимо положить свои, если требуется. Кроме этого, для сборке в файле ./configs/dts/Makefile.in надо добавить цель для сборки dtb:
dtb-$(CONFIG_ARCH_ZYNQMP) += zynqmp-zcu106-custom.dtb
Также проверяем, чтобы в defconfig UBoot ./configs/uboot/xilinx_ulrascale_debug_defconfig использовался требуемый нам dts, указанный без расширения:
CONFIG_DEFAULT_DEVICE_TREE="zynqmp-zcu106-custom"
Загрузочного скрипта UBoot
Данный скрипт выполняется UBoot после загрузки переменных окружения, которые можно хранить, например, на карточке SD. Скрипт поддерживает циклы и операторы условного перехода, синтаксис похож на синтаксис bash. С его помощью в зависимости от заданных переменных, в нашем случае ip адресов tftp или nfs серверов и их папок, сделано отображение пунктов меню с соответствующими вариантами загрузки:
boot.script
setenv bitsteam_name system.bit
setenv kernel_image_name Image
setenv dts_name system.dtb
setenv bitsteam_addr 0x20000000
setenv kernel_image_addr 0x10000000
setenv dts_addr 0x02a00000
setenv load_tftp_bitsteam 'dhcp ${bitsteam_addr} ${tftp_serverip}:${tftp_dir}${bitsteam_name}; fpga load 0 ${bitsteam_addr} $filesize;'
setenv load_tftp_kernel_image 'dhcp ${kernel_image_addr} ${tftp_serverip}:${tftp_dir}${kernel_image_name}'
setenv load_tftp_dts 'dhcp ${dts_addr} ${tftp_serverip}:${tftp_dir}${dts_name}'
setenv load_mmc_bitsteam 'fatload mmc 0:1 ${bitsteam_addr} ${bitsteam_name}; fpga load 0 ${bitsteam_addr} $filesize;'
setenv load_mmc_kernel_image 'fatload mmc 0 ${kernel_image_addr} ${kernel_image_name}'
setenv load_mmc_dts 'fatload mmc 0 ${dts_addr} ${dts_name}'
setenv boot_dev booti 0x10000000 - 0x02a00000
setenv bootargs_mmc 'earlycon clk_ignore_unused consoleblank=0 root=/dev/mmcblk0p2 rootfstype=btrfs rw rootwait uio_pdrv_genirq.of_id=generic-uio net.ifnames=0 biosdevname=0'
setenv bootargs_nfs 'earlycon clk_ignore_unused consoleblank=0 root=/dev/nfs ip=dhcp nfsroot='${nfs_serverip}':'${nfs_dir}',nolock,nfsvers=4 rootfstype=nfs rw rootwait uio_pdrv_genirq.of_id=generic-uio net.ifnames=0 biosdevname=0'
setenv bootcmd bootmenu 10
setenv bootmenu_0 Load All from MMC='setenv bootargs '${bootargs_mmc}';run load_mmc_bitsteam; run load_mmc_kernel_image; run load_mmc_dts; run boot_dev;'
if test -n $tftp_serverip;
then
if test -n $nfs_serverip;
then
setenv root_name NFS
setenv bootargs ${bootargs_nfs}
else
setenv root_name SD_card
setenv bootargs ${bootargs_mmc}
fi
setenv bootmenu_1 Load All via tftp ${tftp_serverip} root ${root_name}='run load_tftp_bitsteam; run load_tftp_kernel_image; run load_tftp_dts; run boot_dev;'
setenv bootmenu_2 Load bitsteam via tftp ${tftp_serverip} kernel from SD card root ${root_name}='run load_tftp_bitsteam; run load_mmc_kernel_image; run load_mmc_dts; run boot_dev;'
setenv bootmenu_3 Load bitsteam from SD Card kernel via tftp ${tftp_serverip} root ${root_name}='run load_mmc_bitsteam; run load_tftp_kernel_image; run load_tftp_dts; run boot_dev;'
setenv bootmenu_4 Reset board=reset
setenv bootmenu_5 Test='ls'
else
setenv bootmenu_1 Reset board=reset
fi
bootmenu 5
Переменные c ip адресами необходимо задать до загрузки скрипта. О том, как это сделать, будет описано ниже.
Также, в случае необходимости, можно требуемым образом изменить аргументы загрузки bootargs для bootargs_mmc и bootargs_nfs. `net.ifnames=0 biosdevname=0` необходимо для запрета переименования eth0 -> end0, т.к. при монтировании по nfs переименования не происходит и этот запрет позволяет избегать создания разных версий дополнительных конфигов rootfs.
Настраиваем кросскомпиляцию
Если вы используете внешний toolchain, добавляем в скриптах: fsbl_pmu_2020.sh, kernel.sh, u-boot.sh путь до его /bin, префикс кросскомпиляции и архитектуру:
export PATH=/tmp/build/buildroot/output/host/bin:${PATH}
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
Настраиваем остальные параметры скриптов
Путь к Vivado прописываем в скрипте all.sh:
export VIVADO=/opt/Electro/FPGA/Xilinx/Vivado/2023.1/settings64.sh
Копируемые в образ файлы и их имена - в скрипте image.sh, при этом не забывая проверить, какой device tree dtb файл мы истользуем:
cp ${WORK}/linux-xlnx/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-custom.dtb ./system.dtb
Запускаем сборку
Запускаем скрипт ./all.sh и ждём завершения сборки. Если всё сделано правильно, то в папке ./image у нас будут следующие файлы, готовые для копирования на загрузочный раздел карты памяти за исключением модулей ядра - их копируем в rootfs
modules |
Папка с модулями ядра Linux. Предназначена для копирования на rootfs раздел скриптами ниже |
BOOT.bin |
Образ загрузчика, содержащий fsbl, pmu, bl31, UBoot |
boot.scr |
Стартовый скрипт загрузчика UBoot |
Image |
Ядро Linux |
system.dtb |
Дерево устройств ядра linux |
system.bit |
Битстрим для fpga, подготавливается отдельно |
Собираем Debian из бинарных пакетов
Сборка debian также полностью автоматизирована при помощи скриптов. RootFs debian собирается в папке ./root. Переходим в папку debian:
files |
Файлы, которые необходимо поместить в целевую систему с заменой существующих |
2_debian_bootstrap.sh |
Скрипт, выполняющий скачивание пакетов debian и производящий их первичную настройку при помощи debootstrap |
3_copy_kernel_modules.sh |
Скрипт, выполняющий копирование собранных выше модулей ядра в rootfs debian |
4_copy_and_config.sh |
Завершающие настройки системы: задание пароля root, создание пользователя, настройки монтирования и сети |
Добавляем дополнительные пакеты, убираем существующие в зависимости от вашей задачи. Для этого в скрипте 2_debian_bootstrap.sh задаём переменной PACKAGES список соответствующих пакетов. При необходимости можно изменить версию систему, разрядность, источник пакетов. А также, при необходимости, можно вместо debian собрать ubuntu
Проводим окончательную настройку системы. Для этого в скрипте 4_copy_and_config.sh задаём требуемый пароль root, создание дополнительного пользователя, настройку сети, монтирования по-умолчанию fstab, копирование требуемых файлов из папки files, очистку кэша пакетов и логов и любые другие требуемые дополнительные настройки
По завершению настройки последовательно выполняем скрипты:
./2_debian_bootstrap.sh
./3_copy_kernel_modules.sh
./4_copy_and_config.sh
и получаем в папке ./root полностью готовую rootfs с debian, содержащую модули ядра для дальнейшего разворачивания на устройстве.
Работа c rootfs
Представим следующую ситуацию. Дождавшись, наконец, завершения сборки debian вы вдруг обнаруживаете, что данный образ не вписался в заданный объём, или вы забыли установить какой-то пакет. Что делать в таком случае? К счастью, заново сборку запускать не нужно, нас выручает qemu-static, производящий эмуляцию пользовательского режима. В этом режиме QEMU может запускать процессы Linux, скомпилированные для одного ЦП, на другом ЦП. Этот режим можно использовать как один из способов кроссборки с некоторыми ограничениями. Самым быстрым, при этом является вариант с использованием нативного кроскомпилятора напрямую, а в данном случае всё же работает через прослойку эмуляции
Например, нам надо добавить пакеты для возможности сборки приложений прямо на устройстве или через эмулятор. Для этого:
Проверить наличие в ./root символической ссылки на qemu-arm64-static и при её отстутствии создать:
ln -s "$(which qemu-arm64-static)" ./root/qemu-arm64-static
Монтирум системные устройства в rootfs целевого устройства (если хотите просто доустановить пакеты, то данный шаг можно пропустить):
mount --bind /dev ./root/dev/
mount --bind /sys ./root/sys/
mount --bind /proc ./root/proc/
mount --bind /dev/pts ./root/dev/pts
Переходим в систему, делая туда chroot
chroot ./root
Устанавливаем требуемые пакеты:
PACKAGES="valgrind build-essential clang u-boot-tools cmake git subversion \
tcpdump kbuild nmap wget libboost-all-dev linux-headers-arm64 \
minicom picocom p7zip-full gzip zstd bzip2 sysfsutils"
apt update
apt install ${PACKAGES}
echo "packages: ${PACKAGES}" >> /info
apt clean
Далее, можно зайти под user через
login
или `sudo -u user bash`, собрать, требуемые программы и выйти `exit`Выходим из
chroot
:
exit
Если монтировали устройства с локальной машины в rootfs устройства, отмонтируем:
umount ./root/sys
umount ./root/proc
umount ./root/dev/pts
umount ./root/dev
В случае известных багов, мешающих запуску некоторых служб, в частности
binfmt.service
чиним это:
rm -rf ./root/usr/lib/binfmt.d/python3.11.conf
rm -rf ./root/usr/lib/binfmt.d/llvm-14-runtime.binfmt.conf
Очищаем логи установки:
rm -rf ./root/var/log/apt/*
echo -n > ./root/var/log/alternatives.log
echo -n > ./root/var/log/dpkg.log
echo -n > ./root/root/.bash_history
rm ./root/var/cache/apt/archives/lock
Дописываем в ./root/info дополнительную иформацию о системе, помимо сгенерированной системое, например, расшифрем, что stable (12.0 bookworm), чтобы не вспоминать через несколько лет, что за система лежит в образе
Создаём образ для карты SD памяти
Для передачи образа карты памяти и его последующего хранения, создадим минимально необходимый образ системы, который, при разворачивании на карту памяти будет расширяться до его максимального размера. В качестве файловой системы для корневой файловой системы мы выбрали btrfs - система, поддерживающая прозрачное сжатие и копирование при записи, что продлевает жизнь карточкам памяти либо иному флеш носителю, записывая меньше информации и благодаря тому, что дважды в одно место данные не пишутся, производится выравнивание ресурса. Если по каким-то причинам необходимо использовать ext2, ext3, ext4 системы - как это сделать, тоже будет описано ниже.
Оцениваем необходимый размер образа
Загрузочный размер может иметь размер от 32Mb до 4Гб. 32Мб будет совсем впритык, а 4Гб - слишком много. Размер выбирается в зависимости от задач и размещаемых на нём файлах. Выберем 128Мб
Размер корневой файловой системы. Смотрим, сколько у нас получилось в образе, который мы собираемся записывать. В качестве примера, расмотрим минимальный образ без дополнительных пакектов. Смотрим:
du -h -d 0
517M
Соответственно, выбираем, 640Мб под корневой раздел
Итоговый размер нашего образа 640Мб + 128Мб = 768Мб
Создаём пустой образ необходимого размера
Выше мы определили, что размер нашего образа составляет 768Мб. Создадим пустой файл такого размера:
dd if=/dev/zero bs=1M count=768 of=ultrascale_short.img
sync;
и дожидаемся успешного создания:
768+0 records in
768+0 records out
805306368 bytes (805 MB, 768 MiB) copied, 0.396911 s, 2.0 GB/s
Монтируем и размечаем образ
размечаем образ утилитой fdisk:
fdisk -c=dos ultrascale_short.img
Затем последовательно выполняем команды. Размер указываем в секторах по 512 байт:
n |
создаём новый раздел (загрузочный) |
p |
указываем тип MBR раздела - первичный |
1 |
Указываем номер загрузочного раздела (строго 1-ый для загрузочного) |
63 |
1-ый сектор загрузочного раздела, оставляем по-умолчанию |
262144 |
Последний сектор раздела, вычисляем для 128Мб: 128*2^20/512 |
a |
Делаем раздел загрузочным |
n |
создаём новый раздел (rootfs) |
p |
указываем тип MBR раздела - первичный |
2 |
Указываем номер раздела rootfs |
262145 |
1-ый сектор раздела rootfs, оставляем по-умолчанию |
1572863 |
Последний сектор раздела rootfs |
w |
Записываем изменения в файл и выходим |
лог выполнения fdisk
fdisk -c=dos ultrascale_short.img
Welcome to fdisk (util-linux 2.39.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Device does not contain a recognized partition table.
DOS-compatible mode is deprecated.
Created a new DOS (MBR) disklabel with disk identifier 0xb315f728.
Command (m for help): n
Partition type
p primary (0 primary, 0 extended, 4 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (63-1572863, default 63):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (63-1572863, default 1572863): 262144
Created a new partition 1 of type 'Linux' and of size 128 MiB.
Command (m for help): a
Selected partition 1
The bootable flag on partition 1 is enabled now.
Command (m for help): n
Partition type
p primary (1 primary, 0 extended, 3 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2): 2
First sector (262145-1572863, default 262145):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (262145-1572863, default 1572863):
Created a new partition 2 of type 'Linux' and of size 640 MiB.
Command (m for help): w
The partition table has been altered.
Syncing disks.
Принудительно сбрасываем данные из буфера на диск:
sync;
Монтируем наш файл в виртуальное устройство:
losetup --partscan --find --show ultrascale_short.img
и дожидаемся ответа, каким устройством смонтировалось:
/dev/loop0
Для избежания проблем, т.к. у вас могут определиться по-другому, будем обозначать loop0 как loopX. При необдуманном копировании возможна потеря данных. Особенно славится в этом отношении команда dd, гарантированно затирающая данные на диски, если ошибиться с буквой диска и ещё подождать при этом...
Смотрим определившиеся разделы:
ls /dev | grep loopX
и видим наши 2 раздела:
loop0
loop0p1
loop0p2
Очищаем полученные разделы:
dd if=/dev/zero of=/dev/loopXp1 bs=4M
dd if=/dev/zero of=/dev/loopXp2 bs=4M
sync;
Лог очистки разделов loopXp1 и loopXp2
dd if=/dev/zero of=/dev/loop0p1 bs=4M
dd if=/dev/zero of=/dev/loop0p2 bs=4M
sync;
dd: error writing '/dev/loop0p1': No space left on device
32+0 records in
31+0 records out
134185984 bytes (134 MB, 128 MiB) copied, 0.617296 s, 217 MB/s
dd: error writing '/dev/loop0p2': No space left on device
160+0 records in
159+0 records out
671088128 bytes (671 MB, 640 MiB) copied, 4.38618 s, 153 MB/s
Появление ошибок о том, что закончилось место на диске вполне нормально и объяснимо, тк dd не знает размер диска и пишет до тех пор, пока не закончится место на разделе.
Форматируем загрузочный раздел в fat, а rootfs в файловую систему btrfs:
mkfs.vfat -F 32 -n "BOOT" /dev/loopXp1
mkfs.btrfs /dev/loopXp2 -L "root"
sync;
Лог форматирования разделов:
mkfs.vfat -F 32 -n "BOOT" /dev/loop0p1
mkfs.btrfs /dev/loop0p2 -L "root"
sync;
mkfs.fat 4.2 (2021-01-31)
btrfs-progs v6.3.2
See https://btrfs.readthedocs.io for more information.
Performing full device TRIM /dev/loop0p2 (640.00MiB) ...
NOTE: several default settings have changed in version 5.15, please make sure
this does not affect your deployments:
- DUP for metadata (-m dup)
- enabled no-holes (-O no-holes)
- enabled free-space-tree (-R free-space-tree)
Label: root
UUID: 553f75fd-bc80-4553-8c7c-279c4e38a6a6
Node size: 16384
Sector size: 4096
Filesystem size: 640.00MiB
Block group profiles:
Data: single 8.00MiB
Metadata: DUP 32.00MiB
System: DUP 8.00MiB
SSD detected: yes
Zoned device: no
Incompat features: extref, skinny-metadata, no-holes, free-space-tree
Runtime features: free-space-tree
Checksum: crc32c
Number of devices: 1
Devices:
ID SIZE PATH
1 640.00MiB /dev/loop0p2
Если всё же выбрали ext4
Форматируем следующим образом (не забывая при этом поправить в boot.script в bootargs_mmc тип корневой файловой системы на rootfstype=ext4):
mkfs.vfat -F 32 -n "BOOT" /dev/loopXp1
mkfs.ext4 /dev/loopXp2 -L "root"
sync;
Монтируем полученные разделы. Для btrfs указываем сжатие. Алгоритм lzo наименее ресурсо-затратный и при этом неплохо сжимает:
mkdir /tmp/root
mkdir /tmp/boot
mount -o compress=lzo /dev/loopXp2 /tmp/root
mount /dev/loopXp1 /tmp/boot
Копируем собранные загрузчики и rootfs в примонтированные разделы образа
Копируем bitstream system.bit в /tmp/boot
Копируем загрузчик, ядро linux, dts и скрипт U-Boot
cp ./../boot/image/BOOT.bin /tmp/boot
cp ./../boot/image/boot.scr /tmp/boot
cp ./../boot/image/Image /tmp/boot
cp ./../boot/image/system.dtb /tmp/boot
chmod -R 777 /tmp/boot
sync;
Копируем rootfs -> /tmp/root, не забывая при этом удалить оттуда qemu-arm64-static
cp -rpna ./root/* /tmp/root/
rm /tmp/root/qemu-arm64-static
sync;
Если наша корневая системы была заархивирована:
tar -C /tmp/root -xjf images/rootfs.tar.bz2
sync;
Отсоединяем примонтированные разделы нашего образа:
umount /tmp/boot
umount /tmp/root
sync;
Отсоединяем наше виртуальное устройство:
losetup -d /dev/loopX
sync;
Сжимаем полученный образ и записываем его на SD карту
-
сжимаем образ
Для дальнейшего хранения образа и передачи его другим разработчикам, удобнее всего его будет сжать. Для сжатия выберем формат gzip, xz, bz2, тк сжатый им диск понимают многие утилиты для записи образов дисков, в том числе и популярный rufus. В нашел случае, наилучшего сжатия удалось добиться с форматом xz. Другой формат zstd позволяет в некоторых случаях сжать лучше, но применяйте его, если уверены, что кому его передаёте будет его удобно использовать
gzip -9cvf ultrascale_short.img > ultrascale_short.img.gz
xz -9cvf ultrascale_short.img > ultrascale_short.img.xz
bzip2 -9cvf ultrascale_short.img > ultrascale_short.img.bz2
zstd -16v --ultra --format=zstd ultrascale_short.img -o ultrascale_short.img.zst
В нашем случае получилось следующее:
ultrascale_short.img |
640Мб |
ultrascale_short.img.gz |
206Мб |
ultrascale_short.img.bz2 |
189Мб |
ultrascale_short.img.zst |
170Мб |
ultrascale_short.img.xz |
143Мб |
Записываем полученный образ на SD карту и передаём другим разработчикам
zcat ultrascale_short.img.gz | dd bs=8M iflag=fullblock of=/dev/sdxxx status=progress; sync;
xzcat ultrascale_short.img.xz | dd bs=8M iflag=fullblock of=/dev/sdxxx status=progress; sync;
bzcat ultrascale_short.img.bz | dd bs=8M iflag=fullblock of=/dev/sdxxx status=progress; sync;
zstdcat ultrascale_short.img.zst | dd bs=8M iflag=fullblock of=/dev/sdxxx status=progress; sync;
Лог записи образа sd карты
zcat ultrascale_short.img.gz | dd bs=8M iflag=fullblock of=/dev/sdf status=progress; sync;
754974720 bytes (755 MB, 720 MiB) copied, 5 s, 150 MB/s
96+0 records in
96+0 records out
805306368 bytes (805 MB, 768 MiB) copied, 129.194 s, 6.2 MB/s
Если лицо, которому передаёте образ, использует windows, то gzip образ можно записать напрямую через rufus без распаковки:
Первый запуск устройства
Устанавливаем карту памяти в устроуйство, переключатель режимов запуска sw6 должен быть установлен в положение 1000.
Подключаемся к uart на устройстве через microusb кабель. На компьютере открываем порт /dev/ttyUSB0 и включаем устройство:
разворачиваем образ rootfs на всё свободное пространство карточки памяти:
для brtfs:
growpart /dev/mmcblk0 2
btrfs filesystem resize max /
sync;
для других файловых систем
для ext2, ext3, ext4:
growpart /dev/mmcblk0 2
resize2fs /dev/mmcblk0p2
sync;
для xfs:
growpart /dev/mmcblk0 2
xfs_growfs /
sync;
и перезагружаем устройство
Для того, чтобы не пропала возможность входить по uart без пороля (если используете):
chattr +i /lib/systemd/system/serial-getty@.service
Загрузка ядра Linux, dts, bitstream по tftp, rootfs debian по NFS
Настраиваем tftp и NFS как было описано выше
Не дожидаясь выполнения загрузочного скрипта uboot, сразу при включении устройства при появлении обратного отсчёта прерываем загрузку устройства клавишей enter:
и попадаем в терминал U-Boot, в котором указываем ip fttp сервера и путь к файлам, относительно корня сервера:
setenv tftp_serverip '192.168.4.12'
setenv tftp_dir /zsu106/
saveenv
reset
Перезагружаемся и видим, что появились пункты меню, позволяющие загрузить ядро Linux, dts, bitstream по tftp, а rootFS debian - с карты памяти:
-
Загружаем все по сети: ядро Linux, dts, bitstream по tftp, а rootFS debian - по NFC
Прерываем загрузку UBoot как в случае с tftp и прописываем ip и путь к папке с rootfs debian:
setenv nfs_serverip '192.168.4.12'
setenv nfs_dir /tmp/root
saveenv
reset
После перезагрузки должно получиться как такое меню:
Весь процесс загрузки подробнее на видео:
Если необходимо отключить какой-либо из режимов - задайте и сохраните без значения
setenv tftp_serverip
setenv nfs_serverip
Никогда не сохраняйте переменные окружения после загрузки скрипта, иначе меню загрузки будет отображаться некорректно! Если всё же это сделали - то извлеките карту памяти и удалите с загрузочного раздела сохранённые файлы окружения:
uboot-redund.env
иuboot.env
Материалы к статье
Комментарии (9)
gosha-z
26.10.2023 00:01+3Я просто оставлю Embedded FPGA - поднимаем Linux на Zynq-7000 - YouTube и Поднимаем MYIR FZ5 – AI Accelerator Card на Xilinx Zynq UltraScale+ MPSoC ZU5EV - YouTube здесь
monah_tuk
26.10.2023 00:01Ох... стримы... 4 часа... но на статью, да, уйдёт больше времени.
Но:
PS: "Petalinux должен сдохнуть" - ведущий
согласен.
monah_tuk
О, я правильно попял, что vitis тут не используется от слова совсем, т.е. на скриптовом уровне тоже? В таком варианте, получается, что при обновлении xsa с переферией для которой нужен драйвер, нужно руками делать запись в device tree?
У себя я оставлял окружение чисто для генерации dt и ещё по мелочи, что бы не лезть туда руками. Ядро и rootfs тоже строились отдельно.
nerudo
fsbl_pmu_2020.sh Скрипт, создающий проект по умолчанию в Vitis и собирающий первичный загрузчик и pmu (Требуется Vivado, Vitis от 2020)
Но если не используется petalinux - уже праздник.
monah_tuk
О да... пока всё запустится... добавлю в заметки, если опять потребуется запускать linux у меня на 104, покурить. Пока baremetal обходимся
K0shi Автор
Спасибо! Всё сразу не учёл, в ручную тоже делали.
Подредактировал скрипты и обновил статью, сейчас dt c overlay берутся из ./configs/dts для ядра и UBoot. Как правило, из сгенерированных dt периферии можно сделать dtsi либо, в зависимости от того, что получилось - скопировать нагенерированное целиком.
Сейчас для использования своих dt требуется, например, для zynqmp-zcu106-custom:
Поместить их исходники (dts, dtsi и если необходимо - .h) в ./configs/dts
Добавить dtb в цели /configs/dts/Makefile.in
Указать в defconfig UBoot (./configs/uboot/xilinx_ulrascale_debug_defconfig) :
В файле формирования образа указать копируемый dtb: