π« HardRT [ExoSpaceLabs]
HardRT is the heartbeat of small embedded systems.
A tiny, portable, modular real-time operating system written in C.
Minimal footprint, predictable behavior, and zero hardware dependencies in its core.
Version: 0.4.0
- Pure C core β no dynamic allocation, no HAL dependencies.
- Portable ports β currently: null, posix, cortex-m.
- Scheduler β priority, round-robin, or hybrid.
- Task control β
hrt_sleep(),hrt_yield(),hrt_task_delete(), andhrt_now_ms()millisecond helper. - Semaphores (binary + counting) β blocking take,
try_take, ISR-safegivewith FIFO wake-up; counting mode viahrt_sem_init_counting. - Mutexes β owner-tracked, non-recursive, FIFO waiter queue with direct handoff on unlock.
- Message Queues β fixed-size, copy-based FIFO, blocking/non-blocking and ISR support.
- Static tasks β stacks and TCBs supplied by the application.
- CMake package β install and consume via
find_package(HardRT). - Generated metadata β version and port headers at build time.
- Optional C++17 wrapper β header-only interface target when enabled. See C++ Guide.
Please refer to PORTING.md for additional port inclusion.
The POSIX port is for logic verification, not timing accuracy.
ucontextis used and supported on Linux/glibc.
βOn Cortex-M, the max time from tick to running the next highest priority ready task is bounded by: ISR tail + PendSV latency + context save/restoreβ
The POSIX port is regularly validated on Ubuntu 22.04, Ubuntu 24.04, and GitHub CI Linux environments. A test-suite failure has been observed on Debian 13 and is currently under investigation.
The Architecture is mainly divided into three layers:
- Application Layer: Where the tasks are defined. e.g., camera, UART downlink, HK/FDIR, etc.
- HardRT Core: Where the RTOS lives, manages tasks, and calls the port to switch context when necessary.
- HardRT Port: Wraps the hardware-specific methods.
- Hardware Layer: Hardware specific methods, registers, primitives, etc.
βEvent-to-task latency in HardRT depends on task priority. When multiple tasks are woken concurrently, the highest-priority task consistently achieves minimal latency, while lower-priority tasks incur bounded additional delay.β
Where each task is executed in accordance with the policy adopted by the scheduler.
Note: If a task exits, it is automatically deleted (EXIT) and the scheduler won't requeue it.
see also Concepts
hardrt/
βββ inc/ # Public headers
βββ src/ # Core + port implementations
β βββ core/ # Kernel internals
β βββ port/ # Architecture-specific backends (null, posix, cortex_m)
βββ cpp/ # Optional C++17 interface
βββ cmake/ # additional cmake files and toolchains
βββ examples/ # Example applications
βββ tests/ # POSIX test harness
βββ scripts/ # scripts to build and test the project
βββ docs/ # Documentation
βββ LICENSE
βββ README.md
mkdir -p build && cd build
cmake -DHARDRT_PORT=posix -DHARDRT_BUILD_EXAMPLES=ON ..
cmake --build . -j$(nproc)
./examples/two_tasks/two_tasksInstall package:
cmake --install . --prefix "$PWD/install"Consume from another CMake project:
find_package(HardRT 0.4.0 REQUIRED)
add_executable(app main.c)
target_link_libraries(app PRIVATE HardRT::hardrt)For further information and CMake flags, see the Build document.
- Tick: base time unit generated by a timer interrupt (or POSIX signal). HardRT increments a counter each tick and wakes sleepers.
- Timeslice: number of ticks a task may run before being rotated under RR. In the current implementation, rotation is triggered safely through the scheduler path rather than by direct context switching inside the tick hook.
Caption:
- Policy:
HRT_SCHED_PRIORITY_RR(RR applies within same priority). - Two READY tasks with
timeslice=1 ms. Handoffs occur exactly at every tick... - Self-transitions mark per-tick continuity when no interrupt/context switch occurs.
Caption:
- Identical example with
timeslice=2 msis shown above, in essence handoffs occur every two ticks.
each task is executed in order.
Caption:
- this example shows four tasks with different priorities.
- D the lowest priority task running continuously.
- D is interrupted at T6 by Task A lasting one tick and resumes Task D.
- D is interrupted once again by higher priority task C at T10 lasting three ticks.
- C as well is interrupted by Task B (an even higher task) at T11 lasting two ticks.
- B returns to C, which finishes the remaining work (two more ticks); and returns to D.
- Self-transitions mark per-tick continuity when no interrupt/context switch occurs.
RR with preemption allows the usage of both policies within the same context.
hrt_sem_init,hrt_sem_init_counting,hrt_sem_take,hrt_sem_try_take,hrt_sem_give,hrt_sem_give_from_isr.- Use semaphores for event signaling, resource counting, and producer/consumer synchronization.
hrt_mutex_init,hrt_mutex_lock,hrt_mutex_try_lock,hrt_mutex_unlock.- Owner-tracked, non-recursive, task-context-only mutual exclusion primitive.
- Unlock performs direct handoff to one waiter when contention exists.
- Current implementation does not provide priority inheritance or timed lock.
hrt_queue_init,hrt_queue_send,hrt_queue_recv,hrt_queue_try_send,hrt_queue_try_recv.- Fixed-size items, copy-based FIFO. See QUEUES.md.
Tick (ISR/signal) -> hrt_tick_from_isr():
- g_tick++
- wake any SLEEP tasks whose wake_tick β now
- hrt__pend_context_switch() (set resched flag)
Scheduler loop (port):
- if resched flag:
- next = hrt__pick_next_ready()
- swapcontext/PendSV to next task
Task-level yield/sleep:
- mark state (READYβqueue or SLEEP)
- hrt__pend_context_switch()
- hrt_port_yield_to_scheduler() (safe handoff from task ctx)
See STATISTICS.md for detailed information on timing and tests collected for HardRT v0.4.0.
These results provide a solid baseline for further optimization and for documenting real-time behavior guarantees. Fundamental deterministic behavior remains identical to previous versions.
Apache License 2.0 β see LICENSE.





