Note
This project is currently under actively developing and improving, some APIs and interfaces may change as the design continues to mature.
-
MOS is a Real-Time Operating System (RTOS) project built in C++/Rust, which consists of a preemptive kernel and a command-line shell with other applications (e.g., GuiLite and FatFS).
-
Renode is a virtual development tool created by Antmicro for multi-node embedded networks (both wired and wireless) and is intended to enable a scalable workflow for creating effective, tested and secure IoT systems.
mos-core- The Kernel and the Shell, check here.mos-stm32- Running on STM32 series, check here.mos-renode- Test on Renode emulation, check here.mos-rust- A "Vibe Coding" Chimera, check here.
-
- Run
git submodule update --init --recursive - STM32 dependencies are managed as submodules:
cmsis-core,cmsis-device-f4/h7,stm32f4xx-hal-driver,stm32h7xx-hal-driver
- Run
-
- Install CMake and the
Arm GNU Toolchain - Run
./build.shand pick a board - Or pass it directly:
./build.sh stm32f4-disco ./build.sh stm32h7-disco ./build.sh all ./build.sh clean
- Equivalent CMake invocation:
cmake -S . -B build/stm32f4-disco -DMOS_BOARD=stm32f4-disco cmake --build build/stm32f4-disco -j - Artifacts live in
build/<board>
- Install CMake and the
-
- Install the Renode platform
- Quick demo:
./emulation/run.sh, then choose a board and mode (builds and starts automatically) - See emulation/README.md for details
- F4:
renode --console emulation/stm32f4-disco/debug.resc(Shell on TCP3333, GDB on1234) - H7:
renode --console emulation/stm32h7-disco/debug.resc(USART1 Shell on TCP3333, GDB on1234)- The H7 board starts from HSI (64 MHz). Renode fixes USART1/6 to 200 MHz, so the board code defaults the baud-rate clock to 200 MHz. Override with
-DMOS_H7_UART_CLOCK=<PCLK2>on real hardware.
- The H7 board starts from HSI (64 MHz). Renode fixes USART1/6 to 200 MHz, so the board code defaults the baud-rate clock to 200 MHz. Override with
- Multi-machine M2M:
emulation/stm32f4-disco/multi_mach.resc(F4) oremulation/stm32h7-disco/multi_mach.resc(H7) - In VS Code press
F5and chooseCortex Debug (F4)orCortex Debug (H7)
-
- The EIDE project is still present under
.eide/, but multi-board builds use CMake. Reconfigure EIDE against the newexamples/andvendor/stm32/layout if needed.
- The EIDE project is still present under
.
βββ π emulation // Renode emulation scripts
βββ π examples // Examples and board support
β βββ common/ // UART driver, platform headers, shared tests
β βββ boards/
β βββ stm32f4-disco/ // main.cpp / board.hpp / HAL conf / linker
β βββ stm32h7-disco/
βββ π vendor/stm32 // CMSIS + HAL/LL dependencies
βββ π core
β βββ π external // External Library
β βββ π arch // Architecture-Specific Code
β β βββ cpu.hpp // Architecture header entry
β β βββ cortex_m.hpp // Context Switching
β β
β βββ π kernel // Kernel Layer
β β βββ macro.hpp // Kernel Constants Macro
β β βββ type.hpp // Basic Types
β β βββ concepts.hpp // Type Constraints
β β βββ data_type.hpp // Basic Data Structures
β β βββ alloc.hpp // Memory Management
β β βββ global.hpp // Kernel global variable declarations
β β βββ global.cpp // Kernel global variable definitions
β β βββ printf.h/.c // Thread-Safe printf
β β βββ task.hpp // Task Management
β β βββ sync.hpp // Synchronization Primitives
β β βββ async.hpp // Asynchronous Stackless Coroutines
β β βββ scheduler.hpp // Scheduler
β β βββ ipc.hpp // Inter-Process Communication
β β βββ utils.hpp // Other Utilities
β β
β βββ config.h // System Configuration
β βββ kernel.hpp // Kernel Modules
β βββ shell.hpp // Shell Command Line
Each board has one standalone program under examples/; see examples/README.md:
| Example | Description |
|---|---|
boards/stm32f4-disco/main.cpp |
F4: shell + LED + M2M + kernel tests |
boards/stm32h7-disco/main.cpp |
H7: shell + LED + M2M + kernel tests |
./build.sh stm32f4-disco
./build.sh stm32h7-discoMutex Test(Priority Ceiling Protocol)

