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