Skip to content

Architecture Overview

What oveRTOS Does

oveRTOS is an embedded RTOS framework with three main parts: a build system that manages RTOS sources, toolchains, and configuration; a portable API layer that maps application calls to the selected backend at compile time; and language bindings that expose the API in C++, Rust, and Zig alongside the core C interface. The build system downloads RTOS kernel sources, acquires cross-compilation toolchains, and translates a single Kconfig-based .config into the native configuration format of each RTOS. The API layer resolves every public call directly to the backend implementation at compile time — there are no virtual functions, no function pointer tables, and no branch overhead at runtime. LVGL is integrated as the GUI toolkit, with display and input drivers provided for each backend.

Layer Diagram

flowchart TD
    A["<b>Application Code</b><br>C · C++ · Rust · Zig"]
    B["<b>oveRTOS API Layer</b><br>thread · sync · queue · timer · audio · fs · lvgl …"]
    C["<b>CONFIG_OVE_RTOS_*</b><br>compile-time dispatch via Kconfig"]

    A --> B --> C

    C --> D & E & F & G

    subgraph backends [" "]
        direction LR
        D["FreeRTOS<br>backend"] --> H["FreeRTOS<br>kernel"]
        E["NuttX<br>backend"] --> I["NuttX<br>kernel"]
        F["Zephyr<br>backend"] --> J["Zephyr<br>kernel"]
        G["POSIX<br>backend"] --> K["pthreads<br>SDL2"]
    end

    style A fill:#5c6bc0,stroke:#3949ab,color:#fff
    style B fill:#1e88e5,stroke:#1565c0,color:#fff
    style C fill:#f57c00,stroke:#e65100,color:#fff
    style D fill:#43a047,stroke:#2e7d32,color:#fff
    style E fill:#43a047,stroke:#2e7d32,color:#fff
    style F fill:#43a047,stroke:#2e7d32,color:#fff
    style G fill:#43a047,stroke:#2e7d32,color:#fff
    style H fill:#546e7a,stroke:#37474f,color:#fff
    style I fill:#546e7a,stroke:#37474f,color:#fff
    style J fill:#546e7a,stroke:#37474f,color:#fff
    style K fill:#546e7a,stroke:#37474f,color:#fff
    style backends fill:none,stroke:none

Build System

The ove CLI handles the full build lifecycle:

Phase Command What it does
Download make download Fetches RTOS kernel sources (FreeRTOS, NuttX, Zephyr) and cross-compilation toolchains into dl/
Configure make configure Reads .config and generates RTOS-native config files (FreeRTOSConfig.h, prj.conf, NuttX defconfig) plus framework headers
Build make build Invokes each RTOS's native build system (CMake for FreeRTOS/POSIX, west build for Zephyr, make for NuttX)
Run make run Launches firmware on QEMU or as a native POSIX process
Flash make flash Programs hardware via OpenOCD or the backend's flash runner

RTOS source URLs, versions, and download methods are configurable via Kconfig (Config.in.rtos). Each RTOS also exposes its own native menuconfig through make nuttx-menuconfig or make zephyr-menuconfig, with a layered config system that preserves user customisations across rebuilds.

Compile-Time Dispatch

The active backend is selected by a single CONFIG_OVE_RTOS_* symbol written into ove_config.h by Kconfig. Each backend directory provides its own implementation files for every oveRTOS module. The C preprocessor picks the right source tree at build time — there is no runtime branch on the backend identity.

Because the choice is entirely preprocessor-driven, the compiler sees exactly one implementation per function and inlines or optimises it as it would any other C code. The abstraction costs zero cycles at runtime.

Heap Mode vs Zero-Heap Mode

The _create() / _destroy() functions are the primary API and work in both heap and zero-heap modes. In heap mode they allocate from the RTOS heap as normal. In zero-heap mode they become GCC statement-expression macros that auto-generate per-call-site static storage and delegate to _init(), so application code does not need to change.

Function Heap mode (default) Zero-heap mode (CONFIG_OVE_ZERO_HEAP=y)
_create() / _destroy() Allocate/free from RTOS heap Macro-generated static storage per call site
_init() / _deinit() Caller-supplied buffer Caller-supplied buffer
OVE_*_DEFINE_STATIC() File-scope static declaration + auto-init File-scope static declaration + auto-init

_init() / _deinit() remain available for explicit storage control — use them when you need objects in arrays, loops, or structs. OVE_*_DEFINE_STATIC() macros remain the convenient way to declare auto-initialized objects at file scope.

In zero-heap mode, size parameters (queue item_size/max_items, stream buffer size, thread stack_sz) must be compile-time constants, and each _create() call site produces exactly one static object — do not call _create() in a loop when you need multiple distinct objects.

Backend Model

Backend Config symbol Typical use
FreeRTOS CONFIG_OVE_RTOS_FREERTOS STM32 hardware targets via STM32CubeF7
Apache NuttX CONFIG_OVE_RTOS_NUTTX POSIX-compliant embedded systems
Zephyr RTOS CONFIG_OVE_RTOS_ZEPHYR Broad hardware support via West
POSIX/SDL2 CONFIG_OVE_RTOS_POSIX Native Linux/macOS development and testing

The POSIX backend runs natively on the host with pthreads for concurrency and SDL2 for display and audio emulation — no cross-compilation or hardware required.

Language Bindings

oveRTOS ships first-class bindings in four languages. All bindings target the same underlying C API and are built automatically by the framework's build system:

  • C — direct use of <ove/ove.h>
  • C++ — RAII wrappers with move semantics, compile-time stack sizing, and fluent LVGL widget builders (bindings/cpp/)
  • Rustno_std crate with safe thread entry functions, error handling via Result<T, Error>, and LVGL bindings (bindings/rust/ove/)
  • Zig — comptime-safe wrappers with generic Queue(T, N) types, @hasDecl-based feature detection, and LVGL bindings (bindings/zig/ove/)

LVGL Integration

The LVGL graphics library is downloaded, configured, and built as part of the oveRTOS framework. Each backend provides a display driver and tick source:

Backend Display driver Input
FreeRTOS (STM32) Hardware LCD via LTDC/DMA2D Touch controller
Zephyr Zephyr display subsystem Zephyr input subsystem
NuttX NuttX framebuffer (/dev/fb0) NuttX input
POSIX SDL2 window SDL2 mouse/keyboard
QEMU Emulated framebuffer

Thread safety is handled through ove_lvgl_lock() / ove_lvgl_unlock(), which each language binding wraps in its idiomatic RAII pattern (LvglGuard in C++, lvgl::lock() guard in Rust, defer guard.deinit() in Zig). LVGL is enabled via CONFIG_OVE_LVGL in Kconfig.