Creating an application for Murmulator OS (1)

As I wrote earlier (https://habr.com/ru/articles/839976/), Murmulator is a single-board ultra-cheap microcomputer based on the Raspberry Pi Pico board, which, in turn, is based on the RP2040 microcontroller.

RP2040 is one of the most well-known dual-core implementations of ARM Cortex-M0+ with 264 KB of built-in SRAM memory and from 2 to 16 MB of flash memory connected via QSPI interface, soldered on the board.

I dedicated a separate tutorial article to using Murmulator OS (MOS): https://habr.com/ru/articles/840052/ from the user's point of view. Now it makes sense to describe the process of creating applications for MOS.

MOS (we are considering the current version 0.2.7) supports three types of applications:

  • heavy full-fledged firmware in format .uf2– these are self-sufficient applications that can be launched without MOS, flashing with standard means of the peak. I will not consider the development of such applications for now.

  • lightweight firmware .uf2 – these applications already depend on the MOS API, but are executed from the flash memory of the peak. Such applications are still rare – there is only demo.uf2supplied with the OS. I think that after the tasks that can be solved by means of a lightweight type of applications are exhausted, lightweight ones will start to be developed. Plus, the MOS does not yet have a memory manager for flash applications, which is “in the plans”.

  • light native elf32 applications – this is the most common class of applications, they fit entirely in SRAM and use only the MOS API, avoiding the use of hardware “directly”. It is this class of applications that we will consider in this article.

The most important thing to understand about the current state of affairs is that there is no native framework for MOS yet, and we will be using the Raspberry Pi Pico SDK 1.5.1, which I recommend downloading and installing from here: https://github.com/raspberrypi/pico-setup-windows/releases. Unfortunately, a clear installation method exists only for MS Windows 10 and above. For earlier Windows, Linux and MacOS, I recommend using documentation to the very peak.

MOS itself is the firmware for RP2040 stored on a flash chip, it is useful to know the XIP memory map mapping to the RP2040 address space:

10000000..10001000h – .boot2 – contains 4 KB startup block, partially used by heavy .uf2 applications

10001000..10FE0000h – 4063 for 16 MB flash, 991 for 4 MB, 479 for 2 MB four-kilobyte pages available to applications

10FE0000..10FFF000h – .text – contains 124 KB of MOS core

10FFF000..11000000h – .sys_table – contains a 4 KB table of pointers to functions that the MOS provides to applications

The peculiarity of connecting flash memory to the peak is the “address space wrap”, i.e. if you have a 2 MB chip, then the entire space from 10000000 to 10FFFFFF (16 MB) will still be available via XIP, but the information will be duplicated. In the write mode – similarly – writing at offset FFF000, which does not exist in the real chip, will still happen, only it will get to a different place (1FF000). Therefore, the MOS API addressing, designed for 16 MB flash, will be correct on any chip size. The only difference is the number of free memory pages. For 16 MB there are 4063, for 2 MB – only 479.

Now about the RAM model. In MOS there is no (yet) mechanism for protecting “foreign” memory, i.e. any application can use the entire volume of available RAM – 264 KB (if you are ready to accidentally ruin the system). Therefore, it is advisable to use some simple rules:

  • by default, the stack is “not yours”, i.e. it will be allocated by the OS, and you don't know how much (currently 4 KB) + the OS has already “eaten” part of it for its own needs (while it was running your program), and you also don't know how much exactly. Therefore, do not abuse variables on the stack. It is better to explicitly request the OS memory via malloc/calloc or the new implementation (C++). It is very difficult to find errors related to stack overflow, and the OS will not help you with this.

  • If you allocated memory and did not delete it using free/delete, then after exiting the program, no one will free this memory, and it is lost until reboot.

  • the current implementation can't initialize global static variables (everything is ok with constants). i.e. initialize them at the beginning of the function mainor define a function int _init(void)which must be maintained gccbut not in our case – gcc knows nothing about our OS and collects .uf2-application for peak.

  • if you have any static variable destructors, call them manually in the function int _fini(void)

In general, consider gccnot as a full-fledged integrated framework, in this case, but as an advanced assembler. Most of the “sugar” gcc will not work in MOS (you will be surprised how much he does “behind the scenes”).

Try not to use the pick hardware “directly”, use the OS API. At the moment, the API is poorly structured and is not documented in any way (in plans). Therefore, you will have to study the files from the directory https://github.com/DnCraptor/murmulator-os/tree/main/apiand if the behavior of the functions is unclear, it is worth starting to consider the guts of the OS itself at the entry point of the corresponding function https://github.com/DnCraptor/murmulator-os/blob/main/src/sys_table.c. For example:

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);
}

has a corresponding entry in the system table:

kill, // 244

this is a pointer to the implementation https://github.com/DnCraptor/murmulator-os/blob/main/src/app.c#L856which you can read and understand what exactly it does.

Examples of ready-made programs can be found there, in the repository: https://github.com/DnCraptor/murmulator-os/tree/main/apps (they all get collected, but not all work, the running applications all end up in the folder compiledand then in the release)

If you have opened a project in VSCode, make sure that the compilation settings are set to MinSizeRel, because there is little memory, it is advisable to save it.

Selecting a configuration

Selecting a configuration

Linking errors when assembling MOS applications should be simply ignored, since in this version we do not use the result of the linker, but run the object file directly (see https://habr.com/ru/articles/839976/). Libraries are not supported in MOS (yet), i.e. the entire program must be in one file, and this file must be in the ARM elf32 format, assembled for ARM Cortex-M0+. For gcc version 10.3.1 this configuration is called arm-none-eabi.

Well, I think that's enough for a teaser, I'll continue later in a separate article…

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *