freertos-in-rust 0.2.0

FreeRTOS kernel ported to Rust - no_std, no C FFI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# FreeRusTOS

[![CI](https://github.com/apullin/freertos-in-rust/actions/workflows/ci.yml/badge.svg)](https://github.com/apullin/freertos-in-rust/actions/workflows/ci.yml)

A line-by-line port of the FreeRTOS kernel from C to Rust.

## Crate

This project is published on [crates.io](https://crates.io/crates/freertos-in-rust). Note that the project is still in active development and the API/feature set may evolve.

- **0.1.x** — Direct port from `master` branch. Closely mirrors C FreeRTOS semantics; uses `unsafe` throughout.
- **0.2.x** — From `safe_shim` branch. Adds Rust-idiomatic safe wrappers that reduce `unsafe` usage.

## Intent

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.

## Project Structure

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)
```

## Configuration

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:

```toml
[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](#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`:

```rust
// 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;
// ... etc
```

**Hardware-specific values** (CPU clock frequency) require the `user-config` feature:

```toml
[dependencies]
freertos-in-rust = { version = "0.1", features = ["user-config", ...] }
```

Then define the required symbols in your crate:

```rust
// 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 frequency
```

Without `user-config`, defaults are used (80MHz CPU clock) which will cause incorrect SysTick timing.

## Feature Support

| 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 |<sup>[1]</sup> | 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 |<sup>[2]</sup> | Requires `tickless-idle` feature |
| Run-time Stats || Requires `generate-run-time-stats` feature |

**Legend:** ✅ Supported | ❌ Not yet done | ⚠️ Partial | ➖ Will not implement

<sup>[1]</sup> *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.*

<sup>[2]</sup> *Tickless idle support is per-port. It has been tested and appears to work correctly in QEMU for all ports included in this repository.*

## Running the Demos on QEMU

Demos are available for all supported ports.

### Prerequisites

```bash
# 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-misc
```

### Cortex-M4F Demo

```bash
cd 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/demo
```

### Cortex-M3 Demo

```bash
cd 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/demo
```

### Cortex-M0 Demo

```bash
cd 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/demo
```

### RISC-V RV32 Demo

```bash
cd demo/riscv32
cargo build --release
qemu-system-riscv32 \
  -machine sifive_e \
  -nographic \
  -kernel target/riscv32imac-unknown-none-elf/release/demo
```

### Cortex-A9 Demo

```bash
cd 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/demo
```

### Cortex-A53 Demo (AArch64)

```bash
cd 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/demo
```

You 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.

## Memory Allocator

FreeRusTOS supports dynamic memory allocation via Cargo feature flags. This is a project-level configuration—select the allocator that fits your needs.

### Option 1: `heap-4` — Ported FreeRTOS Allocator

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_SIZE` in `config.rs`
- **Deterministic timing** — important for real-time systems

```toml
freertos-in-rust = { version = "0.1", features = ["heap-4"] }
```

```rust
use freertos_in_rust::memory::FreeRtosAllocator;

#[global_allocator]
static ALLOCATOR: FreeRtosAllocator = FreeRtosAllocator;
```

### Option 2: `heap-5` — Multi-Region Allocator

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

```toml
freertos-in-rust = { version = "0.1", features = ["heap-5"] }
```

```rust
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()),
        ]);
    }
}
```

### Option 3: `alloc` — Rust-Native Allocator

If you prefer a simpler Rust-native solution, use the `alloc` feature with an external allocator like [`embedded-alloc`](https://crates.io/crates/embedded-alloc). This is a well-maintained linked-list allocator from the embedded Rust ecosystem:

```toml
freertos-in-rust = { version = "0.1", features = ["alloc"] }

[dependencies]
embedded-alloc = "0.6"
```

```rust
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) }
}
```

### Static-only (No heap)

For fully static allocation, don't enable any heap feature:
- Use only `*Static` API variants (e.g., `xTaskCreateStatic`, `xQueueCreateStatic`)
- `pvPortMalloc` will panic if called

## TODOs

### Crate Structure

This is currently a monolithic single-crate repository. Future work includes:

- Split into workspace with separate crates:
  - `freertos-kernel` - Core kernel
  - `freertos-port-cortex-m` - Cortex-M ports
  - `freertos-port-riscv` - RISC-V ports (future)
- Publish to crates.io
- Add `#![no_std]` examples for various boards

### Missing Features

- 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::Write` adds significant code size (~40KB), so demos currently use simple direct print functions. This needs further investigation into what Rust offers for lightweight `no_std` formatting.

### Testing

- Add unit tests (requires test harness)
- Add integration tests running on QEMU
- CI/CD pipeline

## License

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.