Не так давно, а именно в феврале 2023 года, в продажу поступили платы VisionFive2 , имеющие на борту 8GB LPDDR4 памяти, от компании StarFive. По сути VisionFive2 - это первый в мире высокопроизводительный RISC-V одноплатный компьютер (SBC) со встроенным графическим процессором (GPU). И естественно, мы не могли не заинтересоваться этим устройством, которое имеет весьма впечатляющие характеристики.
![Рис.1. VisionFive 2 SBC Рис.1. VisionFive 2 SBC](https://habrastorage.org/getpro/habr/upload_files/2a3/eec/976/2a3eec976c78ad6a96ecf5279f3fe471.png)
Если кратко, то VisionFive2 имеет четырехядерный 64-битный SoC c ISA RV64GC, работающий на частотах до 1.5 GHz, а также интегрированный графический ускоритель IMG BXE-4-32 MC1, поддерживающий OpenCL 3.0, OpenGL ES 3.2 и Vulkan 1.2. На плате может быть распаяно 2/4/8 GB LPDDR4 RAM. Имеются устройства ввода-вывода, такие как разъем M.2, разъем eMMC, четыре порта USB 3.0, 40-контактный разъем GPIO, два порта Gigabit Ethernet, слот для TF-карт.
Поскольку данная плата, прежде всего, нас интересует как устройство для разработки, далее мы поговорим о таких вещах, как средства разработки, исходные коды ядра Linux и загрузчиков, а также приведем некоторые практические примеры.
Toolchains
Мы приготовили два toolchain-а, один из которых основан на GCC-12.2.0 и GNU Libc 2.37, а другой - предназначен для работы с микроконтроллерами типа GD32VF103. Данные toolchain-ы можно загрузить из каталога x86_64/1.9.8, расположенного на FTP-сервере https://ftp.radix.pro. Здесь нас интересуют toolchain-ы: riscv64-RV64GC-linux-glibc-1.9.8.tar.xz и riscv64-NONE-elf-newlib-1.9.8.tar.xz.
Следует отметить, что данные toolchain-ы не перемещаемы и устанавливать их необходимо в каталог /opt/toolchains/. Для установки достаточно создать каталог и распаковать архив:
mkdir -p /opt/toolchains
cd /opt/toolchains
tar xJvf riscv64-RV64GC-linux-glibc-1.9.8.tar.xz
tar xJvf riscv64-NONE-elf-newlib-1.9.8.tar.xz
Использовать toolchain-ы довольно просто, но для удобства, будет не лишним задать переменные окружения:
CROSS_COMPILE=/opt/toolchains/riscv64-RV64GC-linux-glibc/1.9.8/bin/riscv64-rv64gc-linux-gnu-
Которые обычно используются в Make-файлах U-Boot или ядра Linux.
U-Boot
Для сборки загрузчика нам понадобится три git-репозитория, расположенных в каталоге https://github.com/starfive-tech/: Tools, opensbi и u-boot. В репозитории Tools нас не интересует конкретная версия исходного кода, однако, в остальных репозиториях нам важны конкретные срезы, а именно теги с именем VF2_6.1_v3.8.2.
Чтобы в дальнейшем не путаться в версиях того или иного кода, приготовим архивы:
git clone https://github.com/starfive-tech/Tools.git
cd Tools
git archive --format=tar --prefix=tools-20231107/ 0747c0510e090f69bf7d2884f44903b77b3db5c5 | \
xz >../tools-20231107.tar.xz
git clone https://github.com/starfive-tech/opensbi.git
cd opensbi
git archive --format=tar --prefix=opensbi-1.2-3.8.2/ c6a092cd80112529cb2e92e180767ff5341b22a3 | \
xz >../opensbi-1.2-3.8.2.tar.xz
git clone https://github.com/starfive-tech/u-boot.git
cd u-boot
git archive --format=tar --prefix=u-boot-2021.10-3.8.2/ 84c81d3d0254adb8ae88d5e0633bf01c12436898 | \
xz >../u-boot-2021.10-3.8.2.tar.xz
Теперь клоны репозиториев нам не нужны и мы можем их удалить:
rm -rf Tools opensbi u-boot
Распаковав созданные нами архивы:
tar xJvf tools-20231107.tar.xz
tar xJvf opensbi-1.2-3.8.2.tar.xz
tar xJvf u-boot-2021.10-3.8.2.tar.xz
мы получим следующее дерево каталогов:
.
├── opensbi-1.2-3.8.2
├── tools-20231107
└── u-boot-2021.10-3.8.2
Именно с ним мы и будем работать далее.
В первую очередь надо собрать утилиту spl_tool:
cd tools-20231107/spl_tool
make
Затем можно приступать к сборке U-Boot, предварительно сделав в коде некоторые изменения.
Конфигурационным файлом для платы VisionFive2 является файл starfive_visionfive2_defconfig, расположенный в каталоге u-boot-2021.10-3.8.2/configs/. Для того чтобы иметь возможность остановить процесс загрузки с помощью клавиши пробел, добавим в конец файла starfive_visionfive2_defconfig следующие строки:
# Set BOOTDELAY options:
CONFIG_BOOTDELAY=5
CONFIG_AUTOBOOT_KEYED=y
CONFIG_AUTOBOOT_PROMPT="Hit SPACE in %d seconds to stop autoboot.\n"
CONFIG_AUTOBOOT_STOP_STR=" "
Кроме того, если мы планируем загружать операционные системы общего назначения, предпочтительно изменить переменную CONFIG_BOOTCOMMAND:
CONFIG_BOOTCOMMAND="run distro_bootcmd"
О переменных окружения U-Boot мы поговорим позднее, а пока приступим к сборке. Чтобы упростить набор команд в терминале, приготовим скрипт _build.sh следующего содержания:
#!/bin/sh
suppress_host_varnings="-Wno-stringop-truncation"
suppress_target_varnings="-Wno-unused-variable -Wno-int-conversion -Wno-cpp -Wno-implicit-function-declaration"
make starfive_visionfive2_defconfig ARCH=riscv CROSS_COMPILE=/opt/toolchains/riscv64-RV64GC-linux-glibc/1.9.8/bin/riscv64-rv64gc-linux-gnu-
make HOSTCFLAGS="${suppress_host_varnings}" KCFLAGS="${suppress_target_varnings}" ARCH=riscv CROSS_COMPILE=/opt/toolchains/riscv64-RV64GC-linux-glibc/1.9.8/bin/riscv64-rv64gc-linux-gnu-
Здесь, первая команда make конфигурирует исходный код, а вторая - непосредственно выполняет сборку. Таким образом, запустив данный скрипт в каталоге u-boot-2021.10-3.8.2, мы получим интересующие нас файлы u-boot-2021.10-3.8.2/u-boot.bin и u-boot-2021.10-3.8.2/spl/u-boot-spl.bin.
Первичный загрузчик u-boot-spl.bin требует дополнительной обработки с помощью утилиты spl_tool, которую мы приготовили ранее. Теперь нам остается выполнить следующую команду:
tools-20231107/spl_tool/spl_tool -c -f u-boot-2021.10-3.8.2/spl/u-boot-spl.bin
и получить файл u-boot-2021.10-3.8.2/spl/u-boot-spl.bin.normal.out.
OpenSBI
Сборку opensbi, так же как и u-boot, лучше осуществлять с помощью приготовленного заранее скрипта:
#!/bin/sh
U_BOOT_PATH="../u-boot-2021.10-3.8.2"
make ARCH=riscv CROSS_COMPILE=/opt/toolchains/riscv64-RV64GC-linux-glibc/1.9.8/bin/riscv64-rv64gc-linux-gnu- \
PLATFORM=generic FW_PAYLOAD_PATH=${U_BOOT_PATH}/u-boot.bin \
FW_FDT_PATH=${U_BOOT_PATH}/arch/riscv/dts/starfive_visionfive2.dtb \
FW_TEXT_START=0x40000000
Выполнив данный скрипт в каталоге opensbi-1.2-3.8.2/, мы получим файл fw_payload.bin, который, как и SPL, потребует дополнительной обработки:
OPENSBI_PATH="../../opensbi-1.2-3.8.2"
U_BOOT_PATH="../../u-boot-2021.10-3.8.2"
cd tools-20231107/uboot_its
cp ${OPENSBI_PATH}/build/platform/generic/firmware/fw_payload.bin ./
${U_BOOT_PATH}/tools/mkimage -f visionfive2-uboot-fit-image.its \
-A riscv -O u-boot -T firmware visionfive2_fw_payload.img
Представленные выше команды дадут нам подписанный образ загрузчика tools-20231107/uboot_its/visionfive2_fw_payload.img.
QSPI Flash Layout
Итак мы получили все необходимое для приготовления образа QSPI Nor Flash.
Формат QSPI Nor Flash жестко фиксирован и имеет четыре раздела, три из которых должны располагаться по фиксированным адресам:
# Offset | Length | Size | Part Name | Description
# ----------+-----------+-------+-----------+-----------------
# 0x0 0x40000 256k spl SPL
# 0xf0000 0x10000 64k uboot-env U-Boot environment variables
# 0x100000 0x300000 3072k uboot fw_payload.img (OpenSBI + U-Boot)
# 0xf00000 0x100000 1024k data Reseved
На сайте компании StarFive приведена несколько иная таблица разделов, однако, действительные размеры разделов лучше согласовывать с записью:
#
# linux/arch/riscv/boot/dts/starfive/jh7110-visionfive-v2.dtsi:
#
&qspi {
nor_flash: nor-flash@0 {
compatible = "jedec,spi-nor";
reg=<0>;
cdns,read-delay = <5>;
spi-max-frequency = <100000000>;
cdns,tshsl-ns = <1>;
cdns,tsd2d-ns = <1>;
cdns,tchsh-ns = <1>;
cdns,tslch-ns = <1>;
status = "okay";
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
spl@0 {
reg = <0x0 0x40000>;
};
uboot-env@f0000 {
reg = <0xf0000 0x10000>;
};
uboot@100000 {
reg = <0x100000 0x300000>;
};
data@f00000 {
reg = <0xf00000 0x100000>;
};
};
};
};
в dts-файле linux/arch/riscv/boot/dts/starfive/jh7110-visionfive-v2.dtsi исходных кодов ядра Linux. Это позволит обновлять загрузчик с помощью mtd-utils, прямо на работающей системе.
Теперь, зная точный формат QSPI Nor Flash, мы можем составить скрипт, который создаст образ для записи микросхемы GD25Q128E объемом 16MB:
#!/bin/sh
#
# Create empty image:
#
image_file=visionfive2.spi-flash.image
image_size_kb=16384
dd if=/dev/zero bs=1k count=${image_size_kb} | tr '\000' '\377' > ${image_file}
#
# Clear U-Boot environment:
#
env_size_kb=64
dd if=/dev/zero of=${image_file} bs=1k count=${env_size_kb} seek=960 conv=notrunc
#
# write spl:
#
spl_size_kb=256
dd if=u-boot-2021.10-3.8.2/spl/u-boot-spl.bin.normal.out of=${image_file} bs=1k conv=notrunc
#
# write fw_payload:
#
fw_size_kb=3072
dd if=tools-20231107/uboot_its/visionfive2_fw_payload.img of=${image_file} bs=1k seek=1024 conv=notrunc
В результате работы данного скрипта мы получим файл visionfive2.spi-flash.image.
Программатор CH341a
Для записи образа visionfive2.spi-flash.image нам понадобится программатор, например ch341a и прищепка, показанные на следующем рисунке.
![Рис.2. CH341a Mini Programmer Рис.2. CH341a Mini Programmer](https://habrastorage.org/getpro/habr/upload_files/845/762/8c4/8457628c40d0939b33edb25ba268d5e6.png)
Программатор необходимо доработать так, чтобы на шине данных он давал не 5V, а 3.3V. На следующем видео весьма подробно описан процесс доработки:
Прошивку мы будем осуществлять с помошью утилиты flashrom версии 1.3.0.
Если вы правильно подключили программатор, как показано на рис.3, то набрав следующую команду, вы увидите обнаруженное устройство "GD25LQ128C/GD25LQ128D".
bash-5.2# flashrom --programmer ch341a_spi -VV
flashrom v1.2 on Linux 6.1.20 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
flashrom was built with libpci 3.7.0, GCC 11.2.0, little endian
Command line (3 args): flashrom --programmer ch341a_spi -VV
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Initializing ch341a_spi programmer
Device revision is 3.0.4
The following protocols are supported: SPI.
Probing for AMIC A25L010, 128 kB: probe_spi_rdid_generic: id1 0xc8, id2 0x6018
. . .
Found GigaDevice flash chip "GD25LQ128C/GD25LQ128D" (16384 kB, SPI).
This chip may contain one-time programmable memory. flashrom cannot read
and may never be able to write it, hence it may not be able to completely
clone the contents of this chip (see man page for details).
This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE
The test status of this chip may have been updated in the latest development
version of flashrom. If you are running the latest development version,
please email a report to flashrom@flashrom.org if any of the above operations
work correctly for you with this flash chip. Please include the flashrom log
file for all operations you tested (see the man page for details), and mention
which mainboard or programmer you tested in the subject line.
Thanks for your help!
No operations were specified.
На сообщение о том, что операции: PROBE READ ERASE WRITE еще не протестированы, можно не обращать внимание, так как разработчики flashrom писали, что код функций для работы с "GD25Q128E" полностью совпадает с теми, что используются для "GD25LQ128C", просто они еще не успели привести сообщения в порядок.
![Рис.3. Подключение CH341A к микросхеме GD25Q128E Рис.3. Подключение CH341A к микросхеме GD25Q128E](https://habrastorage.org/getpro/habr/upload_files/777/724/392/77772439279b49f3475f7b2e31d6ea28.png)
Прежде чем шить новый образ visionfive2.spi-flash.image, мы рекомендуем сохранить существующую прошивку на случай, если будет необходимо восстановить предыдущее содержимое GD25Q128E. Сделать это лучше несколько раз и сравнить контрольные суммы архивных образов:
bash-5.2# flashrom --programmer ch341a_spi -r GD25Q128E.bin -c "GD25LQ128C/GD25LQ128D"
bash-5.2# flashrom --programmer ch341a_spi -r GD25Q128E-2.bin -c "GD25LQ128C/GD25LQ128D"
bash-5.2# flashrom --programmer ch341a_spi -r GD25Q128E-3.bin -c "GD25LQ128C/GD25LQ128D"
bash-5.2# sha256sum GD25Q128E-2.bin GD25Q128E-3.bin GD25Q128E.bin
658de4da3c9ec801e8c0c2cee8ba07bfb387019a682a15d9350e0103d52e372f GD25Q128E-2.bin
658de4da3c9ec801e8c0c2cee8ba07bfb387019a682a15d9350e0103d52e372f GD25Q128E-3.bin
658de4da3c9ec801e8c0c2cee8ba07bfb387019a682a15d9350e0103d52e372f GD25Q128E.bin
Если контрольные суммы совпадают, то сохраненная копия верна и можно обновлять содержимое GD25Q128E с помощью команды:
bash-5.2# flashrom --programmer ch341a_spi --write visionfive2.spi-flash.image -c "GD25LQ128C/GD25LQ128D"
В случае успеха, на консоль будет выведено сообщение:
bash-5.2# flashrom --programmer ch341a_spi --write visionfive2.spi-flash.image -c "GD25LQ128C/GD25LQ128D"
flashrom v1.2 on Linux 6.1.20 (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found GigaDevice flash chip "GD25LQ128C/GD25LQ128D" (16384 kB, SPI) on ch341a_spi.
. . .
Reading old flash chip contents... done.
Erasing and writing flash chip... Erase/write done.
Verifying flash... VERIFIED.
Теперь надо перевести переключатели вправо (RGPIO_0 = 0, RGPIO_1 = 0
), как показано на рис.4 и можно подавать питание на плату.
![Рис.4. VisionFive2 RGPIO_0 = 0, RGPIO_1 = 0 Рис.4. VisionFive2 RGPIO_0 = 0, RGPIO_1 = 0](https://habrastorage.org/getpro/habr/upload_files/d00/48c/dda/d0048cdda96a469120b575c5e0b9f2f4.png)
Подключив UART-to-USB адаптер, как показано на рис.4, и установив скорость передачи данных 115200n8, в терминале можно наблюдать процесс загрузки:
U-Boot SPL 2021.10 (Feb 12 2023 - 18:15:33 +0800)
DDR version: dc2e84f0.
Trying to boot from SPI
OpenSBI v1.2
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|
Platform Name : StarFive VisionFive V2
Platform Features : medeleg
Platform HART Count : 5
Platform IPI Device : aclint-mswi
Platform Timer Device : aclint-mtimer @ 4000000Hz
Platform Console Device : uart8250
Platform HSM Device : jh7110-hsm
Platform PMU Device : ---
Platform Reboot Device : pm-reset
Platform Shutdown Device : pm-reset
Firmware Base : 0x40000000
Firmware Size : 292 KB
Runtime SBI Version : 1.0
. . .
. . .
. . .
In: serial@10000000
Out: serial@10000000
Err: serial@10000000
Model: StarFive VisionFive V2
Net: eth0: ethernet@16030000, eth1: ethernet@16040000
switch to partitions #0, OK
mmc1 is current device
found device 1
bootmode flash device 1
** Invalid partition 3 **
Couldn't find partition mmc 1:3
Can't set block device
** Invalid partition 3 **
Couldn't find partition mmc 1:3
Can't set block device
Hit any key to stop autoboot: 2 0
StarFive #
Переменные окружения U-Boot
Исходный код U-Boot не предусматривает все возможные источники загрузки и, кроме того, не рассчитан на загрузку OS общего назначения, таких как Debian, Ubuntu, Fedora. Ранее мы говорили о том, что добиться нужного нам поведения загрузчика можно с помощью переменной CONFIG_BOOTCOMMAND
в файле u-boot-2021.10-3.8.2/configs/starfive_visionfive2_defconfig. Однако теперь, когда в нашем распоряжении есть собственный загрузчик, мы можем определять нужные нам переменные и процедуры, сохраняя их во втором разделе QSPI Nor Flash.
Для того, чтобы читателю было проще начать работу с U-Boot, приведем здесь patch:
diff --unified -Nr u-boot-2021.10-3.8.2-orig/include/config_distro_bootcmd.h u-boot-2021.10-3.8.2/include/config_distro_bootcmd.h
--- u-boot-2021.10-3.8.2-orig/include/config_distro_bootcmd.h 2023-10-18 15:15:18.000000000 +0300
+++ u-boot-2021.10-3.8.2/include/config_distro_bootcmd.h 2023-11-13 20:59:08.609989837 +0300
@@ -37,7 +37,7 @@
#define BOOTENV_DEV_BLKDEV(devtypeu, devtypel, instance) \
"bootcmd_" #devtypel #instance "=" \
- "devnum=" #instance "; " \
+ "setenv devnum " #instance "; " \
"run " #devtypel "_boot\0"
#define BOOTENV_DEV_NAME_BLKDEV(devtypeu, devtypel, instance) \
diff --unified -Nr u-boot-2021.10-3.8.2-orig/include/configs/starfive-visionfive2.h u-boot-2021.10-3.8.2/include/configs/starfive-visionfive2.h
--- u-boot-2021.10-3.8.2-orig/include/configs/starfive-visionfive2.h 2023-10-18 15:15:18.000000000 +0300
+++ u-boot-2021.10-3.8.2/include/configs/starfive-visionfive2.h 2023-11-13 20:59:08.609989837 +0300
@@ -82,7 +82,7 @@
#endif
/* HACK these should have '#if defined (stuff) around them like zynqp*/
-#define BOOT_TARGET_DEVICES(func) func(MMC, mmc, 0) func(DHCP, dhcp, na)
+#define BOOT_TARGET_DEVICES(func) func(MMC, mmc, 1) func(MMC, mmc, 0) func(NVME, nvme, 0) func(DHCP, dhcp, na)
#include <config_distro_bootcmd.h>
@@ -202,6 +202,7 @@
"bootenv=uEnv.txt\0" \
"bootenv_sdk=vf2_uEnv.txt\0" \
"boot_devs=mmc nvme\0" \
+ "nvme_devnum=0\0" \
"emmc_devnum=0\0" \
"sd_devnum=1\0" \
"mmc_devnum_l=1 0\0" \
@@ -261,6 +262,8 @@
"bootdir=/boot\0" \
"bootpart=3\0" \
"rootpart=4\0" \
+ "bootcmd_nvme1=setenv devnum 1; run mmc_boot\0" \
+ "bootcmd_nvme0=setenv devnum 0; run nvme_boot\0" \
"load_distro_uenv=" \
"fatload ${bootdev} ${devnum}:${bootpart} ${loadaddr} /${bootenv}; " \
"env import ${loadaddr} ${filesize}; \0" \
который позволяет обеспечить загрузку из четырех источников: mmc1, mmc0, nvme0, dhcp и, кроме того, позволить нам загружать GRUB с NVME диска.
Готовый образ visionfive2.spi-flash.image для GD25Q128E можно загрузить с FTP-сервера, где также размещен выпуск Radix cross Linux предназначенный для работы на VisionFive2.