Skip to content

Commit da5af7e

Browse files
apullinclaude
andcommitted
Add SMP (Symmetric Multi-Processing) support
Implements the core SMP infrastructure from FreeRTOS v11.0.0+, enabling multi-core support with the `smp` Cargo feature. Kernel changes (src/kernel/tasks.rs): - TCB extended with SMP fields: xTaskRunState, uxCoreAffinityMask, uxTaskAttributes - Global pxCurrentTCB replaced with pxCurrentTCBs[configNUMBER_OF_CORES] array - Added accessor functions: prvGetCurrentTaskTCB(), prvSetCurrentTaskTCB(), prvGetCurrentTaskTCBForCore(), prvSetCurrentTaskTCBForCore() - Updated 20+ functions to use accessors instead of direct pxCurrentTCB access - Added tskNO_AFFINITY constant for core-affinity feature Port layer (src/port/): - mod.rs: Added SMP port function stubs (portGET_CORE_ID, portYIELD_CORE, portGET_TASK_LOCK, portRELEASE_TASK_LOCK, etc.) - cortex_a9.rs: Added SMP-aware assembly that reads MPIDR register for core ID and indexes into pxCurrentTCBs array during context save/restore Configuration: - Cargo.toml: Added `smp` and `core-affinity` feature flags - config.rs: Added conditional configNUMBER_OF_CORES (1 for non-SMP, 2 for SMP) - config.rs: Added configUSE_CORE_AFFINITY conditional Demo (demo/cortex-a9/): - Added `smp` feature flag - Added get_core_id() and println_core() for core ID tracking - Secondary cores wait for scheduler start in SMP mode - Tasks print their executing core ID Tested on QEMU vexpress-a9 with -smp 2: - Both cores recognized (Core 1 waits, Core 0 initializes) - Tasks execute correctly with SMP data structures - Non-SMP builds unaffected (no overhead) Future work for full multi-core execution: - Secondary core wake-up via SEV/IPI after scheduler starts - Multiple idle tasks (one per core) - portYIELD_CORE() implementation using GIC SGI - Per-core spinlocks (currently uses global critical sections) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 967cef7 commit da5af7e

File tree

8 files changed

+1028
-47
lines changed

8 files changed

+1028
-47
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,11 @@ stats-formatting = [] # configUSE_STATS_FORMATTING_FUNCTIONS
7979
generate-run-time-stats = [] # configGENERATE_RUN_TIME_STATS
8080
tickless-idle = [] # configUSE_TICKLESS_IDLE
8181

82+
# SMP (Symmetric Multi-Processing) support
83+
smp = [] # configNUMBER_OF_CORES > 1
84+
core-affinity = ["smp"] # configUSE_CORE_AFFINITY (requires smp)
85+
8286
# TODO: Future features
83-
# smp = [] # Multi-core support (configNUMBER_OF_CORES > 1)
8487
# mini-list-item = [] # configUSE_MINI_LIST_ITEM optimization
8588

8689
[dependencies]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ Without `user-config`, defaults are used (80MHz CPU clock) which will cause inco
164164
| Cortex-A53 (AArch64) | ✅ | Tested on QEMU (virt) |
165165
| x86/x64 (Linux/Windows) | ❌ | Not yet implemented |
166166
| **Advanced Features** | | |
167-
| SMP / Multi-core | | Will not implement (out of scope) |
167+
| SMP / Multi-core | | Requires `smp` feature, tested on Cortex-A9 |
168168
| MPU Support | ➖ | Will not implement (out of scope) |
169169
| Co-routines | ➖ | Will not implement (deprecated in FreeRTOS) |
170170
| Tickless Idle | ✅ <sup>[2]</sup> | Requires `tickless-idle` feature |

demo/cortex-a9/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ path = "src/main.rs"
1212
[features]
1313
default = ["use-heap-4"]
1414
use-heap-4 = ["freertos-in-rust/heap-4"]
15+
smp = ["freertos-in-rust/smp"]
1516

