A small, from-scratch preemptive priority-based RTOS kernel for ARM Cortex-M3, built without depending on FreeRTOS/Zephyr/any existing RTOS. ~1,200 lines total, no dynamic memory allocation, statically-sized task pool.
Written as a teaching-quality reference for how a PendSV-driven
Cortex-M RTOS actually works under the hood: priority-bitmap scheduling
(O(1) task selection via __builtin_ctz), round-robin among
equal-priority tasks, blocking semaphores/mutexes, tick-based sleep with
wraparound-safe wake comparisons, and the full exception-frame
choreography of a PendSV/SVC context switch.
Every other embedded project in this portfolio builds on Zephyr RTOS. This one deliberately does not — building a kernel on top of an existing kernel would defeat the point. This is what's actually underneath a production RTOS's scheduler.
Functionally complete: scheduler, task lifecycle, semaphores, mutexes,
Cortex-M3 context switch, STM32F103 clock/GPIO drivers, and a 3-task
demo (blinky_demo). See docs/VERIFICATION.md
for an honest breakdown of what's actually been tested (host-tested
scheduler logic + CI-verified ARM cross-compilation) versus what has
only been design-reviewed against the ARMv7-M architecture reference
manual and not yet run on physical silicon.
Full hardware requirements, exact part numbers, and wiring diagrams:
docs/hardware/BOM.md. Short version: an
STM32F103C8T6 "Blue Pill" board ($3) + an ST-Link V2 programmer
($3-8 clone) + 3 jumper wires. ~$10 total, no soldering.
# Host tests (no hardware/cross-compiler needed)
cd tests/host && make test
# Independent Python reference-model cross-check
python3 tools/scheduler_ref.py
# Cross-compile for real hardware (needs arm-none-eabi-gcc)
make
make flash # needs st-flash / stlink-tools + a wired-up boardinclude/tinykernel/tinykernel.h Public API
src/kernel/ Portable scheduler core (host-testable)
src/arch/cortex_m3/ Cortex-M3 context switch, SysTick, startup
src/drivers/ STM32F103 clock (RCC) + GPIO drivers
examples/blinky_demo/ 3-task demo: LED blink + producer/consumer via semaphore
tests/host/ Host-run scheduler unit tests
tools/scheduler_ref.py Independent Python model of the scheduler algorithm
linker/stm32f103c8t6.ld Linker script for the Blue Pill's memory map
docs/ARCHITECTURE.md Design rationale, scheduler algorithm, context-switch details
docs/VERIFICATION.md What's tested vs. design-reviewed-only, bugs found during dev
docs/hardware/BOM.md Parts list, wiring diagrams, build/flash instructions
tk_task_create(entry, arg, stack, stack_words, priority, &handle);
tk_start(); /* never returns */
tk_yield();
tk_sleep(ticks);
tk_sem_create(initial, max, &sem);
tk_sem_take(sem, timeout_ticks); /* 0 = non-blocking, UINT32_MAX = forever */
tk_sem_give(sem);
tk_mutex_create(&mutex);
tk_mutex_lock(mutex, timeout_ticks);
tk_mutex_unlock(mutex);See include/tinykernel/tinykernel.h for full documentation of each call.
- No priority inheritance on mutexes — priority inversion is possible.
- Semaphore/mutex timeouts only support
0andUINT32_MAX; arbitrary finite timeouts are treated as "forever" (disclosed, not silent). - Max 16 tasks, 32 priority levels — compile-time constants in
tinykernel.h, not currently configurable without editing the header. - Single-core only; no SMP.
MIT — see LICENSE.