Архитектура RISC-V корнями уходит к началу 1980-х годов, группа под руководством Дэвида Паттерсона в стенах университета Беркли разработала архитектуры RISC-I и RISC-II. Долгое время архитектуре приходилось существовать в тени лицензируемых ARM и MIPS ядер. Архитектура RISC-V появилась в 2010 году, и поддерживается Linux Foundation. Отметка в 10 миллиардов произведенных ядер была преодолена за 12 лет.
Сейчас RISC-V может сыграть большую роль в становлении российской микроэлектроники. Компании CloudBEAR и Syntacore работают над процессорами собственной микроархитектуры, совместимыми с системой команд RISC-V. Архитектура RISC-V позволяет нашим разработчикам создавать энергоэффективные процессоры сравнимого с мировым уровня и сохранять программную совместимость со всеми программами, созданными для экосистемы RISC-V во всем мире. Большая часть RISC-V устройств на сегодняшний день требуют кросс-компиляции кода, чем мы сегодня и займемся.
Исследуемое RISC-V устройство
Нашим подопытным является плата MangoPi MQ PRO D1, выполненная в нестандартном розовом цвете.

Характеристики у данной платы следующие:
SoC - Allwinner D1 C906 RISC-V частотой 1 ГГц с 2D ускорителем
1 Гб DDR3L ОЗУ (есть версия платы с 512 Мб)
Разъем для карты MicroSD под систему
Mini HDMI порт
24-контактный разъем для камеры
RTL8723DS модуль Wi-Fi 2,4 ГГц 802.11b/g/n + Bluetooth 4.2 и гнездо для антенны
2 USB type C порта под питание и подключение периферии
40-pins GPIO гребенка, идентичная оной на Raspberry Pi
Данная плата стоит чуть больше 2000 рублей на Aliexpress, что делает ее идеальным кандидатом на знакомство с архитектурой RISC-V.
Установка операционной системы на Mango Pi.
Для работы миникомпьютера нужна специализированная сборка операционной системы. Мы возьмем образ системы Armbian 22.08.0-trunk Jammy c ядром Linux 5.19.0-rc1-d1 с официального сайта mangopi: ссылка. Установка системы делается также, как и на любой Raspberry или его клоне - образ разворачивается на MicroSD карточку, например с помощью Raspberry Pi Imager. Дальше просто вставляем флешку в разъем и подаем питание на плату. Плата начнет загружаться и при подключении mini HDMI к монитору, вы будете видеть логи загрузки.
При первом запуске система предложит установить пароль root-пользователя, а также имя и пароль обычного пользователя. Для подключения WiFi и настройки SSH можно воспользоваться встроенной утилитой armbian-config. В данной утилите с псевдографикой в меню Network можно удобно подключиться к домашней WiFi-сети и включить удаленный доступ по SSH.
С помощью команды ifconfig узнаем ip-адрес нашей MangoPi, чтобы в дальнейшем работать дистанционно.
Установка кросс-компилятора
Сама по себе плата не очень производительная, ведь у нее всего одно процессорное ядро, поэтому для экспериментов наше ПО мы будем собирать с помощью кросс-компилятора на основной машине, и затем запускать на MangoPi.
Для экспериментов подойдет любая актуальная версия Ubuntu, мы воспользуемся версией 23.04. Для сборки программы под RISC-V архитектуру на x86 машине понадобится собрать и установить кросс-компилятор.
Установим зависимости:
sudo apt-get install git cmake autoconf automake autotools-dev curl \ libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo \ gperf libtool patchutils bc zlib1g-dev libexpat-dev ninja-build
При установке на Ubuntu 20.04 была небольшая ошибка с установкой пакета libglib2.0-dev, она решилась указанием версии пакета libglib2.0
sudo apt-get install libglib2.0-dev libglib2.0-0=2.64.6-1~ubuntu20.04.3
Скачиваем исходный код кросс-компилятора:
git clone https://github.com/riscv/riscv-gnu-toolchain cd riscv-gnu-toolchain
Создадим директорию под собираемый компилятор:
sudo mkdir -m 777 -p /opt/riscv/bin
И добавим наш будущий компилятор в переменную PATH:
export PATH="/opt/riscv/bin:$PATH"
Можно приступать к сборке компилятора. Стоит указать параметр -j с числом физических ядер вашего процессора, чтобы при компиляции использовались все ядра.
./configure --prefix=/opt/riscv make linux -j$(nproc)
Сборка кросс-компилятора - дело небыстрое, поэтому запасаемся терпением и кофе, и идем полчаса прогуляться. Возвращаясь с прогулки, видим, что наш компьютер перестал шуметь и переходим к сборке тестовой программы.
#include <stdio.h> int main() { printf("Hello World\n"); return 0; }
Соберем его с помощью нашего кросс-компилятора:
riscv64-unknown-linux-gnu-gcc hello.c -o hello
Чтобы проверить, что мы действительно собрали приложение для архитектуры RISC-V, можно воспользоваться командами readelf или file:
Утилита readelf
$ /opt/riscv/bin/riscv64-unknown-linux-gnu-readelf -h hello ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2s complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: RISC-V Version: 0x1 Entry point address: 0x0 Start of program headers: 0 (bytes into file) Start of section headers: 848 (bytes into file) Flags: 0x5, RVC, double-float ABI Size of this header: 64 (bytes) Size of program headers: 0 (bytes) Number of program headers: 0 Size of section headers: 64 (bytes) Number of section headers: 12 Section header string table index: 11
Утилита file
$ file hello hello: ELF 64-bit LSB relocatable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), not stripped
Для удобства переноса файлов с основной машины на MangoPi можно подключиться по SFTP, тогда файловое пространство MangoPi будет доступной папкой в файловом менеджере, что облегчит копирование.

