До недавнего времени разрабатывать IoT приложения на C# можно было только для компьютеров построенных на архитектуре ARM или x86. Поддержка RISC-V процессоров для платформы .NET уже давно в стадии разработки. В начале этого года был представлен не официальный .NET 8.0 SDK, который уже сейчас вы сможете запустить на RISC-V процессоре под ОС Debian/Ubuntu. В качестве платформы запуска возьмем одноплатный компьютер Sipeed Lichee RV на RISC-V процессоре Allwinner D1 (ядро Alibaba/T-Head Xuantie C906 RISC-V). В первой части поста рассмотрим установку .NET 8.0 SDK на Sipeed Lichee RV. Во второй, запустим приложение для работы с контактами GPIO и датчиком BME280 для замера температуры, влажности и давления.
❯ Предыстория
Первоначальная работа по портированию платформы .NET для RISC-V архитектуры началась в 2023 году инженером Dong-Heon Jung из Samsung. Тогда платформа .NET максимум, что могла выполнить, это вывести «helloworld». Сейчас CoreCLR позволяет выполнять многие вещи, но пока все работает достаточно медленно. В текущие планы портирования не входит JIT оптимизация, функций SIMD, главное это выполнение любого кода. Основная ветка обсуждения процесса портирования RISC-V support #36748.
❯ Подготовка образа
Для Sipeed Lichee RV необходимо загрузить образ от Олега, одного из ведущих мейнтейнеров проекта Armbian. В ветке обсуждения можно отслеживать последние изменения. На данный момент порт работает на платах Nezha D1 и Lichee RV (Dock).
- Для платы Nezha D1 поддерживается: HDMI, LAN, USB, аналоговый вывод через 3.5 jack;
- Для платы Lichee RV Dock поддерживается: HDMI, WiFi, USB, USB-LAN.
Образы можно скачать по ссылке. Все дальнейшие работы выполнялись на образе Armbian_23.09_Nezha_lunar_current_6.1.0.img.xz от 2023.09.30. Образ построен на Ubuntu 23.04 (lunar), ядро Linux 6.1.0-rc3-d1.
Для запуска потребуется micro-SD карта и программа balenaEtcher, с помощью которой образ записывается на карту-памяти.
Armbian — это самый популярный дистрибутив Linux, предназначенный для одноплатных компьютеров построенных на ARM процессоре, список поддерживаемых плат огромен: Orange Pi, Banana Pi, Odroid, Olimex, Cubietruck, Roseapple Pi, Pine64, NanoPi и др.
Первый запуск Armbian сопровождается инициализацией системных настроек, в рамках которых необходимо ответить на несколько вопросов связанных с конфигурацией системы.
❯ Установка .NET 8.0 SDK на Sipeed Lichee RV
Готовая сборка опубликована на GitHub dkurt/dotnet_riscv. Вы можете установить SDK, так и отдельно Runtime среду исполнения AspNetCore и NETCore. Для RISC-V архитектуры определен RID: linux-riscv64.
Установку выполним в каталог /usr/share/dotnet. Скрипт для установки:
$ export INSTALLPATH=/usr/share/dotnet
$ sudo apt-get update
$ sudo apt-get install -y wget tar
$ wget -O dotnet-sdk-riscv64.tar.gz "https://github.com/dkurt/dotnet_riscv/releases/download/v8.0.100/dotnet-sdk-8.0.100-linux-riscv64.tar.gz"
$ sudo mkdir -p $INSTALLPATH
$ sudo tar -xf dotnet-sdk-riscv64.tar.gz -C "$INSTALLPATH" --checkpoint=.100
$ rm dotnet-sdk-riscv64.tar.gz
$ sudo ln -s $INSTALLPATH/dotnet /usr/bin/dotnet
По итогу установки отобразим информацию об SDK:
root@nezha:~# dotnet --info
.NET SDK:
Version: 8.0.100
Commit: 57efcf1350
Workload version: 8.0.100-manifests.6c33ef20
Runtime Environment:
OS Name: ubuntu
OS Version: 23.04
OS Platform: Linux
RID: linux-riscv64
Base Path: /usr/share/dotnet/sdk/8.0.100/
.NET workloads installed:
Workload version: 8.0.100-manifests.6c33ef20
There are no installed workloads to display.
Host:
Version: 8.0.0
Architecture: riscv64
Commit: 5535e31a71
.NET SDKs installed:
8.0.100 [/usr/share/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 8.0.0 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 8.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
...
Теперь можно запускать .NET приложения c помощью команды dotnet myapp.dll.
❯ Создание первого приложения
Создадим первое приложение DotnetConsoleAppCoreCLRInfo.
$ dotnet new console -n DotnetConsoleAppCoreCLRInfo
Перейдем в каталог DotnetConsoleAppCoreCLRInfo и заменим код в Program.cs на следующий:
using System;
using System.Runtime.InteropServices;
namespace DotnetConsoleAppCoreCLRInfo
{
class Program
{
static void Main(string[] args)
{
//Runtime Info
Console.WriteLine(".NET console application!");
bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null;
//output
Console.WriteLine($"Hello World {(isMono ? "from Mono!" : "from CoreCLR!")}");
Console.WriteLine(typeof(object).Assembly.FullName);
Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly ());
Console.WriteLine($".NET version: {RuntimeInformation.FrameworkDescription}");
Console.WriteLine($"OS architecture: {RuntimeInformation.OSArchitecture}");
Console.WriteLine($"OS Id: {RuntimeInformation.RuntimeIdentifier}");
Console.WriteLine("Successfully!");
}
}
}
Запустим приложение командой:
$ dotnet run
Запуск приложения DotnetConsoleAppCoreCLRInfo
В программном коде выполняется проверка окружения «Mono.RuntimeStructs», Mono или CoreCLR. Еще до портирования .NET на RISC-V можно было запускать программы под Mono в режиме работы ОС поверх гипервизора RISC-V Open Source Supervisor Binary Interface (OpenSBI). Процесс восстановления зависимостей и компиляции занял ~8 минут. Если код компилировать локально на x86, то процесс существенно ускоряется.
Приложение dotnet-iot-coreclr-info-riscv64 на GitHub — .NET IoT Samples.
❯ Работа с контактами GPIO
На несущей Dock плате для Lichee RV выведен разъем GPIO на 40-pins с шагом 2,54 мм частично совместимый с аналогичным разъемом Raspberry Pi, так же присутствуют линии питания на 3.3V и 5V, но некоторые выводы GND не соответствуют выводам на Raspberry Pi.
- Подключим светодиод к контакту с номером линии 144, метка PE16.
Для управления контактами GPIO используется библиотека Libgpiod. Библиотека Libgpiod позволяет работать с контактами GPIO одноплатного компьютера из .NET среды исполнения, она может быть установлена на Linux (Armbian). Библиотека не является аппаратно-зависимой, что позволяет ее использовать на различных одноплатных компьютерах архитектуры ARM32, ARM64, RISC-V и x86. Как работать с GPIO рекомендуется ознакомиться с публикацией Программирование на Python и установка Docker для Sipeed Lichee RV RISC-V.
Теперь необходимо установить библиотеку Libgpiod версии 1.6.4, несмотря на наличие последней версии 2.1.1. Начиная с версии 2.0 для библиотеки libgpiod требуется интерфейс uAPI v2 GPIO символьных устройств, который впервые был добавлен в ядро Linux 5.10. Соответственно изменились вызовы интерфейсов. Поддержка второй версии Commit Add libgpiodv2 support (#2184) уже внесена в библиотеку System.Device.Gpio, но еще не вышел новый релиз с дополнениями.
Скрипт установки библиотеки libgpiod и утилит для x86/ARM32/ARM64/RISC-V:
$ sudo apt-get update
$ sudo apt-get install -y curl
$ curl -SL --output setup-libgpiod.sh https://raw.githubusercontent.com/devdotnetorg/docker-libgpiod/dev/setup-libgpiod.sh
$ chmod +x setup-libgpiod.sh
$ sudo ./setup-libgpiod.sh
Рекомендуется вариант установки из бинарников (4) Installation from binaries.
Создадим новое приложение DotnetBlinkLEDApp по аналогии как в предыдущем варианте и заменим код в Program.cs на следующий:
using System;
using System.Device.Gpio;
using System.Device.Gpio.Drivers;
using System.Threading;
namespace DotnetBlinkLEDApp
{
class Program
{
static void Main(string[] args)
{
/*Blink an LED
*
https://docs.microsoft.com/en-us/dotnet/iot/tutorials/blink-led
*
*/
Console.WriteLine("Blinking LED.");
//for Linux
//Board: Lichee RV.
//https://habr.com/ru/companies/timeweb/articles/649327/
//Pin: PE16.
//GPIOCHIP = 0, LED_PIN = 144
const int GPIOCHIP = 0;
const int LED_PIN = 144;
GpioController controller;
var drvGpio = new LibGpiodDriver(GPIOCHIP);
controller = new GpioController(PinNumberingScheme.Logical, drvGpio);
controller.OpenPin(LED_PIN, PinMode.Output);
controller.Write(LED_PIN,PinValue.Low);
bool ledOn = false;
const int count=5;
for(int i=1;i<count;i++)
{
Console.WriteLine($"Step: {i} of {count}");
controller.Write(LED_PIN, ((ledOn) ? PinValue.High : PinValue.Low));
Console.WriteLine($"LED: {((ledOn) ? "High" : "Low")}");
Thread.Sleep(100);
ledOn = !ledOn;
}
}
}
}
После запуска приложения светодиод должен мигать, но с большей задержкой, чем указано в программном коде из-за медленной работы CoreCLR.
Приложение dotnet-iot-blink-riscv64 на GitHub — .NET IoT Samples.
❯ Подключение датчика Bosh BME280
Датчик BME280 компании Bosch Sensortec используется для замера физических величин, таких как температура, влажность, атмосферное давление, подключается по шине I2C. В образе уже включена шина I2C. Контакты для подключения шины I2C:
- Контакт PB1 — линия SDA (Serial Data) — эта линия отвечает непосредственно за передаваемые данных;
- Контакт PB0 — линия SCL (Serial Clock) — эта линия отвечает за синхронизацию соединения.
Для проверки включения шины I2C необходимо выполнить команду:
$ ls -l /sys/bus/i2c/devices/i2c-*
Проверка включения шины I2C:
root@nezha:~# ls -l /sys/bus/i2c/devices/i2c-*
lrwxrwxrwx 1 root root 0 Jan 2 1970 /sys/bus/i2c/devices/i2c-0 -> ../../../devices/platform/soc/5500000.hdmi/i2c-0
lrwxrwxrwx 1 root root 0 Jan 2 1970 /sys/bus/i2c/devices/i2c-1 -> ../../../devices/platform/soc/2502800.i2c/i2c-1
В списке присутствует второе устройство /sys/bus/i2c/devices/i2c-1, то шина I2C включена и не требует дополнительных действий для работы. Первое устройство это шина I2C входящая в состав вывода HDMI.
Более подробно о подключение датчика BME280 пост .NET IoT. Часть 4. Шина I2C, подключение датчика Bosh BME280.
По аналогии с примером GPIO создаем новое приложение DotnetBME280App и содержимое Program.cs заменяем на вариант из проекта dotnet-iot-bme280 на GitHub — .NET IoT Samples. Номер шины I2C остается тот же:
const int busId = 1;
Запускаем приложение:
Запуск приложения DotnetBME280App
Шина I2C успешно работает, как и датчик BME280.
Приложение dotnet-iot-bme280-riscv64 на GitHub — .NET IoT Samples.
Шаблоны проектов для RISC-V и удобная удаленная отладка есть в расширение .NET FastIoT, пост Простая разработка IoT приложений на C# для Raspberry Pi и других одноплатников, на Linux.
❯ Вывод
Уже сейчас предварительные версии IoT приложений для RISC-V процессоров можно успешно тестировать. Базовая периферия в виде управления контактами GPIO и шина I2C работает, пакеты NuGet в проект подтягиваются. Работает медленно, но все же работает. Работа над портированием RISC-V ведется активно, в дальнейшем будет предоставлен скрипт сборки SDK с последними Commits.
❯ Ресурсы
- RISC-V support Issue #36748 — GitHub dotnet/runtime
- Build .NET SDK for RISC-V — GitHub dkurt/dotnet_riscv
- .NET is a cross-platform runtime — GitHub Xinlong-Wu/runtime
- .NET IoT Samples — GitHub devdotnetorg/dotnet-iot-samples