Создание приложения под Мурмулятор ОС (1)

Как я писал ранее (https://habr.com/ru/articles/839976/), Murmulator — одноплатный ультрадешевый микрокомпьютер на основе платы Raspberry Pi Pico (пика), которая, в свою очередь, основана на микроконтроллере — RP2040.

RP2040 — одна из наиболее известных двухъядерных реализаций ARM Cortex-M0+ с 264 КБ встроенной SRAM памяти и от 2-ух до 16-ти МБ flash-памяти подключаемых по QSPI интерфейсу, распаянной на плате пики.

Отдельную статью-тутуриал я посвятил использованию Мурмулятор ОС (МОС): https://habr.com/ru/articles/840052/ с точки зрения пользователя. Теперь имеет смысл описать процесс создания приложений под МОС.

МОС (рассматриваем текущую версию 0.2.7) поддерживает три вида приложений:

  • тяжёлые полноценные прошивки в формате .uf2— это самодостаточные приложения, которые можно запускать и без МОС, прошивая штатными средствами пики. Разработку таких приложений я рассматривать пока не буду.

  • облегченные прошивки .uf2 — эти приложения уже зависят от MOS API, но исполняются из flash-памяти пики. Такие приложения пока редкость — имеется только demo.uf2, поставляемое в комплекте с ОС. Думаю, что по исчерпании задач, которые можно решить посредством легкого типа приложений, начнут разрабатываться и облегчённые. Плюс, в МОС пока нет менеджера памяти для flash-приложений, который «в планах».

  • лёгкие нативные elf32 приложения — это наиболее распространённый класс приложений, они полностью помещаются в SRAM и используют только MOS API, избегая использования аппаратуры «напрямую». Именно этот класс приложений мы и рассмотрим в данной статье.

Наиболее важным для понимания текущего положения дел является то, что пока для МОС не существует нативного фреймворка, и мы будем пользоваться Raspberry Pi Pico SDK 1.5.1, который я рекомендую скачать и установить отсюда: https://github.com/raspberrypi/pico-setup-windows/releases. К сожалению, внятный способ установки существует только для MS Windows 10 и выше. Для более ранних Windows, Linux и MacOS рекомендую воспользоваться документацией на саму пику.

МОС сама является прошивкой для RP2040 хранящейся на flash-чипе, полезно знать карту памяти XIP отображения на адресное пространство RP2040:

10000000…10001000h — .boot2 — содержит 4 КБ startup-блок, частично используется тяжёлыми .uf2 приложениями

10001000…10FE0000h — 4063 для 16 МБ флеш, 991 — для 4 МБ, 479 — для 2 МБ четырёхкилобайтных страниц, доступных для приложений

10FE0000…10FFF000h — .text — содержит 124 КБ ядра МОС

10FFF000…11000000h — .sys_table — содержит 4 КБ таблицу указателей на функции, которые МОС предоставляет для приложений

Особенностью подключения flash-памяти к пике является «заворот адресного пространства», т.е. если у вас имеется чип на 2 МБ, то всё пространство от 10000000 до 10FFFFFF (16 МБ) всё равно будет доступно через XIP, но информация будет дублироваться. В режиме записи — аналогично — запись по смещению FFF000, не существующему в реальном чипе, всё равно произойдет, только попадёт в другое место (1FF000). Поэтому адресация МОС API, рассчитанная на 16 МБ флеш будет корректна на любом объёме чипа. Единственное отличие — количество свободных страниц памяти. Для 16 МБ их 4063, для 2 МБ — всего 479.

Теперь про модель RAM. В МОС отсутствует (пока) механизм защиты «чужой» памяти, т.е. любое приложение может использовать весь объём доступной RAM — 264 КБ (если вы готовы случайно угробить систему). Поэтому желательно пользоваться некоторыми простыми правилами:

  • по-умолчанию стек «не ваш», т.е. его выделят ОС, и вы не знаете сколько (на данный момент 4 КБ) + часть его ОС уже «съела» для своих нужд (пока запускала вашу программу), и вы тоже не знаете сколько именно. Поэтому не злоупотребляйте переменными на стеке. Лучше явно запросить память ОС через malloc/calloc или реализацию new (С++). Искать ошибки, связанные с переполнение стека очень сложно, а ОС вам в этом ничем не поможет.

  • если вы выделили память и не удалили её, используя free/delete, то по выходу из программы данную память никто уже не освободит, и она потеряна до перезагрузки.

  • текущая реализация не умеет инициализировать глобальные статические переменные (с константами всё ок). т.е. инициализируйте их в начале функции main, или определите функцию int _init(void), которая должна поддерживаться gcc, но не в нашем случае — gcc ничего про нашу ОС не знает и собирает .uf2-приложение для пики.

  • если у вас имеются какие-то деструкторы статических переменных, вызывайте их вручную в функции int _fini(void)

Вообще, рассматривайте gcc, не как полноценный интегрированный фреймворк, в данном случае, а как продвинутый ассемблер. Большая часть «сахара» gcc в МОС работать не будет (вы удивитесь, сколько всего он делает «за кадром»).

Старайтесь не использовать оборудование пики «напрямую», пользуйтесь ОС API. На данный момент API слабо структурировано и пока никак не документировано (в планах). Поэтому придётся изучить файлы из каталога https://github.com/DnCraptor/murmulator-os/tree/main/api, а если поведение функций непонятно, стоит начать рассмотрение потрохов самой ОС о входной точки соотв. функции https://github.com/DnCraptor/murmulator-os/blob/main/src/sys_table.c. Например:

inline static int kill(uint32_t task_n) {
typedef int (*fn_ptr_t)(uint32_t);
return ((fn_ptr_t)_sys_table_ptrs[244])(task_n);
}

имеет соотв. запись в системной таблице:

kill, // 244

это указатель на реализацию https://github.com/DnCraptor/murmulator-os/blob/main/src/app.c#L856, которую можно прочитать и понять, что именно она делает.

Примеры готовых программ можно посмотреть там же, в репозитории: https://github.com/DnCraptor/murmulator-os/tree/main/apps (все они собираются, но не все работают, работающие приложения все попадают в папку compiled, а затем и в релиз)

Если вы открыли какой-то проект в VSCode, убедитесь, что настройки компиляции установлены в MinSizeRel, т.к. памяти мало, желательно её экономить.

Выбор конфигурации

Выбор конфигурации

Ошибки линковки при сборке МОС-приложений просто игнорируйте, т.к. в данной версии мы не используем результат работы линковщика, а запускаем сразу объектный файл (см. https://habr.com/ru/articles/839976/). Библиотеки в МОС не поддерживаются (пока), т.е. вся программа должна оказаться в одном файле, а этот файл — в формате ARM elf32, собранный под ARM Cortex-M0+. Для gcc версии 10.3.1 это конфигурация называется arm-none-eabi.

Ну, думаю, для затравки достаточно, продолжу позже в отдельной статье…

© Habrahabr.ru