Вступ до програмування вбудованих систем мовою Ada#

Вступ#

До вбудовуваних систем належать програмні системи, що виконуються на пристроях з обмеженою потужністю. Часто до таких систем висувають особливі вимоги надійності, роботи в реальному часі, обмеженого споживання тощо.

Мова Ada чудово підходить для реалізації таких систем завдяки своїм можливостям. Оскільки Ada - компілювальна мова, програми на ній дуже енергоефективні. Початкова орієнтація мови на надійність і безпеку гармонує з цілями розробки вбудованих систем. Гнучкі можливості специфікації подання дають змогу легко взаємодіяти з обладнанням на низькому рівні. Водночас, потужна система типів дає змогу природно виражати поняття прикладної галузі. Нарешті, у стандарті мови є окремий розділ, присвячений підтримці вбудованих систем і систем реального часу. Реалізуючи вимоги стандарту, Ada дає змогу програмувати пристрої без необхідності використання універсальних операційних систем, водночас пропонуючи ефективну підтримку багатозадачного середовища на пристроях із досить обмеженими можливостями.

Метою цієї статті є допомога початківцям розібратися з наявним інструментарієм програмування вбудовуваних систем мовою Ada на прикладі створення простої програми. Ми розглянемо встановлення та налаштування ПЗ на прикладі плати розробника STM32F407.

Необхідні інструменти та компоненти#

Для розробки вбудованих систем нам потрібні такі інструменти:

  • Крос-компілятор

  • Бібліотека часу виконання (RTL)

  • Бібліотеки драйверів (наприклад, Ada Drivers Library)

Крос-компілятор#

Крос-компілятор дає змогу збирати виконувані модулі для процесора, відмінного від поточного. Наприклад, побудувати код для ARM, використовуючи робочу станцію з архітектурою AMD64. Пакетний менеджер Alire має кілька готових до використання крос-компіляторів для наступних архітектур процесорів:

  • ARM

  • AVR

  • RISC-V

  • xtensa_esp32 (експериментальний, не рекомендується для початківців)

Бібліотека часу виконання (RTL)#

Для підтримки нетривіальних конструкцій мови потрібна бібліотека часу виконання. Виявивши таку конструкцію в коді користувача, компілятор шукає в бібліотеці часу виконання відповідні типи та підпрограми і використовує їх. Логічно, що RTL тісно пов'язана з крос-компілятором, і вихід нової версії компілятора вимагає бібліотеки часу виконання відповідної версії.

Не всі конструкції мови Ada затребувані у вбудованих системах. Наприклад, обробка винятків вважається занадто витратною, щоб реалізувати її в повному обсязі. Залежно від реалізованих конструкцій мови бібліотеки часу виконання діляться на кілька типів. Наразі з GNAT доступні три типи RTL:

  • light - мінімальна версія бібліотеки, без підтримки багатозадачності та інших конструкцій;

  • light-tasking - з обмеженою підтримкою багатозадачності, так звані Ravenscar, Jorvik профілі;

  • embedded - повніша бібліотека часу виконання.

Бібліотеки часу виконання розраховані на використання з конкретним обладнанням, оскільки містять код ініціалізації та розподіл ресурсів. Крос-компілятори для ARM і RISC-V ідуть із набором бібліотек RTL для найпопулярніших пристроїв розроблення. Підтримка інших пристроїв може поширюватися як окремі Alire crates.

Бібліотеки драйверів#

Для мікропроцесорів вбудованих систем властиво мати багатий асортимент допоміжних пристроїв, як-от порти введення-виведення загального призначення, таймери, UART, SPI, I2C тощо. Для роботи з цими пристроями потрібні відповідні драйвери. Напевно, найповнішою підтримкою драйверів може похвалитися проект Ada Drivers Library (ADL). ADL поширюється під BSD ліцензією і містить код драйверів пристроїв мікропроцесорів, а також драйвера зовнішніх пристроїв, таких як датчики, дисплеї, камери тощо. Також репозиторій ADL надає приклади використання драйверів для деяких плат розробки.