Перекинув собранный бинарный файл на MangoPi, программа запустится. Теперь можно переходить к более полезной задаче - кросс-компиляции библиотеки OpenCV под RISC-V.
Кросс-компиляция библиотеки OpenCV для RISC-V
Чтобы узнать возможности платы, давайте соберем под нее OpenCV. Поскольку наша одноядерная плата хорошо вписывается в задачу edge computing - получения изображения с веб-камеры, некоторой обработки и передачи информации, извлеченной из изображений, в хранилище.
Качаем исходный код OpenCV:
git clone https://github.com/opencv/opencv.git cd opencv
Создаем папку для билда:
mkdir build_mangopi && cd build_mangopi
Собираем OpenCV, гулять уже не идем, потому что OpenCV собирается значительно быстрее.
cmake -DCMAKE_TOOLCHAIN_FILE=../platforms/linux/riscv64-gcc.toolchain.cmake -DCMAKE_C_COMPILER=/opt/riscv/bin/riscv64-unknown-linux-gnu-gcc -DCMAKE_CXX_COMPILER=/opt/riscv/bin/riscv64-unknown-linux-gnu-g++ -DBUILD_SHARED_LIBS=OFF ../ make -j$(nproc)
В OpenCV собираются несколько тестовых приложений. Перекидываем их на MangoPi и запускаем приложения из папки build_mangopi/bin, собранные с OpenCV. Перенесем файлы из папки build_mangopi/bin на плату и запустим бенчмаркинг модуля core:
build_mangopi/bin/opencv_perf_core
Пришло время кросс-компиляцией собрать собственное приложение, использующее OpenCV. Протестируем скорость работы фильтра Гаусса с большим ядром, повторив запуск фильтра 10 раз. Создадим следующие cpp и cmake файлы.
gaussian.cpp
#include <iostream> #include <opencv2/core.hpp> #include <opencv2/imgproc.hpp> using namespace cv; using namespace std; int main() { Mat img(2560, 2027, CV_32FC3, Scalar::all(0)); RNG rng; rng.fill(img, RNG::UNIFORM, 0, 255); Mat dst; TickMeter tick; for (size_t i = 0; i < 10; ++i) { tick.start(); GaussianBlur(img, dst, Size(19, 19), 0.84089642); tick.stop(); cout << i << " | " << tick.getAvgTimeMilli() << " ms" << endl; } return 0; }
CMakeLists.txt
cmake_minimum_required(VERSION 3.10) project( TestGaussian ) find_package( OpenCV REQUIRED ) include_directories( ${OpenCV_INCLUDE_DIRS} ) add_executable( TestGaussian gaussian.cpp ) target_link_libraries( TestGaussian ${OpenCV_LIBS} )
Команды для кросс-компиляции нашего приложения с использованием функций OpenCV собранным нами компилятором:
cmake -DCMAKE_TOOLCHAIN_FILE=~/opencv/platforms/linux/riscv64-gcc.toolchain.cmake \ -DCMAKE_C_COMPILER=/opt/riscv/bin/riscv64-unknown-linux-gnu-gcc \ -DCMAKE_CXX_COMPILER=/opt/riscv/bin/riscv64-unknown-linux-gnu-g++ \ -DOpenCV_DIR=~/opencv/build_mangopi -DCMAKE_EXE_LINKER_FLAGS="-latomic" ../ make -j$(nproc)
После того, как у вас собрался файл TestGaussian, перенесите его на MangoPi, и запустите. На моем устройстве программа выдала следующие результаты работы. Интересно что разброс времени работы от запуска к запуску одного и того же фильтра достигает 5%.
./TestGaussian 0 | 15376.1 ms 1 | 14943.1 ms 2 | 14808.8 ms 3 | 14721.3 ms 4 | 14672.3 ms 5 | 14652.8 ms 6 | 14625.9 ms 7 | 14616.6 ms 8 | 14601.1 ms 9 | 14599.2 ms
Поздравляем! У вас получилось собрать RISC-V кросс-компилятор, библиотеку OpenCV и собственное приложение с её использованием. В следующих статьях мы попробуем углубиться в оптимизацию и ускорение вычислений под RISC-V, чтобы улучшить данный результат.
Большая благодарность инженерам компании YADRO Владимиру Дуднику, Максиму Шабунину за помощь в подготовке данной статьи.
