A line-by-line port of the FreeRTOS kernel from C to Rust.
This project is published on crates.io. Note that the project is still in active development and the API/feature set may evolve.
- 0.1.x — Direct port from
masterbranch. Closely mirrors C FreeRTOS semantics; usesunsafethroughout. - 0.2.x — From
safe_shimbranch. Adds Rust-idiomatic safe wrappers that reduceunsafeusage.
The goal here is to duplicate the FreeRTOS C implementation in Rust, as closely as possible. Ideally, this gives us a package that runs fully in Rust (plus minimal assembly for context switching), while retaining FreeRTOS usage semantics and API structure.
This is not a Rust wrapper around FreeRTOS C code. There is no C FFI. The kernel is pure Rust.
The master branch contains the most direct port, with all features except SMP. This heavily uses unsafe, as all constructs are kept as close to the C code as possible.
The safe_shim branch adds an interface shim that elides most of the unsafe usage, and is more Rust-idiomatic. See RUST_WRAPPER.md for details.
SMP support does appear to work, and is in smp branch - separated because it steps on a fair amount of definitions throughout the code.
File tree for the core code:
src/
├── lib.rs # Crate root, re-exports
├── config.rs # FreeRTOSConfig.h equivalent
├── types.rs # Base types (BaseType_t, TickType_t, etc.)
├── kernel/
│ ├── tasks.rs # Task management (tasks.c)
│ ├── queue.rs # Queues & semaphores (queue.c)
│ ├── list.rs # Linked list implementation (list.c)
│ ├── timers.rs # Software timers (timers.c)
│ ├── event_groups.rs # Event groups (event_groups.c)
│ └── stream_buffer.rs# Stream buffers (stream_buffer.c)
├── port/
│ ├── cortex_m4f.rs # Cortex-M4F port (ARMv7E-M with FPU)
│ ├── cortex_m7.rs # Cortex-M7 port (reexports CM4F)
│ ├── cortex_m3.rs # Cortex-M3 port (ARMv7-M, no FPU)
│ ├── cortex_m0.rs # Cortex-M0/M0+ port (ARMv6-M, Thumb-1)
│ ├── cortex_a9.rs # Cortex-A9 port (ARMv7-A, GIC, SWI)
│ ├── cortex_a53.rs # Cortex-A53 port (ARMv8-A/AArch64, GIC, SVC)
│ ├── riscv32.rs # RISC-V RV32 port (RV32I/RV32IMAC)
│ └── dummy.rs # Dummy port for testing
├── memory/
│ └── mod.rs # pvPortMalloc/vPortFree
└── trace.rs # Trace macros (no-op by default)
Rust doesn't have header files, so FreeRTOSConfig.h is replaced by two mechanisms:
Boolean flags (e.g., #define configUSE_MUTEXES 1) become Cargo features:
[dependencies]
freertos-in-rust = { version = "0.1", features = [
"port-cortex-m4f", # Select your port (or port-cortex-m3, port-cortex-m0, port-cortex-m7, port-riscv32, port-cortex-a9, port-cortex-a53)
"heap-4", # Allocator: heap_4 (or "heap-5" for multi-region, or "alloc" for external)
"use-mutexes", # configUSE_MUTEXES
"timers", # configUSE_TIMERS
"task-delete", # INCLUDE_vTaskDelete
"task-suspend", # INCLUDE_vTaskSuspend
] }See Memory Allocator for choosing between heap-4, heap-5, and alloc.
Numeric constants (e.g., #define configTOTAL_HEAP_SIZE 10240) live in src/config.rs and are referenced as crate::config::configTOTAL_HEAP_SIZE:
// src/config.rs
pub const configMAX_PRIORITIES: UBaseType_t = 5;
pub const configTICK_RATE_HZ: TickType_t = 1000;
pub const configTOTAL_HEAP_SIZE: usize = 10240;
pub const configTIMER_TASK_PRIORITY: UBaseType_t = 2;
// ... etcHardware-specific values (CPU clock frequency) require the user-config feature:
[dependencies]
freertos-in-rust = { version = "0.1", features = ["user-config", ...] }Then define the required symbols in your crate:
// In your main.rs or lib.rs
#[no_mangle]
pub static FREERTOS_CONFIG_CPU_CLOCK_HZ: u32 = 32_000_000; // Your CPU clock
// For RISC-V, also provide:
#[no_mangle]
pub static FREERTOS_CONFIG_MTIME_HZ: u32 = 10_000_000; // Timer frequencyWithout user-config, defaults are used (80MHz CPU clock) which will cause incorrect SysTick timing.
| Feature | Status | Notes |
|---|---|---|
| Task Management | ||
| xTaskCreate (dynamic) | ✅ | Requires alloc feature |
| xTaskCreateStatic | ✅ | |
| vTaskDelete | ✅ | Requires task-delete feature |
| vTaskDelay / vTaskDelayUntil | ✅ | |
| vTaskSuspend / vTaskResume | ✅ | Requires task-suspend feature |
| vTaskPrioritySet / Get | ✅ | Requires task-priority-set feature |
| xTaskAbortDelay | ✅ | Requires abort-delay feature |
| eTaskGetState | ✅ | |
| Task Notifications | ✅ | |
| Thread Local Storage | ✅ | Requires thread-local-storage feature |
| Application Task Tags | ✅ | Requires application-task-tag feature |
| Stack High Water Mark | ✅ | Requires stack-high-water-mark feature |
| vTaskGetInfo / uxTaskGetSystemState | ✅ | Requires trace-facility feature |
| vTaskListTasks | ✅ | Requires stats-formatting feature |
| Queues & Semaphores | ||
| xQueueCreate (dynamic) | ✅ | Requires alloc feature |
| xQueueCreateStatic | ✅ | |
| xQueueSend / Receive / Peek | ✅ | All variants (ToFront, ToBack, FromISR) |
| xQueueGiveFromISR | ✅ | Optimized semaphore give from ISR |
| vQueueDelete | ✅ | Requires alloc feature |
| Binary Semaphores | ✅ | |
| Counting Semaphores | ✅ | xQueueCreateCountingSemaphore/Static |
| Mutexes | ✅ | Requires use-mutexes feature |
| Recursive Mutexes | ✅ | Requires use-mutexes feature |
| Priority Inheritance | ✅ | |
| Queue Sets | ✅ | Requires queue-sets feature |
| Queue Registry | ✅ | Requires queue-registry feature |
| Queue Trace Functions | ✅ | Requires trace-facility feature |
| Timers | ||
| Software Timers | ✅ | Requires timers feature |
| xTimerCreate (dynamic) | ✅ | |
| xTimerCreateStatic | ✅ | |
| Timer Daemon Task | ✅ | |
| Event Groups | ||
| Event Groups | ✅ | |
| Stream/Message Buffers | ||
| Stream Buffers | ✅ | Requires stream-buffers feature |
| Message Buffers | ✅ | Requires stream-buffers feature |
| Memory Management | ||
| pvPortMalloc / vPortFree | ✅ | Wraps Rust's #[global_allocator] |
| Static Allocation | ✅ | |
| heap_4 | ✅ | Single contiguous region (see heap-4 feature) |
| heap_5 | ✅ | Multiple non-contiguous regions (see heap-5 feature) |
| heap_1, heap_2, heap_3 | ➖ | N/A - use Rust allocator crates instead |
| Ports | ||
| Cortex-M4F | ✅ | Tested on QEMU (lm3s6965evb) |
| Cortex-M7 | ✅ [1] | Tested on QEMU |
| Cortex-M3 | ✅ | Tested on QEMU (lm3s6965evb) |
| Cortex-M0/M0+ | ✅ | Tested on QEMU (microbit) |
| RISC-V RV32 | ✅ | Tested on QEMU (sifive_e) |
| Cortex-A9 | ✅ | Tested on QEMU (vexpress-a9) |
| Cortex-A53 (AArch64) | ✅ | Tested on QEMU (virt) |
| x86/x64 (Linux/Windows) | ❌ | Not yet implemented |
| Advanced Features | ||
| SMP / Multi-core | ➖ | Will not implement (out of scope) |
| MPU Support | ➖ | Will not implement (out of scope) |
| Co-routines | ➖ | Will not implement (deprecated in FreeRTOS) |
| Tickless Idle | ✅ [2] | Requires tickless-idle feature |
| Run-time Stats | ✅ | Requires generate-run-time-stats feature |
Legend: ✅ Supported | ❌ Not yet done |
[1] Cortex-M7 reuses the CM4F port, per FreeRTOS's recommendation. Early CM7 silicon (r0p0/r0p1) requires ARM Errata 837070 workaround, which is not currently implemented. Most modern CM7 chips do not need this workaround.
[2] Tickless idle support is per-port. It has been tested and appears to work correctly in QEMU for all ports included in this repository.
Demos are available for all supported ports.
# Install Rust ARM targets
rustup target add thumbv7em-none-eabihf # CM4F, CM7
rustup target add thumbv7m-none-eabi # CM3
rustup target add thumbv6m-none-eabi # CM0
# Install Rust RISC-V target
rustup target add riscv32imac-unknown-none-elf # RISC-V RV32
# Install Rust ARM Cortex-A targets
rustup target add armv7a-none-eabi # Cortex-A9
rustup target add aarch64-unknown-none # Cortex-A53
# Install QEMU (macOS)
brew install qemu
# Install QEMU (Ubuntu/Debian)
sudo apt install qemu-system-arm qemu-system-misccd demo/cortex-m4f
cargo build --release
qemu-system-arm \
-cpu cortex-m4 \
-machine lm3s6965evb \
-nographic \
-semihosting-config enable=on,target=native \
-kernel target/thumbv7em-none-eabihf/release/democd demo/cortex-m3
cargo build --release
qemu-system-arm \
-cpu cortex-m3 \
-machine lm3s6965evb \
-nographic \
-semihosting-config enable=on,target=native \
-kernel target/thumbv7m-none-eabi/release/democd demo/cortex-m0
cargo build --release
qemu-system-arm \
-cpu cortex-m0 \
-machine microbit \
-nographic \
-semihosting-config enable=on,target=native \
-kernel target/thumbv6m-none-eabi/release/democd demo/riscv32
cargo build --release
qemu-system-riscv32 \
-machine sifive_e \
-nographic \
-kernel target/riscv32imac-unknown-none-elf/release/democd demo/cortex-a9
cargo build --release
qemu-system-arm \
-machine vexpress-a9 \
-cpu cortex-a9 \
-m 128M \
-nographic \
-semihosting-config enable=on,target=native \
-kernel target/armv7a-none-eabi/release/democd demo/cortex-a53
cargo build --release
qemu-system-aarch64 \
-machine virt \
-cpu cortex-a53 \
-nographic \
-semihosting-config enable=on,target=native \
-kernel target/aarch64-unknown-none/release/demoYou should see output like:
========================================
FreeRusTOS Demo - Cortex-M0
========================================
[Init] Creating mutex...
[Init] Mutex created successfully
...
[Main] Starting scheduler...
Press Ctrl-A X to exit QEMU.
FreeRusTOS supports dynamic memory allocation via Cargo feature flags. This is a project-level configuration—select the allocator that fits your needs.
FreeRTOS's heap_4.c is one of the most commonly used allocators in embedded FreeRTOS projects. We've ported it to Rust:
- First-fit allocation with block splitting
- Coalescing on free — merges adjacent free blocks to reduce fragmentation
- Fixed heap size — configured via
configTOTAL_HEAP_SIZEinconfig.rs - Deterministic timing — important for real-time systems
freertos-in-rust = { version = "0.1", features = ["heap-4"] }use freertos_in_rust::memory::FreeRtosAllocator;
#[global_allocator]
static ALLOCATOR: FreeRtosAllocator = FreeRtosAllocator;Like heap-4, but supports multiple non-contiguous memory regions. Ideal for systems with:
- Internal SRAM + external PSRAM (ESP32, STM32 with QSPI RAM)
- Cortex-M7 with TCM + main RAM
- Any chip with multiple RAM banks
freertos-in-rust = { version = "0.1", features = ["heap-5"] }use freertos_in_rust::memory::{vPortDefineHeapRegions, HeapRegion, FreeRtosAllocator};
// Define your memory regions (must be in ascending address order)
static mut SRAM: [u8; 32768] = [0; 32768];
static mut PSRAM: [u8; 65536] = [0; 65536];
#[global_allocator]
static ALLOCATOR: FreeRtosAllocator = FreeRtosAllocator;
fn main() {
// Initialize heap BEFORE any allocations
unsafe {
vPortDefineHeapRegions(&[
HeapRegion::new(SRAM.as_mut_ptr(), SRAM.len()),
HeapRegion::new(PSRAM.as_mut_ptr(), PSRAM.len()),
]);
}
}If you prefer a simpler Rust-native solution, use the alloc feature with an external allocator like embedded-alloc. This is a well-maintained linked-list allocator from the embedded Rust ecosystem:
freertos-in-rust = { version = "0.1", features = ["alloc"] }
[dependencies]
embedded-alloc = "0.6"use embedded_alloc::LlffHeap as Heap;
#[global_allocator]
static HEAP: Heap = Heap::empty();
fn main() {
// Initialize heap (required for embedded-alloc)
static mut HEAP_MEM: [MaybeUninit<u8>; 16384] = [MaybeUninit::uninit(); 16384];
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, 16384) }
}For fully static allocation, don't enable any heap feature:
- Use only
*StaticAPI variants (e.g.,xTaskCreateStatic,xQueueCreateStatic) pvPortMallocwill panic if called
This is currently a monolithic single-crate repository. Future work includes:
- Split into workspace with separate crates:
freertos-kernel- Core kernelfreertos-port-cortex-m- Cortex-M portsfreertos-port-riscv- RISC-V ports (future)
- Publish to crates.io
- Add
#![no_std]examples for various boards
- Additional ports:
- RISC-V 64-bit
- x86/x64
- Debug printing: A holistic solution for debug output in demos is not yet solidified. Using
core::fmt::Writeadds significant code size (~40KB), so demos currently use simple direct print functions. This needs further investigation into what Rust offers for lightweightno_stdformatting.
- Add unit tests (requires test harness)
- Add integration tests running on QEMU
- CI/CD pipeline
MIT License - same as FreeRTOS.
This is a clean-room port; no FreeRTOS C source code is included in the compiled output. The FreeRTOS-Kernel/ directory (if present) is for reference only and is excluded from version control.