На жаль, ADL не підтримує Alire, і навпаки. Частину коду ADL було скопійовано в Alire як окремі crates.

Практичний приклад: STM32F407#

Давайте розглянемо створення мінімального застосунку на прикладі мікроконтролера STM32F407. На ринку є кілька плат розробника на основі цього недорогого процесора, починаючи від Discovery Board і закінчуючи китайськими STM32 F4VE. Бібліотека часу виконання для цього чіпа має назву light-tasking-stm32f4.

Ми почнемо зі створення додатка в терміналі без використання середовища розроблення, щоб показати, як усе влаштовано, а потім окремо зупинимося на підтримці таких проєктів у GNAT Studio.

Створення проекту#

Alire допомагає нам створити відповідну заготовку для проєкту:

alr init --bin test
cd test

Наступний крок - вказати крос-компілятор у залежностях проєкту:

alr with gnat_arm_elf

Щоб проектний менеджер gprbuild знав, що потрібно використовувати крос-компілятор, необхідно вказати Target. Найпростіше це зробити в проектному файлі. Заодно вкажемо, яка бібліотека часу виконання нам потрібна. Відредагуємо test.gpr, додавши такі рядки:

for Target use "arm-eabi"; 
for Runtime ("Ada") use "light-tasking-stm32f4";

Код програми (test.adb) зробимо максимально простим:

with Ada.Text_IO;

procedure Test is
begin
   loop
      Ada.Text_IO.Put_Line ("Hello");
      delay 1.0;
   end loop;
end Test;

Усе готово, щоб зібрати виконуваний файл:

alr build

Перетворимо отриманий ELF на бінарний файл, придатний для завантаження програми:

alr exec -- arm-eabi-objcopy -Obinary bin/test bin/test.bin

Для завантаження програми в пристрої на основі процесорів STM32 зазвичай використовується апаратний відладчик ST Link v2. Discovery board має його інтегрованим прямо на платі. (Підійде також і DAPLINK.) Для управління апаратним відладчиком можна використовувати програмне забезпечення st-util, або пакет з відкритим вихідним кодом OpenOCD (який вміє також керувати DAPLINK).

Команда для завантаження програми з st-util:

st-flash --connect-under-reset write bin/test.bin 0x08000000

або з OpenOCD

openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c 'program bin/test.bin verify reset exit 0x08000000' 

Давайте скористаємося відладчиком, щоб подивитися, як виконується наша програма. В одному вікні консолі активуємо апаратний відладчик:

stutil --semihosting

або (у разі OpenOCD):

openocd -f interface/stlink.cfg -f target/stm32f4x.cfg

А в іншому запустимо GDB:

alr exec arm-eabi-gdb ./bin/test
target extended-remote :4242

або (для OpenOCD)

target extended-remote :3333
monitor arm semihosting enable

У консолі відладчика ми можемо:

  • виконувати програму по кроках за допомогою команд next, step;

  • дивитися вміст регістрів info reg;

  • перевіряти вміст пам'яті x <addr>;

  • провести повторну ініціалізацію процесора monitor reset init.

Інтеграція з GNAT Studio#

Щоб налаштувати GNAT Studio для запуску st-util для завантаження програми та налагодження, достатньо у файл проєкту додати такий код:

package Ide is
   for Program_Host use "localhost:3333";
   for Communication_Protocol use "remote";
   for Connection_Tool use "st-util";
end Ide;

Тепер запустимо студію:

alr exec gnatstudio

Студія розпізнає, що цей проєкт для вбудованих систем, і дасть вам змогу збирати, завантажувати програму та налагоджувати її за допомогою GUI.

Використання OpenOCD з GNAT Studio лише трохи складніше - потрібно підготувати конфігураційний файл, наприклад openocd.cfg:

