Создание приложения под Мурмулятор ОС (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
.
Ну, думаю, для затравки достаточно, продолжу позже в отдельной статье…