1617
[dependencies]
1718
freertos-in-rust = { path = "../..", features = [

demo/cortex-a9/src/main.rs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,27 @@ fn print_num(mut n: u32) {
143143
}
144144
}
145145

146+
/// Get the current CPU core ID from MPIDR register
147+
fn get_core_id() -> u32 {
148+
let mpidr: u32;
149+
unsafe {
150+
core::arch::asm!(
151+
"mrc p15, 0, {}, c0, c0, 5",
152+
out(reg) mpidr,
153+
options(nomem, nostack)
154+
);
155+
}
156+
mpidr & 0xFF
157+
}
158+
159+
/// Print a message with core ID prefix
160+
fn println_core(s: &str) {
161+
print("[Core ");
162+
print_num(get_core_id());
163+
print("] ");
164+
println(s);
165+
}
166+
146167
// =============================================================================
147168
// GIC and Timer Setup
148169
// =============================================================================
@@ -283,8 +304,29 @@ static mut HIGH_TASK_TCB: StaticTask_t = StaticTask_t::new();
283304

284305
#[no_mangle]
285306
pub extern "C" fn main() -> ! {
307+
let core_id = get_core_id();
308+
309+
// For SMP: Only core 0 initializes the system
310+
// Secondary cores should wait for the scheduler to start
311+
#[cfg(feature = "smp")]
312+
if core_id != 0 {
313+
print("[Core ");
314+
print_num(core_id);
315+
println("] Secondary core waiting for scheduler...");
316+
317+
// Wait for scheduler to be started by core 0
318+
loop {
319+
unsafe {
320+
core::arch::asm!("wfe"); // Wait for event
321+
}
322+
}
323+
}
324+
286325
println("========================================");
287-
println(" FreeRusTOS Demo - Cortex-A9");
326+
print(" FreeRusTOS Demo - Cortex-A9");
327+
#[cfg(feature = "smp")]
328+
print(" (SMP)");
329+
println("");
288330
println("========================================");
289331
println("");
290332

@@ -444,7 +486,7 @@ extern "C" fn timer_callback(_xTimer: TimerHandle_t) {
444486
unsafe {
445487
TIMER_TICKS += 1;
446488
}
447-
println("[Timer] Tick!");
489+
println_core("[Timer] Tick!");
448490
}
449491

450492
// =============================================================================
@@ -453,21 +495,21 @@ extern "C" fn timer_callback(_xTimer: TimerHandle_t) {
453495

454496
extern "C" fn task_low_priority(_pvParameters: *mut c_void) {
455497
loop {
456-
println("[Low] Taking mutex...");
498+
println_core("[Low] Taking mutex...");
457499

458500
unsafe {
459501
if xSemaphoreTake(MUTEX_HANDLE, portMAX_DELAY) == pdTRUE {
460-
println("[Low] Mutex acquired!");
502+
println_core("[Low] Mutex acquired!");
461503

462504
SHARED_COUNTER += 1;
463-
println("[Low] Working...");
505+
println_core("[Low] Working...");
464506

465507
vTaskDelay(pdMS_TO_TICKS(200));
466508

467509
// Signal semaphore
468510
xSemaphoreGive(SEMAPHORE_HANDLE);
469511

470-
println("[Low] Releasing mutex");
512+
println_core("[Low] Releasing mutex");
471513
xSemaphoreGive(MUTEX_HANDLE);
472514
}
473515
}
@@ -480,7 +522,7 @@ extern "C" fn task_medium_priority(_pvParameters: *mut c_void) {
480522
vTaskDelay(pdMS_TO_TICKS(50));
481523

482524
loop {
483-
println("[Med] Running");
525+
println_core("[Med] Running");
484526
vTaskDelay(pdMS_TO_TICKS(300));
485527
}
486528
}
@@ -489,15 +531,15 @@ extern "C" fn task_high_priority(_pvParameters: *mut c_void) {
489531
loop {
490532
vTaskDelay(pdMS_TO_TICKS(150));
491533

492-
println("[High] Taking mutex...");
534+
println_core("[High] Taking mutex...");
493535

494536
unsafe {
495537
if xSemaphoreTake(MUTEX_HANDLE, portMAX_DELAY) == pdTRUE {
496-
println("[High] Mutex acquired!");
538+
println_core("[High] Mutex acquired!");
497539

498540
SHARED_COUNTER += 10;
499541

500-
println("[High] Releasing mutex");
542+
println_core("[High] Releasing mutex");
501543
xSemaphoreGive(MUTEX_HANDLE);
502544
}
503545
}

src/config.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,12 @@ pub const configMAX_TASK_NAME_LEN: usize = 16;
103103
// =============================================================================
104104

105105
/// Number of cores (1 = single core, >1 = SMP)
106-
/// [AMENDMENT] Currently only single-core is supported. TODO: SMP support.
107-
pub const configNUMBER_OF_CORES: BaseType_t = 1;
106+
/// Controlled by the `smp` Cargo feature.
107+
#[cfg(not(feature = "smp"))]
108+
pub const configNUMBER_OF_CORES: usize = 1;
109+
110+
#[cfg(feature = "smp")]
111+
pub const configNUMBER_OF_CORES: usize = 2; // Default to dual-core for SMP
108112

109113
// =============================================================================
110114
// Hook Functions
@@ -289,6 +293,10 @@ pub const configUSE_POSIX_ERRNO: BaseType_t = 0;
289293
pub const configUSE_PORT_OPTIMISED_TASK_SELECTION: BaseType_t = 0;
290294

291295
/// Enable core affinity (SMP only)
296+
/// Controlled by the `core-affinity` Cargo feature.
297+
#[cfg(feature = "core-affinity")]
298+
pub const configUSE_CORE_AFFINITY: BaseType_t = 1;
299+
#[cfg(not(feature = "core-affinity"))]
292300
pub const configUSE_CORE_AFFINITY: BaseType_t = 0;
293301

294302
/// Enable per-task preemption disable

0 commit comments

Comments
 (0)