source [find interface/stlink.cfg]
source [find target/stm32f4x.cfg]
reset_config none

Потім на цей файл потрібно послатися в пакеті IDE проєктного файлу:

package Ide is
   for Program_Host use "localhost:4242";
   for Communication_Protocol use "remote";
   for Connection_Tool use "openocd";
   for Connection_Config_File use "openocd.cfg";
end Ide;

Приклад із світлодіодом#

Серед демонстраційних проєктів у GNAT Studio є один, який може бути нам цікавий. Давайте створимо його просто зараз. Натисніть /File/New Project і виберіть "STM32F4 compatible/LED demo project". Коли студія створить файл, виправте ім'я бібліотеки часу виконання:

for Runtime ("Ada") use "light-tasking-stm32f4";

Цей приклад розрахований на OpenOCD, але переробити на використання st-util легко, просто виправте Connection_Tool у пакеті IDE в проєктному файлі.

Цей демо-проект містить код для керування портами вводу/виводу. Розібратися в устрої цього коду було б досить пізнавально, але це виходить за рамки вступної статті. Поки лише зазначимо, що цей код не включено до бібліотеки часу виконання. Драйвери, подібні до цього коду, були зібрані в окремому проекті Ada Drivers Library (ADL).

Ada Drivers Library#

Проєкт Ada Drivers Library був створений досить давно. У ньому зібрані драйвери різних пристроїв, починаючи від пристроїв, розташованих на мікроконтролерах, і закінчуючи драйверами до зовнішніх пристроїв, таких як сенсори, дисплеї і навіть камери. Завантажимо його з GitHub:

git clone https://github.com/AdaCore/Ada_Drivers_Library

Рекомендується почати ознайомлення з прикладів, що поставляються в цьому проекті. Виберіть відповідний пристрій у каталозі examples. Наприклад:

examples/STM32F429_Discovery/blinky_f429disco.gpr

На жаль, ADL не підтримує Alire, і навпаки. Тому, перш ніж відкрити проєкт у GNAT Studio, потрібно переконатися, що крос-компілятор і gprbuild присутні в PATH. Найпростіше виконати alr exec bash або alr exec gnatstudio в каталозі test, який ми створили на початку статті. Таким чином Alire додасть необхідні шляхи у змінну середовища PATH. Після чого можна вже відкривати проєкт із прикладом у студії.

Висновок#

У цій статті ми розглянули основи програмування вбудованих систем мовою Ada, використовуючи плату STM32F407 як приклад. Ми познайомилися з необхідними інструментами, такими як крос-компілятор, бібліотека часу виконання та бібліотеки драйверів, і дізналися, як їх використовувати для створення простих програм.

Ми також розглянули процес складання, завантаження та налагодження програм на Ada, як з використанням командного рядка, так і за допомогою інтегрованого середовища розробки GNAT Studio. Крім того, ми дізналися, як використовувати бібліотеку Ada Drivers Library для роботи з периферійними пристроями мікроконтролера.

Сподіваємося, що ця стаття допоможе розробникам-початківцям зробити перші кроки у світі програмування вбудованих систем на Ada. Ada - потужна та надійна мова, яка ідеально підходить для розробки критично важливих додатків, і ми закликаємо вас вивчити її можливості далі.

Додаток. Список підтримуваних пристроїв ARM#

Embedded та/або light-tasking:#

Назва

feather_stm32f405

microbit

nrf52832

nrf52833

nrf52840

nucleo_f401re

openmv2

rpi2

rpi-pico

rpi-pico-smp

sam4s

samg55

samv71

stm32f4

stm32f429disco

stm32f469disco

stm32f746disco

stm32f769disco

tms570

tms570lc

zynq7000

Тільки light RTL:#

Назва

cortex-m0

cortex-m0p

cortex-m1

cortex-m23

cortex-m3

cortex-m33df

cortex-m33f

cortex-m4

cortex-m4f

cortex-m7df

cortex-m7f

lm3s