LCD Driver & GUI Demo
Concurrent Task Period & Time Sequence
// Entry point: examples/boards/stm32f4-disco/main.cpp
#include "board.hpp"
#include "kernel.hpp"
#include "shell.hpp"
#include "common/tests.hpp"
using namespace MOS;
namespace Board = Boards::STM32F4Disco;
static void led_task()
{
while (true) {
Board::led_toggle();
Kernel::Task::delay(500_ms);
}
}
static void m2m_task()
{
char msg[] = "Hej! I'm Mx";
msg[sizeof(msg) - 2] = Board::machine_id();
[&] -> Kernel::Async::Future_t<> {
while (true) {
Board::hub().send_line(msg);
co_await Kernel::Async::delay(500_ms);
}
}();
while (true) {
LOG("rx -> \"%s\"", Board::hub().buf.recv().as_str());
}
}
int main()
{
Board::init(); // clocks, GPIO, USART, SysTick
Kernel::Task::create(Shell::launch<Board::Stdio::max_size()>, &Board::stdio().buf, 0, "shell");
Kernel::Task::create(led_task, nullptr, 1, "led");
Kernel::Task::create(m2m_task, nullptr, 2, "m2m");
/* Test examples */
Kernel::Task::create(Examples::Test::MsgQueueTest<3>, nullptr, 2, "msgq/test");
Kernel::Task::create(Examples::Test::AsyncTest<300>, nullptr, 2, "async/test");
Kernel::Scheduler::launch(); // never returns
}The board support lives in examples/boards/<board>/board.hpp and is header-only:
namespace MOS::Boards::STM32F4Disco
{
using Stdio = Common::SyncUart<MOS_CONF_SHELL_BUF_SIZE>;
using Hub = Common::SyncUart<32>;
void init();
Stdio& stdio();
Hub& hub();
void led_toggle();
char machine_id();
} A_A _ [name] @ x.x.x(Version)
o'' )_____// Build @ TIME, DATE
`_/ MOS ) Chip @ MCU, ARCH
(_(_/--(_/ 2023-2026 Copyright by Eplankton
<Tid> <Name> <Priority> <Status> <Mem%>
---------------------------------------
#0 shell 0 RUNNING 7%
#1 led 1 READY 7%
#2 m2m 2 READY 7%
#3 msgq/test 2 READY 7%
#4 async/test 2 READY 7%
#5 idle 127 READY 15%
---------------------------------------
The kernel picks one scheduling policy at compile time (Kernel::Policy). All policies share the same context-switch path and add no runtime overhead:
| Policy | Ready-queue key | Preemptive | Notes |
|---|---|---|---|
RoundRobin |
arrival order | time slice | priority ignored, FIFO rotation |
PreemptPri |
static priority | yes | time-slice rotation within a priority (default) |
RateMonotonic (RM) |
period T |
yes | shorter period wins; optimal for fixed priority when D = T |
DeadlineMonotonic (DM) |
relative deadline D |
yes | generalizes RM to D <= T |
EarliestDeadlineFirst (EDF) |
absolute deadline | yes | optimal for preemptive uniprocessor, schedulable when Ξ£C/T <= 1 |
Cooperative (Coop) |
static priority | no | switches only on block or explicit Task::yield() |
Select it in core/config.h:
#define MOS_CONF_SCHED_POLICY EarliestDeadlineFirstor override it from CMake without touching the source:
cmake -S . -B build/stm32f4-disco -DMOS_SCHED_POLICY=EarliestDeadlineFirstThe default remains PreemptPri, so existing projects keep their behaviour.
static void control_job()
{
while (true) {
/* work */
Kernel::Task::periodic_wait(); // block until the next activation
}
}
Kernel::Task::create_periodic(
control_job, nullptr,
0, // base priority (RM/DM override it from T/D)
20_ms, // period T
20_ms, // relative deadline D (0 means D = T)
"ctrl",
3_ms // optional WCET for admission control
);- The first job starts immediately; every
periodic_wait()wake-up refreshes the absolute deadline asdl_abs = activation + D. - With a WCET, admission control runs: Liu & Layland bound for RM, density test for DM,
Ξ£C/T <= 1for EDF. A rejected task makescreate_periodicreturnnullptr. - Each missed deadline is counted once per job; query it with
Task::missed_deadlines(). Seeexamples/common/sched_test.hppfor a demo.
SchedPolicyTest() in examples/common/tests.hpp runs automatically in the default build. Batteries are selected at compile time for the active policy and each prints one [TEST] ... PASS/FAIL line:
| Case | Policies | Checks |
|---|---|---|
| RR fairness | all | equal-priority (equal-deadline) tasks rotate fairly, share spread < 40% |
| Preemption | PreemptPri/RM/DM | a higher-priority task preempts a busy lower-priority one |
| Cooperative | Cooperative | the busy task keeps the CPU until it finishes; higher priority waits |
| Deadline order | RM/DM/EDF | jobs run in T/D/absolute-deadline order (created in reverse); RM/DM also verify derived priorities |
| Periodic timing | PreemptPri/RM/DM/EDF | activation interval stays near the period; no missed deadline under EDF |
| Nested PIP | PreemptPri/RM/DM | nested-mutex priority boosts unwind level by level; no medium-priority inversion |
- RM/DM map periods/deadlines onto priorities
[0, 31]viafloor(log2(T)); use>= 32for ordinary tasks. - Under EDF, tasks without a deadline (
Task::create) rank after periodic ones and rotate by time slice, so they never starve. Cooperativehas no tick preemption: CPU-bound work must callTask::yield()explicitly.- Priority inheritance (PIP) supports nested locks; depth is set by
MOS_CONF_PRI_STACK_DEPTH. - Build the policy demo with
-DMOS_SCHED_DEMO=ON(orMOS_SCHED_DEMO=ON ./build.sh <board>). That build skips themsgq/testandasync/teststress tests so they do not exhaust the page pool or flood the UART log.
π¦ v0.5
β DoneοΌ
- Added
RateMonotonic/DeadlineMonotonic/EarliestDeadlineFirst/Cooperativepolicies and completedRoundRobin- Added the periodic-task API
Task::create_periodic/Task::periodic_waitwith admission control- Fixed the PendSV-over-SysTick stall, nested priority inheritance and tick-wrap comparisons
- [Experimental] Port to
ESP32-C6(RISC-V)- [Experimental] Rewrite it in Rust
π¦ v0.4
β DoneοΌ
- Add Hardware
FPUsupport- CMake Tools are now available for compiling the project
- Add external library ETL, a C++ template library for embedded applications
- Add
Renodeemulation platform, add stable support forCortex-Mseries- [Experimental] Add scheduler lock
Scheduler::suspend()- [Experimental] Add Asynchronous stackless coroutines
Async::{Executor, Future_t, co_await/yield/return}π Planned:
- Shift from
FatFStoLittleFS
π¦ v0.3
β Done:
- Mapping
TidstoBitMap_t- Message queue
IPC::MsgQueue_tTask::createallows generic function signatures asvoid fn(auto argv)with type checker- Add
ESP32-C3as aWiFicomponent- Add
Driver::Device::SD_t,SDcard driver, portingFatFsfile system- Add
Shell::usr_cmdsfor user-registered commands- [Experimental] Atomic types
<stdatomic.h>- [Experimental]
Utils::IrqGuard_t, nested interrupt critical sections- [Experimental] Simple formal verification of
Scheduler + Mutexπ Planned:
- Inter-Process Communication: pipes/channels
FPUhardware float support- Performance benchmarking
- Error handling with
Result<T, E>,Option<T>DMA_tDMA Driver- Software/hardware timers
Timer- [Experimental] Adding
POSIXsupport- [Experimental] More real-time scheduling algorithms
π¦ v0.2
β Done:
- Synchronization primitives
Sync::{Sema_t, Lock_t, Mutex_t<T>, CondVar_t, Barrier_t}Scheduler::Policy::PreemptPriwithRoundRobinscheduling for same priority levelsTask::terminateimplicitly called upon task exit to reclaim resources- Simple command-line interaction
Shell::{Command, CmdCall, launch}HAL::STM32F4xx::SPI_tandDriver::Device::ST7735S_t, porting theGuiLitegraphics library- Blocking delay with
Kernel::Global::os_ticksandTask::delay- Refactored project organization into
{kernel, arch, drivers}- Support for
GCCcompilation, compatible withSTM32Cube HAL- Real-time calendar
HAL::STM32F4xx::RTC_t,CmdCall::date_cmd,App::CalendaridleusesKernel::Global::zombie_listto reclaim inactive pages- Three basic page allocation policies
Page_t::Policy::{POOL, DYNAMIC, STATIC}
π¦ v0.1
β Done:
- Basic data structures, scheduler, and task control, memory management
π Planned:
- Timers, round-robin scheduling
- Inter-Process Communication (IPC), pipes, message queues
- Process synchronization (Sync), semaphores, mutexes
- Design a simple Shell
- Variable page sizes, memory allocator
- SPI driver, porting GuiLite/LVGL graphics libraries
- Porting to other boards/arch, e.g., ESP32-C3 (RISC-V)
- How to build a Real-Time Operating System(RTOS)
- PeriodicScheduler_Semaphore
- STM32F4-LCD_ST7735s
- A printf/sprintf Implementation for Embedded Systems
- GuiLite
- STMViewer
- FatFs
- The Zephyr Project
- Eclipse ThreadX
- Embassy
- Renode
- Embedded Template Library (ETL)
I hope the Pacific is as blue as it has been in my dreams.
I hope.
-- Stephen King's "Rita Hayworth and the Shawshank Redemption", 1982









