- RTOS audit: ChibiOS, RT-Thread, Contiki, RODOS comparison - PikeOS x86 audit report - Bibliography for safety-critical hypervisor research
784 lines
31 KiB
Markdown
784 lines
31 KiB
Markdown
# Deep Audit: ChibiOS, RT-Thread, Contiki, RODOS
|
|
|
|
## Source locations
|
|
- ChibiOS: `/home/fabiorafaelcoutada/portugalfuturista/rtos_ref/ChibiOS/`
|
|
- RT-Thread: `/home/fabiorafaelcoutada/portugalfuturista/rtos_ref/rt-thread/`
|
|
- Contiki: `/home/fabiorafaelcoutada/portugalfuturista/rtos_ref/contiki/`
|
|
- RODOS: `/home/fabiorafaelcoutada/portugalfuturista/rtos_ref/rodos/`
|
|
|
|
---
|
|
|
|
# 1. ChibiOS/RT v8.0.0
|
|
|
|
## 1.1 Architecture Support
|
|
|
|
**Port layers** in `os/common/ports/`:
|
|
- ARM, ARM-common, ARMv6-M, ARMv7-M, ARMv7-M-ALT, ARMv7-R, ARMv8-M-ML, ARMv8-M-ML-ALT, ARMv8-M-ML-TZ, ARMvx-M-SB
|
|
- AVR, e200 (PowerPC NXP), SIMIA32, SIMX86_64
|
|
|
|
**HAL ports** in `os/hal/ports/`:
|
|
- ADUCM (Analog Devices), AVR, LPC (NXP), MAX32, RP (Raspberry Pi RP2040), SPC5 (ST PowerPC), STM32 (C0/F0/F1/F3/F4/F7/G0/G4/H5/H7/L0/L1/L4/L5/MP1/U0/U3/U5/WB/WL), simulator
|
|
|
|
**Demo BSPs** in `demos/`:
|
|
- ADUCM, AVR (Arduino UNO/MEGA/MINI/NANO/Leonardo, DigiSpark ATTiny, MT-DB-X4), LPC21xx, MAX32, RP, SPC5, STM32, various
|
|
|
|
**Key takeaway**: Broad ARM Cortex-M coverage (M0/M0+/M3/M4/M7/M23/M33/M55/M85 with TrustZone), AVR, PPC e200, simulator (x86/x86_64).
|
|
|
|
## 1.2 Memory Model
|
|
|
|
**No MMU required.** Flat memory model with static allocation as primary pattern.
|
|
|
|
File: `os/rt/include/chmem.h`
|
|
- **Static working areas**: `THD_WORKING_AREA(s, n)` allocates thread stack + `thread_t` struct in BSS
|
|
- **NUMA memory classes**: `CH_MEM_GLOBAL_BSS`, `CH_MEM_LOCAL_BSS(c)`, `CH_MEM_PRIVATE_BSS(c)`, plus coherent variants for SMP cache management
|
|
- **No heap allocator in kernel**: ChibiOS relies on statically allocated pools. Dynamic allocation is optional via `oslib/` (heap, mempool, mailbox abstractions)
|
|
- Stack guard via MPU (`PORT_ENABLE_GUARD_PAGES` on ARMv7-M)
|
|
|
|
**Design pattern**: Pre-allocated working areas, no `malloc()` in kernel. Thread stacks are compile-time sized arrays.
|
|
|
|
## 1.3 Kernel Primitives
|
|
|
|
### Threads (`os/rt/include/chthreads.h`, `os/rt/src/chthreads.c`)
|
|
```c
|
|
typedef void (*tfunc_t)(void *p);
|
|
|
|
// Static creation
|
|
thread_t *chThdCreateStatic(void *wsp, size_t size, tprio_t prio, tfunc_t tp, void *arg);
|
|
thread_t *chThdCreateI(...); // from ISR context
|
|
// Dynamic creation (requires CH_CFG_USE_DYNAMIC)
|
|
thread_t *chThdCreate(...);
|
|
|
|
// Control
|
|
void chThdExit(msg_t msg);
|
|
void chThdTerminate(thread_t *tp);
|
|
msg_t chThdWait(thread_t *tp); // join
|
|
void chThdSleep(sysinterval_t ticks);
|
|
void chThdSleepUntil(systime_t time);
|
|
void chThdYield(void);
|
|
void chThdSuspendS(thread_t *tpp);
|
|
void chThdResumeI(thread_t *tp, msg_t msg);
|
|
```
|
|
|
|
**Thread states** (16 states, `chschd.h`):
|
|
```
|
|
READY, CURRENT, WTSTART, SUSPENDED, QUEUED, WTSEM, WTMTX,
|
|
WTCOND, SLEEPING, WTEXIT, WTOREVT, WTANDEVT, SNDMSGQ, SNDMSG, WTMSG, FINAL
|
|
```
|
|
|
|
### Scheduler (`os/rt/include/chschd.h`, `os/rt/src/chschd.c`)
|
|
```c
|
|
void chSchReadyI(thread_t *tp); // insert in ready list
|
|
void chSchGoSleepS(tstate_t newstate); // sleep
|
|
msg_t chSchGoSleepTimeoutS(tstate_t, sysinterval_t);
|
|
void chSchWakeupS(thread_t *ntp, msg_t msg);
|
|
void chSchRescheduleS(void);
|
|
void chSchDoPreemption(void);
|
|
void chSchDoYieldS(void);
|
|
thread_t *chSchSelectFirst(void);
|
|
```
|
|
|
|
**Priority-based preemptive** ready list. Priorities 1-255 (IDLE=1, LOW=2, NORMAL=128, HIGH=255). `ch_sch_prio_insert()` walks sorted doubly-linked list. Supports `CH_CFG_TIME_QUANTUM` for round-robin among equal-priority threads.
|
|
|
|
### IPC Primitives
|
|
|
|
**Semaphores** (`chsem.h`):
|
|
```c
|
|
void chSemObjectInit(semaphore_t *sp, cnt_t n);
|
|
msg_t chSemWait(semaphore_t *sp);
|
|
msg_t chSemWaitTimeout(semaphore_t *sp, sysinterval_t timeout);
|
|
void chSemSignal(semaphore_t *sp);
|
|
void chSemSignalI(semaphore_t *sp); // ISR-safe
|
|
msg_t chSemSignalWait(semaphore_t *sps, semaphore_t *spw); // atomic signal+wait
|
|
```
|
|
|
|
**Mutexes** (`chmtx.h`) with **priority inheritance**:
|
|
```c
|
|
void chMtxLock(mutex_t *mp);
|
|
bool chMtxTryLock(mutex_t *mp);
|
|
void chMtxUnlock(mutex_t *mp);
|
|
void chMtxUnlockAll(void); // unlock all mutexes held by current thread
|
|
```
|
|
Mutex structure has `owner`, `next` (linked list on thread), optional `cnt` for recursive.
|
|
|
|
**Condition Variables** (`chcond.h`):
|
|
```c
|
|
void chCondSignal(condition_variable_t *cp);
|
|
void chCondBroadcast(condition_variable_t *cp);
|
|
msg_t chCondWait(condition_variable_t *cp);
|
|
```
|
|
|
|
**Events** (`chevents.h`) - event flags model:
|
|
```c
|
|
void chEvtRegisterMaskWithFlags(event_source_t *esp, event_listener_t *elp, eventmask_t events, eventflags_t wflags);
|
|
void chEvtSignal(thread_t *tp, eventmask_t events);
|
|
eventmask_t chEvtWaitAny(eventmask_t events);
|
|
eventmask_t chEvtWaitAll(eventmask_t events);
|
|
void chEvtDispatch(const evhandler_t *handlers, eventmask_t events);
|
|
```
|
|
Event listeners link to event sources. Uses bitmask model (eventmask_t + eventflags_t).
|
|
|
|
**Synchronous Messages** (`chmsg.h`) - rendezvous-style:
|
|
```c
|
|
msg_t chMsgSend(thread_t *tp, msg_t msg); // blocking send, returns reply
|
|
thread_t *chMsgWait(void); // blocking receive
|
|
void chMsgRelease(thread_t *tp, msg_t msg); // reply
|
|
```
|
|
|
|
### ChibiOS/NIL (ultra-lightweight variant, `os/nil/`)
|
|
- Only 4 source files: `ch.c`, `chevt.c`, `chmsg.c`, `chsem.c`
|
|
- Cooperative only, no preemption
|
|
- Fixed number of threads (compile-time)
|
|
- Minimal states: WTSTART, READY, SLEEPING, SUSPENDED, WTEXIT, WTQUEUE, WTOREVT, WTANDEVT, SNDMSGQ, SNDMSG, WTMSG
|
|
- ~1KB footprint, ideal for 8-bit MCUs
|
|
|
|
## 1.4 API Surface
|
|
|
|
Single include: `#include "ch.h"` → pulls all kernel headers:
|
|
```
|
|
chearly.h → chrfcu.h → chdebug.h → chtime.h → chalign.h → chtrace.h →
|
|
chport.h → chsafety.h → chlists.h → chtmm → chstats.h → chobjects.h →
|
|
chmem.h → chsys.h → chinstances.h → chvt.h → chschd.h → chthreads.h →
|
|
chregistry.h → chsem.h → chmtx.h → chcond.h → chevents.h → chmsg.h →
|
|
chlib.h (OSLIB) → chdynamic.h
|
|
```
|
|
|
|
**Locking model**: Three-class API with `S` suffix (caller must hold system lock), `I` suffix (caller must be in ISR), no suffix (API-level, acquires lock internally):
|
|
```c
|
|
chSysLock(); // enter critical section
|
|
chSysUnlock(); // exit critical
|
|
```
|
|
|
|
## 1.5 Hardware Abstraction
|
|
|
|
Port layer contract (`os/rt/include/chport.h`) requires every port to define:
|
|
```c
|
|
PORT_COMPILER_NAME, PORT_IDLE_THREAD_STACK_SIZE, PORT_INT_REQUIRED_STACK,
|
|
PORT_SUPPORTS_RT, PORT_NATURAL_ALIGN, PORT_STACK_ALIGN, PORT_WORKING_AREA_ALIGN,
|
|
PORT_ARCHITECTURE_NAME, PORT_CORE_VARIANT_NAME, PORT_INFO,
|
|
PORT_IRQ_IS_VALID_PRIORITY, PORT_IRQ_IS_VALID_KERNEL_PRIORITY,
|
|
PORT_SETUP_CONTEXT, PORT_WA_SIZE, PORT_IRQ_PROLOGUE, PORT_IRQ_EPILOGUE,
|
|
PORT_IRQ_HANDLER, PORT_FAST_IRQ_HANDLER
|
|
```
|
|
|
|
HAL layer (`os/hal/`): Full driver abstraction for ADC, CAN, DAC, EXT, GPT, I2C, ICU, MAC, PAL (GPIO), PWM, RTC, SDC, SERIAL, SIO, SPI, UART, USB, WDG. Each has a `hal_xxx_lld.h` low-level driver per port.
|
|
|
|
## 1.6 Tick/Timer Infrastructure
|
|
|
|
File: `os/rt/include/chvt.h`, `os/rt/src/chvt.c`
|
|
|
|
**Two modes**:
|
|
1. **Tick mode** (`CH_CFG_ST_TIMEDELTA == 0`): Regular SysTick interrupt increments `currcore->vtlist.systime`. Virtual timers stored in delta list.
|
|
2. **Tickless mode** (`CH_CFG_ST_TIMEDELTA > 0`): Hardware timer programmed for exact next wakeup. `port_timer_get_time()` returns current time.
|
|
|
|
```c
|
|
void chVTDoSetI(virtual_timer_t *vtp, sysinterval_t delay, vtfunc_t vtfunc, void *par);
|
|
void chVTDoSetContinuousI(virtual_timer_t *vtp, sysinterval_t delay, vtfunc_t vtfunc, void *par);
|
|
void chVTDoResetI(virtual_timer_t *vtp);
|
|
void chVTDoTickI(void); // called from tick ISR or timer ISR
|
|
systime_t chVTGetSystemTimeX(void);
|
|
```
|
|
|
|
Virtual timer is a delta-list node with callback. Supports one-shot and continuous (auto-reload).
|
|
|
|
## 1.7 Cooperative vs Preemptive
|
|
|
|
**ChibiOS/RT**: Fully **preemptive** by default. Priority-based preemption. Optional time quantum for round-robin at same priority (`CH_CFG_TIME_QUANTUM > 0`). No cooperative-only mode.
|
|
|
|
**ChibiOS/NIL**: **Cooperative only**. Threads yield explicitly. No preemption. Ultra-minimal for deeply constrained targets.
|
|
|
|
---
|
|
|
|
# 2. RT-Thread v5.3.0
|
|
|
|
## 2.1 Architecture Support
|
|
|
|
**libcpu/** supports 21 architecture families:
|
|
- aarch64, arc, arm (23 sub-ports: am335x, arm926, armv6, AT91SAM7S/X, cortex-a/m0/m23/m3/m33/m4/m7/m85/r4/r52, dm36x, lpc214x/lpc24xx, s3c24x0/s3c44b0, sep4020, zynqmp-r5)
|
|
- avr32, blackfin, c-sky, ia32, m16c, mips, nios, ppc, risc-v, rx, sim, sparc-v8, ti-dsp, unicore32, v850, xilinx
|
|
|
|
**BSP/**: 107 board support packages including STM32 (full range), GD32, Infineon, Renesas, NXP, Allwinner, Raspberry Pi, QEMU (aarch64, riscv, virt64), RISC-V (hifive1, rv32m1_vega), x86, simulator, and many Chinese MCU vendors.
|
|
|
|
## 2.2 Memory Model
|
|
|
|
**No MMU required.** Three memory allocation strategies (compile-time selectable):
|
|
|
|
1. **Small Memory** (`src/mem.c`, `RT_USING_SMALL_MEM`): First-fit allocator from contiguous heap. `rt_smem_init()` takes a begin address + size. Items linked via `next`/`prev` offsets. Coalescing on free. O(n) worst-case.
|
|
|
|
2. **SLAB allocator** (`src/slab.c`, `RT_USING_SLAB`): Derived from DragonFly BSD. 72 zones, chunk sizes 8B to 16KB. Page-based backing. O(1) alloc/free for common sizes.
|
|
|
|
3. **Memory heap** (`src/memheap.c`, `RT_USING_MEMHEAP`): Multiple disjoint memory regions managed as a single logical heap. Good for systems with non-contiguous RAM (e.g., SRAM + external SDRAM).
|
|
|
|
4. **Memory pool** (`src/mempool.c`): Fixed-size block pool, O(1) alloc/free.
|
|
|
|
**Key API**:
|
|
```c
|
|
void *rt_malloc(rt_size_t nbytes);
|
|
void rt_free(void *ptr);
|
|
void *rt_realloc(void *ptr, rt_size_t nbytes);
|
|
void *rt_calloc(rt_size_t count, rt_size_t size);
|
|
rt_smem_t rt_smem_init(const char *name, void *begin_addr, rt_size_t size);
|
|
```
|
|
|
|
## 2.3 Kernel Primitives
|
|
|
|
### Threads (`src/thread.c`, `include/rtdef.h`)
|
|
```c
|
|
// Static creation
|
|
rt_err_t rt_thread_init(struct rt_thread *thread, const char *name,
|
|
void (*entry)(void *parameter), void *parameter,
|
|
void *stack_start, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick);
|
|
// Dynamic creation
|
|
rt_thread_t rt_thread_create(const char *name, void (*entry)(void *parameter),
|
|
void *parameter, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick);
|
|
|
|
rt_err_t rt_thread_startup(rt_thread_t thread);
|
|
rt_err_t rt_thread_delay(rt_tick_t tick);
|
|
rt_err_t rt_thread_yield(void);
|
|
rt_err_t rt_thread_suspend(rt_thread_t thread);
|
|
rt_err_t rt_thread_resume(rt_thread_t thread);
|
|
rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg);
|
|
rt_thread_t rt_thread_self(void);
|
|
```
|
|
|
|
**Thread control block** (`rtdef.h:852`):
|
|
```c
|
|
struct rt_thread {
|
|
struct rt_object parent; // inherit from kernel object
|
|
void *sp, *entry, *parameter, *stack_addr;
|
|
rt_uint32_t stack_size;
|
|
rt_err_t error;
|
|
RT_SCHED_THREAD_CTX; // scheduler-specific context
|
|
struct rt_timer thread_timer; // built-in thread timer
|
|
rt_thread_cleanup_t cleanup;
|
|
rt_list_t taken_object_list; // mutex tracking for priority inheritance
|
|
rt_object_t pending_object;
|
|
rt_uint32_t event_set;
|
|
rt_uint8_t event_info;
|
|
// ... signals, pthreads, LWP fields
|
|
};
|
|
```
|
|
|
|
### Scheduler (`src/scheduler_comm.c`, `src/scheduler_up.c`, `src/scheduler_mp.c`)
|
|
|
|
**Two scheduler variants**:
|
|
1. **UP (uniprocessor)**: Bit-map priority scheduler. `priority_group` + `ready_table[32]` for O(1) highest-priority lookup. Up to 256 priority levels.
|
|
2. **MP (multiprocessor/SMP)**: Per-CPU ready queues, IPI-based preemption, CPU affinity binding.
|
|
|
|
```c
|
|
void rt_sched_thread_init_ctx(struct rt_thread *thread, rt_uint32_t tick, rt_uint8_t priority);
|
|
rt_err_t rt_sched_thread_ready(struct rt_thread *thread);
|
|
```
|
|
|
|
### IPC (`src/ipc.c`)
|
|
|
|
**Semaphores**:
|
|
```c
|
|
rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag);
|
|
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t timeout);
|
|
rt_err_t rt_sem_release(rt_sem_t sem);
|
|
```
|
|
|
|
**Mutexes** with priority inheritance + priority ceiling:
|
|
```c
|
|
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag);
|
|
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout);
|
|
rt_err_t rt_mutex_release(rt_mutex_t mutex);
|
|
```
|
|
|
|
**Events** (32-bit bitmask):
|
|
```c
|
|
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag);
|
|
rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t option, rt_int32_t timeout, rt_uint32_t *recved);
|
|
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
|
|
```
|
|
|
|
**Mailbox** (fixed-size message slots):
|
|
```c
|
|
rt_err_t rt_mb_init(rt_mailbox_t mb, const char *name, void *msgpool, rt_size_t size, rt_uint8_t flag);
|
|
rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value);
|
|
rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout);
|
|
```
|
|
|
|
**Message Queue** (variable-size messages):
|
|
```c
|
|
rt_err_t rt_mq_init(rt_mq_t mq, const char *name, void *msgpool, rt_size_t msg_size, rt_size_t pool_size, rt_uint8_t flag);
|
|
rt_err_t rt_mq_send(rt_mq_t mq, const void *buffer, rt_size_t size);
|
|
rt_err_t rt_mq_recv(rt_mq_t mq, void *buffer, rt_size_t size, rt_int32_t timeout);
|
|
```
|
|
|
|
IPC flags: `RT_IPC_FLAG_PRIO` (priority-ordered waiters) or `RT_IPC_FLAG_FIFO`.
|
|
|
|
## 2.4 API Surface
|
|
|
|
Single include: `#include <rtthread.h>` → `rtdef.h`, `rtservice.h`, `rtm.h`, `rtatomic.h`, `rtklibc.h`
|
|
|
|
**Object system**: All kernel objects inherit from `struct rt_object` with name, type, flag, list node. Enables runtime object discovery via `rt_object_find()`.
|
|
|
|
**Initialization export macros** for component init ordering:
|
|
```c
|
|
INIT_BOARD_EXPORT(fn) // level "1"
|
|
INIT_DEVICE_EXPORT(fn) // level "3"
|
|
INIT_COMPONENT_EXPORT(fn)// level "4"
|
|
INIT_APP_EXPORT(fn) // level "6"
|
|
```
|
|
|
|
**Hook system**: Per-operation hooks (`rt_thread_suspend_sethook`, `rt_timer_enter_sethook`, etc.) plus hook lists for multiple subscribers.
|
|
|
|
## 2.5 Hardware Abstraction
|
|
|
|
**libcpu/** per-arch provides:
|
|
```c
|
|
rt_uint8_t *rt_hw_stack_init(void *entry, void *parameter, rt_uint8_t *stack_addr, void *exit);
|
|
void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to);
|
|
void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to, rt_thread_t from_thread, rt_thread_t to_thread);
|
|
rt_base_t rt_hw_interrupt_disable(void);
|
|
void rt_hw_interrupt_enable(rt_base_t level);
|
|
void rt_hw_cpu_reset(void);
|
|
void rt_hw_cpu_shutdown(void);
|
|
```
|
|
|
|
**BSP/** per-board provides system clock, peripheral init, linker script, console.
|
|
|
|
**Device framework** (`include/rtdef.h`): `struct rt_device` with `rt_device_ops` (init, open, close, read, write, control).
|
|
|
|
## 2.6 Tick/Timer Infrastructure
|
|
|
|
File: `src/clock.c`, `src/timer.c`
|
|
|
|
```c
|
|
rt_tick_t rt_tick_get(void);
|
|
void rt_tick_increase(void); // called from SysTick ISR
|
|
void rt_tick_increase_tick(rt_tick_t tick); // multi-tick advance
|
|
|
|
void rt_timer_init(rt_timer_t timer, const char *name,
|
|
void (*timeout)(void *parameter), void *parameter,
|
|
rt_tick_t time, rt_uint8_t flag);
|
|
rt_err_t rt_timer_start(rt_timer_t timer);
|
|
rt_err_t rt_timer_stop(rt_timer_t timer);
|
|
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg);
|
|
```
|
|
|
|
Timer flags: `RT_TIMER_FLAG_ONE_SHOT` / `RT_TIMER_FLAG_PERIODIC`, `RT_TIMER_FLAG_HARD_TIMER` (callback in ISR) / `RT_TIMER_FLAG_SOFT_TIMER` (callback in timer thread). Uses **skip list** for O(log n) timer insertion (`RT_TIMER_SKIP_LIST_LEVEL`).
|
|
|
|
## 2.7 Cooperative vs Preemptive
|
|
|
|
**Fully preemptive** by default. Priority-based preemption with optional time-slice round-robin (`tick` parameter per thread). SMP variant adds per-CPU scheduling with IPI-triggered rescheduling.
|
|
|
|
No explicit cooperative mode, but a thread can yield voluntarily with `rt_thread_yield()`.
|
|
|
|
---
|
|
|
|
# 3. Contiki OS (v3.x)
|
|
|
|
## 3.1 Architecture Support
|
|
|
|
**Platform directory** (`platform/`) with 33 platforms:
|
|
- AVR: atmega128rfa1, raven, ravenlcd, ravenusb, rcb, rss2, zigbit
|
|
- ARM: cc2538dk, nrf52dk, openmote-cc2538, srf06-cc26xx, stm32nucleo-spirit1, stm32test, zoul
|
|
- MSP430: exp5438, sky, wismote, z1
|
|
- x86: native (Linux host), win32, galileo, minimal-net
|
|
- Simulators: cooja, cooja-ip64
|
|
- Other: cc2530dk, econotag, ev-aducrf101mkxz, jn516x, mbxxx, micaz, seedeye
|
|
|
|
**No libcpu separation** — each platform provides its own clock, rtimer, and radio drivers directly.
|
|
|
|
## 3.2 Memory Model
|
|
|
|
**No MMU. No dynamic memory allocation in the kernel.**
|
|
|
|
Contiki uses **no heap at all** by default. Everything is statically allocated:
|
|
- Process structures are compile-time macros
|
|
- Event queue is a fixed-size array: `static struct event_data events[PROCESS_CONF_NUMEVENTS]` (default 32)
|
|
- Protothread state is a single `char` in the process struct (local continuation)
|
|
- No thread stacks — protothreads share the main stack
|
|
|
|
**This is the most memory-efficient model of all four RTOSes.** A protothread is literally a single byte of state.
|
|
|
|
## 3.3 Kernel Primitives
|
|
|
|
### Protothreads (`core/sys/pt.h`)
|
|
|
|
Protothreads are **stackless threads** implemented as C macros using Duff's device (local continuations):
|
|
|
|
```c
|
|
struct pt { lc_t lc; }; // lc_t is typically a char
|
|
|
|
PT_THREAD(my_thread(struct pt *pt, process_event_t ev, process_data_t data));
|
|
#define PT_BEGIN(pt) // start
|
|
#define PT_END(pt) // end
|
|
#define PT_WAIT_UNTIL(pt, condition) // block until condition
|
|
#define PT_WAIT_WHILE(pt, condition) // block while condition
|
|
#define PT_YIELD(pt) // yield to scheduler
|
|
#define PT_SPAWN(pt, child, thread) // spawn child protothread
|
|
```
|
|
|
|
**No per-thread stack.** Each protothread is a function that returns `PT_WAITING`, `PT_YIELDED`, `PT_EXITED`, or `PT_ENDED`. The continuation point is stored in `lc_t` (typically 1 byte using GCC computed goto labels, or 2 bytes using switch-based Duff's device).
|
|
|
|
### Processes (`core/sys/process.h`, `core/sys/process.c`)
|
|
|
|
```c
|
|
PROCESS_THREAD(name, ev, data); // declare process
|
|
PROCESS_NAME(name); // get process name
|
|
void process_start(struct process *p, process_data_t data);
|
|
void process_exit(struct process *p);
|
|
int process_post(struct process *p, process_event_t ev, process_data_t data);
|
|
void process_poll(struct process *p);
|
|
process_event_t process_alloc_event(void);
|
|
```
|
|
|
|
Each process wraps a protothread:
|
|
```c
|
|
struct process {
|
|
struct process *next;
|
|
const char *name;
|
|
PT_THREAD((*thread)(struct pt *, process_event_t, process_data_t));
|
|
struct pt pt;
|
|
unsigned char state, needspoll;
|
|
};
|
|
```
|
|
|
|
### Event System
|
|
|
|
**Synchronous event dispatch** via `process_run()`:
|
|
```c
|
|
// In main loop:
|
|
while(1) {
|
|
do {} while(process_run() > 0); // dispatch events
|
|
// low-power sleep if no events
|
|
}
|
|
```
|
|
|
|
Events are posted to a **fixed-size circular queue** (`events[PROCESS_CONF_NUMEVENTS]`). Pre-defined events: `PROCESS_EVENT_INIT`, `PROCESS_EVENT_POLL`, `PROCESS_EVENT_EXIT`, `PROCESS_EVENT_TIMER`, `PROCESS_EVENT_CONTINUE`, etc.
|
|
|
|
### No mutexes, semaphores, or message queues
|
|
|
|
Contiki has **none of these**. Synchronization is entirely event-driven + protothread blocking conditions. `core/sys/pt-sem.h` provides a minimal counting semaphore for protothreads.
|
|
|
|
## 3.4 API Surface
|
|
|
|
Main includes: `contiki.h`, `contiki-net.h`, `contiki-lib.h`
|
|
|
|
```c
|
|
// Process macros
|
|
PROCESS_THREAD(), PROCESS_BEGIN(), PROCESS_END(), PROCESS_YIELD()
|
|
PROCESS_WAIT_EVENT(), PROCESS_WAIT_EVENT_UNTIL()
|
|
PROCESS_POLL_AND_EXIT()
|
|
|
|
// Timer APIs
|
|
timer_set(), timer_reset(), timer_expired()
|
|
etimer_set(), etimer_reset(), etimer_expired()
|
|
ctimer_set(), ctimer_reset(), ctimer_expired()
|
|
stimer_set(), stimer_expired()
|
|
rtimer_set()
|
|
|
|
// Event APIs
|
|
process_post(), process_poll()
|
|
```
|
|
|
|
## 3.5 Hardware Abstraction
|
|
|
|
Each platform provides:
|
|
- `clock.c` / `clock.h`: System tick
|
|
- `rtimer-arch.c`: Real-time timer
|
|
- `*contiki-conf.h`: Platform configuration
|
|
- Radio driver (for networking)
|
|
|
|
No unified HAL layer — each platform is self-contained.
|
|
|
|
## 3.6 Tick/Timer Infrastructure
|
|
|
|
**Four timer layers** (coexisting):
|
|
|
|
1. **timer** (`core/sys/timer.h`): Simple interval timer, checked by polling `timer_expired()`. Uses `clock_time_t` (typically 16-bit).
|
|
|
|
2. **etimer** (`core/sys/etimer.h`): Event timer — posts `PROCESS_EVENT_TIMER` to the owning process when expired. Linked list managed by `etimer_request_poll()`.
|
|
|
|
3. **ctimer** (`core/sys/ctimer.h`): Callback timer — calls a function pointer on expiry.
|
|
|
|
4. **rtimer** (`core/sys/rtimer.h`): Real-time timer with **microsecond precision**, runs from ISR context. Used for time-critical radio operations.
|
|
|
|
```c
|
|
// Clock layer
|
|
void clock_init(void);
|
|
clock_time_t clock_time(void); // typically 32 ticks/sec
|
|
unsigned long clock_seconds(void);
|
|
#define CLOCK_SECOND (clock_time_t)32
|
|
|
|
// rtimer
|
|
void rtimer_set(struct rtimer *rt, rtimer_clock_t time, rtimer_clock_t duration,
|
|
rtimer_callback_t func, void *ptr);
|
|
```
|
|
|
|
## 3.7 Cooperative vs Preemptive
|
|
|
|
**Purely cooperative.** Protothreads yield explicitly via `PT_YIELD()`, `PT_WAIT_UNTIL()`, etc. No preemption. The main loop runs `process_run()` which dispatches one event at a time.
|
|
|
|
The only "preemption" is ISR-level rtimer callbacks, which run in interrupt context and must not block.
|
|
|
|
---
|
|
|
|
# 4. RODOS (Realtime Onboard Dependable Operating System) v2.x
|
|
|
|
## 4.1 Architecture Support
|
|
|
|
**Bare-metal ports** (`src/bare-metal/`):
|
|
- **Cortex-M** (`src/bare-metal-cortex-m/`): Generic Cortex-M port with PendSV-based context switch
|
|
- **STM32F4**: STM32F4xx (F407, F411 discovery boards)
|
|
- **STM32H7**: STM32H7xx (H723, H735, H745, H753 — Nucleo, Discovery, custom boards)
|
|
- **STM32L4**: STM32L431, L432, L475, L496 (Nucleo L432KC, L496ZG, Discovery L475)
|
|
- **STM32WB**: STM32WB55 Nucleo (BLE-capable)
|
|
- **EFR32FG1P**: Silicon Labs EFR32 Flex Gecko (Thunderboard)
|
|
- **VA41620**: Vorago VA41620 (radiation-hardened Cortex-M4)
|
|
- **SF2**: Microsemi SmartFusion2 (Cortex-M3 + FPGA)
|
|
- **Raspberry Pi 3**: Bare-metal AArch64
|
|
- **Linux x86**: Linux-hosted with makecontext/setcontext
|
|
- **Template**: Porting template
|
|
|
|
**POSIX simulation** (`src/on-posix/`, `src/on-posix-mac/`): Run as Linux/macOS process using POSIX threads.
|
|
|
|
## 4.2 Memory Model
|
|
|
|
**No MMU required.** Fully static memory model in C++.
|
|
|
|
- **Threads** use either static stacks (via template) or dynamic `new` (deprecated):
|
|
```cpp
|
|
template <size_t STACK_SIZE>
|
|
Thread(char (&stack)[STACK_SIZE], const char* name, const int32_t priority);
|
|
// deprecated: Thread(const char* name, int32_t priority, size_t stackSize);
|
|
```
|
|
|
|
- **StaticThread<STACK_SIZE>** is the recommended pattern (compile-time sized stack array).
|
|
- No heap allocator in kernel. All kernel objects (`Thread`, `Semaphore`, `TimeEvent`, `Topic`, `Subscriber`) are statically constructed C++ objects created before `main()`.
|
|
- Stack grows downward. Sentinel value `0xdeaddead` placed at stack base for overflow detection.
|
|
|
|
## 4.3 Kernel Primitives
|
|
|
|
### Threads (`api/thread.h`, `src/independent/thread.cpp`)
|
|
|
|
C++ class with virtual `run()` method:
|
|
```cpp
|
|
class Thread : public ListElement {
|
|
public:
|
|
Thread(const char* name, int32_t priority, size_t stackSize);
|
|
template <size_t STACK_SIZE>
|
|
Thread(char (&stack)[STACK_SIZE], const char* name, int32_t priority);
|
|
|
|
virtual void run() = 0; // user implements
|
|
virtual void init() {} // called before run()
|
|
|
|
static bool suspendCallerUntil(int64_t reactivationTime = END_OF_TIME, void* signaler = 0);
|
|
void suspendUntilNextBeat();
|
|
void setPeriodicBeat(int64_t begin, int64_t period);
|
|
void resume();
|
|
void resumeAndYield();
|
|
static void yield();
|
|
static Thread* getCurrentThread();
|
|
static Thread* findNextToRun(int64_t& selectedEarliestSuspendedUntil);
|
|
static Thread* findNextWaitingFor(void* signaler);
|
|
};
|
|
```
|
|
|
|
Thread state tracked via:
|
|
```cpp
|
|
Atomic<long*> context{nullptr}; // saved stack pointer
|
|
Atomic<int32_t> priority{};
|
|
Atomic<int64_t> suspendedUntil{0}; // wake time
|
|
Atomic<void*> waitingFor{nullptr}; // synchronization target
|
|
Atomic<uint64_t> lastActivation{0};
|
|
```
|
|
|
|
### Scheduler
|
|
|
|
**Priority-based preemptive** scheduler using PendSV on Cortex-M:
|
|
- `Thread::findNextToRun()` iterates all threads, selects highest-priority ready thread
|
|
- Context switch via PendSV interrupt: save R4-R11 + EXC_RETURN to PSP, call `schedulerWrapper()`, restore next thread's context
|
|
- SVC handler for first thread start (idle thread)
|
|
- Time-driven: scheduler triggered by `TimeEvent::propagate()` in SysTick
|
|
|
|
```cpp
|
|
// Context switch chain (Cortex-M):
|
|
__asmSaveContextAndCallScheduler() → SCB->ICSR |= PENDSVSET →
|
|
PendSV_Handler: save regs → schedulerWrapper() → restore next thread regs
|
|
```
|
|
|
|
### Semaphore (`api/rodos-semaphore.h`)
|
|
|
|
**Mutex semaphore** with priority ceiling:
|
|
```cpp
|
|
class Semaphore {
|
|
Atomic<Thread*> owner;
|
|
Atomic<int32_t> ownerEnterCnt; // reentrant lock count
|
|
Atomic<int32_t> ownerPriority;
|
|
public:
|
|
void enter(); // blocking lock
|
|
void leave(); // unlock + resume highest-priority waiter
|
|
};
|
|
|
|
// RAII guard
|
|
class ScopeProtector { ... };
|
|
#define PROTECT_IN_SCOPE(_sema)
|
|
```
|
|
|
|
### Barrier (`api/barrier.h`)
|
|
```cpp
|
|
class Barrier {
|
|
Thread* volatile owner;
|
|
public:
|
|
bool waitForSignal(bool condition = true, int64_t timeOutAt = END_OF_TIME);
|
|
bool unblock();
|
|
};
|
|
```
|
|
|
|
### Event (`api/event.h`)
|
|
```cpp
|
|
class Event {
|
|
bool state_;
|
|
Thread *waiter_; // only ONE waiter!
|
|
public:
|
|
bool set(void); // trigger + optionally resume waiter
|
|
bool suspendUntilTriggered(int64_t timeout = END_OF_TIME);
|
|
void reset(void);
|
|
};
|
|
```
|
|
|
|
### Topic/Subscriber (Publish-Subscribe middleware, `api/topic.h`)
|
|
```cpp
|
|
template <class Type>
|
|
class Topic : public TopicInterface {
|
|
public:
|
|
Topic(int64_t id, const char* name, bool onlyLocal = false);
|
|
uint32_t publish(Type &msg, bool shallSendToNetwork = true);
|
|
};
|
|
|
|
class Subscriber : public SubscriberInterface {
|
|
public:
|
|
Subscriber(TopicInterface &topic, const char* name = "Subscriber");
|
|
virtual void put(uint32_t topicId, size_t len, void *msg, ...) = 0;
|
|
};
|
|
```
|
|
|
|
### Gateway (inter-node communication, `api/gateway/`)
|
|
```cpp
|
|
class Gateway : public ListElement {
|
|
virtual bool sendNetworkMessage(NetworkMessage &msg);
|
|
virtual void onIncomingMessage(NetworkMessage &msg);
|
|
};
|
|
```
|
|
Link interfaces: CAN, UART, UDP, Shared Memory.
|
|
|
|
### CommBuffer (`api/commbuffer.h`)
|
|
```cpp
|
|
template <class Type>
|
|
class CommBuffer { ... }; // lock-free single-producer/single-consumer buffer
|
|
```
|
|
|
|
## 4.4 API Surface
|
|
|
|
Language: **C++ (C++11 minimum)**. Single master header: `#include <rodos.h>` → `api/rodos.h`
|
|
|
|
Key headers:
|
|
```
|
|
thread.h, rodos-semaphore.h, barrier.h, event.h, topic.h, subscriber.h,
|
|
timeevent.h, timemodel.h, hal.h, gateway.h, commbuffer.h, application.h,
|
|
fifo.h, putter.h
|
|
```
|
|
|
|
**Application model**:
|
|
```cpp
|
|
class Application : public ListElement {
|
|
virtual long init() { return 0; }
|
|
virtual void run() { }
|
|
};
|
|
```
|
|
All `Application` and `Thread` instances are global objects. `main()` calls `initSystem()` → `initAllThreads()` → `startIdleThread()`.
|
|
|
|
## 4.5 Hardware Abstraction
|
|
|
|
File: `api/hal.h`, `api/hal/hal_*.h`
|
|
|
|
```cpp
|
|
class HW_HAL { /* base for all HW peripherals */ };
|
|
class GPIO : public HW_HAL { ... };
|
|
class UART : public HW_HAL { ... };
|
|
class SPI : public HW_HAL { ... };
|
|
class I2C : public HW_HAL { ... };
|
|
class ADC : public HW_HAL { ... };
|
|
class PWM : public HW_HAL { ... };
|
|
class CAN : public HW_HAL { ... };
|
|
class Ethernet : public HW_HAL { ... };
|
|
class SharedMemory : public HW_HAL { ... };
|
|
```
|
|
|
|
Each port implements the HAL classes. Platform-specific code in `src/bare-metal/<platform>/hal/`.
|
|
|
|
## 4.6 Tick/Timer Infrastructure
|
|
|
|
File: `api/timeevent.h`, `src/independent/timeevent.cpp`, `src/independent/timemodel.cpp`
|
|
|
|
```cpp
|
|
class TimeEvent : public ListElement {
|
|
static List timeEventList;
|
|
Atomic<int64_t> eventAt;
|
|
Atomic<int64_t> eventPeriod;
|
|
public:
|
|
virtual void handle(void) {} // override for custom handling
|
|
void activateAt(int64_t time);
|
|
void activatePeriodic(int64_t startAt, int64_t period);
|
|
static int32_t propagate(int64_t timeNow); // called from tick ISR
|
|
static int64_t getNextTriggerTime();
|
|
};
|
|
```
|
|
|
|
**Time model**: Uses nanosecond-precision `int64_t` time (`Nanoseconds`, `Microseconds`, `Milliseconds`, `Seconds` are all int64_t aliases). `NOW()` returns current time. `TimeModel::computeNextBeat()` handles period computation.
|
|
|
|
Tick ISR calls `TimeEvent::propagate(NOW())` which:
|
|
1. Iterates all TimeEvents
|
|
2. Fires handlers where `eventAt < now`
|
|
3. Updates `eventAt` for periodic events
|
|
4. Triggers scheduler if a thread became ready
|
|
|
|
## 4.7 Cooperative vs Preemptive
|
|
|
|
**Fully preemptive** via PendSV on Cortex-M:
|
|
- `TimeEvent::propagate()` runs in SysTick ISR
|
|
- When a higher-priority thread becomes ready, `__asmSaveContextAndCallScheduler()` triggers PendSV
|
|
- PendSV performs full context switch (R4-R11 + EXC_RETURN, optionally S16-S31 for FPU)
|
|
- `Thread::yield()` calls `resume()` + triggers reschedule
|
|
|
|
On POSIX platforms: preemption via POSIX signals (`SIGALRM` or timer threads).
|
|
|
|
---
|
|
|
|
# Comparative Summary for UniversalisOS
|
|
|
|
| Feature | ChibiOS/RT | RT-Thread | Contiki | RODOS |
|
|
|---------|-----------|-----------|---------|-------|
|
|
| **Language** | C | C | C | C++ |
|
|
| **Arch support** | ARM, AVR, PPC, x86 | 21 families, 107 BSPs | 33 platforms (sensor nodes) | ARM Cortex-M, RPi3, POSIX |
|
|
| **MMU needed** | No | No (optional RT-Smart uses it) | No | No |
|
|
| **Memory model** | Static working areas | Heap (small/slab/mempool) | Zero-allocation | Static C++ objects |
|
|
| **Thread model** | Preemptive, priority | Preemptive, priority | Stackless protothreads | Preemptive, priority |
|
|
| **Context switch** | SVC/PendSV (ARM) | arch-specific | None (continuation) | PendSV (ARM) |
|
|
| **IPC** | Sem, Mutex, CondVar, Events, Msg | Sem, Mutex, Event, MB, MQ | Event queue only | Sem, Barrier, Event, Topic/Subscriber |
|
|
| **Priority inherit.** | Yes (mutex) | Yes (mutex + ceiling) | N/A | Yes (semaphore) |
|
|
| **SMP** | Yes (CH_CFG_SMP_MODE) | Yes (RT_USING_SMP) | No | No (single-core) |
|
|
| **Timer** | Virtual timer delta list | Skip-list timer | 4-layer (timer/etimer/ctimer/rtimer) | TimeEvent list |
|
|
| **Tickless** | Yes (CH_CFG_ST_TIMEDELTA) | No (tick-based) | rtimer is real-time | Yes (nanosecond time) |
|
|
| **Min footprint** | ~1KB (NIL), ~5KB (RT) | ~3KB (nano mode) | ~1KB (protothreads) | ~2KB (Cortex-M) |
|
|
| **Scheduling** | Preemptive + round-robin | Preemptive + round-robin | Cooperative only | Preemptive |
|
|
|
|
## Key Design Patterns for UniversalisOS
|
|
|
|
1. **ChibiOS port contract** (`chcore.h`): Excellent model for hypervisor port layer. Defines exact macros every port must export (stack init, IRQ prologue/epilogue, context setup, priority validation).
|
|
|
|
2. **RT-Thread object system**: Uniform kernel object model with name, type, list node. Enables `rt_object_find()` for runtime discovery. Good pattern for hypervisor resource management.
|
|
|
|
3. **Contiki protothreads**: Zero-overhead "threads" for monitoring/management tasks that never need to block on hardware. Could be used for hypervisor event handlers.
|
|
|
|
4. **RODOS Topic/Subscriber**: Built-in publish-subscribe middleware for inter-partition communication in a hypervisor. The Gateway/LinkInterface pattern maps directly to inter-VM communication.
|
|
|
|
5. **ChibiOS SMP memory classes**: `CH_MEM_GLOBAL_COHERENT_BSS`, `CH_MEM_PRIVATE_BSS(c)` — directly applicable to hypervisor memory partitioning for cache-coherent shared regions.
|
|
|
|
6. **RT-Thread skip-list timers**: O(log n) timer insertion vs O(n) delta lists. Better for large numbers of virtual timers.
|
|
|
|
7. **RODOS nanosecond time**: `int64_t` nanoseconds avoids 32-bit wrap issues. Better for hypervisor wall-clock management than tick-based models.
|