Skip to content

Commit 6f287c1

Browse files
Merge pull request #24 from rust-embedded/add-multicore-example
Add multicore example
2 parents b493320 + f0b74bb commit 6f287c1

File tree

14 files changed

+510
-58
lines changed

14 files changed

+510
-58
lines changed

cortex-a-rt/src/lib.rs

+18
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@
5656
//! `__sbss` and `__ebss` is zeroed, and the memory between `__sdata` and
5757
//! `__edata` is initialised with the data found at `__sidata`.
5858
//!
59+
//! The stacks look like:
60+
//!
61+
//! ```text
62+
//! +------------------+ <----_stack_top
63+
//! | UND Stack | } _und_stack_size bytes
64+
//! +------------------+
65+
//! | SVC Stack | } _svc_stack_size bytes
66+
//! +------------------+
67+
//! | ABT Stack | } _abt_stack_size bytes
68+
//! +------------------+
69+
//! | IRQ Stack | } _irq_stack_size bytes
70+
//! +------------------+
71+
//! | FIQ Stack | } _fiq_stack_size bytes
72+
//! +------------------+
73+
//! | SYS Stack | } No specific size
74+
//! +------------------+
75+
//! ```
76+
//!
5977
//! ### C-Compatible Functions
6078
//!
6179
//! * `kmain` - the `extern "C"` entry point to your application.

cortex-ar/Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ version = "0.1.0"
2828
arbitrary-int = "1.3.0"
2929
bitbybit = "1.3.3"
3030
num_enum = { version = "0.7", default-features = false }
31-
critical-section = {version = "1.2.0", features = ["restore-state-bool"], optional = true}
31+
critical-section = {version = "1.2.0", features = ["restore-state-u8"], optional = true}
3232
defmt = {version = "0.3", optional = true}
3333

3434
[build-dependencies]
@@ -38,5 +38,8 @@ arm-targets = {version = "0.1.0", path = "../arm-targets"}
3838
# Adds a critical-section implementation that only disables interrupts.
3939
# This is not sound on multi-core systems because interrupts are per-core.
4040
critical-section-single-core = ["critical-section"]
41+
# Adds a critical-section implementation that disables interrupts and does
42+
# a CAS spinlock.
43+
critical-section-multi-core = ["critical-section"]
4144
# Adds defmt::Format implementation for the register types
4245
defmt = ["dep:defmt"]

cortex-ar/src/asm.rs

+12
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,15 @@ pub fn sev() {
5656
core::arch::asm!("sev");
5757
}
5858
}
59+
60+
/// Which core are we?
61+
///
62+
/// Return the bottom 24-bits of the MPIDR
63+
#[inline]
64+
pub fn core_id() -> u32 {
65+
let r: u32;
66+
unsafe {
67+
core::arch::asm!("MRC p15, 0, {}, c0, c0, 5", out(reg) r, options(nomem, nostack, preserves_flags));
68+
}
69+
return r & 0x00FF_FFFF;
70+
}

cortex-ar/src/critical_section.rs

+137-20
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,147 @@
1-
//! Code that implements the `critical-section` traits on Cortex-R or Cortex-A when only a single
2-
//! core is used.
1+
//! Code that implements the `critical-section` traits on Cortex-R or Cortex-A
32
//!
4-
//! Only valid if you have a single core.
3+
//! We have single-core and multi-core versions. Select with the
4+
//! `critical-section-single-core` and `critical-section-multi-core` features.
55
6-
use core::sync::atomic;
6+
#[cfg(feature = "critical-section-single-core")]
7+
mod single_core {
8+
struct SingleCoreCriticalSection;
79

8-
struct SingleCoreCriticalSection;
9-
critical_section::set_impl!(SingleCoreCriticalSection);
10+
critical_section::set_impl!(SingleCoreCriticalSection);
1011

11-
unsafe impl critical_section::Impl for SingleCoreCriticalSection {
12-
unsafe fn acquire() -> critical_section::RawRestoreState {
13-
// the i bit means "masked"
14-
let was_active = !crate::register::Cpsr::read().i();
15-
crate::interrupt::disable();
16-
atomic::compiler_fence(atomic::Ordering::SeqCst);
17-
was_active
12+
/// Indicates the critical section was entered with interrupts on
13+
pub const INT_ON: u8 = 0;
14+
15+
/// Indicates the critical section was entered with interrupts off
16+
pub const INT_OFF: u8 = 1;
17+
18+
#[cfg(feature = "critical-section-single-core")]
19+
unsafe impl critical_section::Impl for SingleCoreCriticalSection {
20+
unsafe fn acquire() -> critical_section::RawRestoreState {
21+
use core::sync::atomic;
22+
// the i bit means "masked"
23+
let was_active = !crate::register::Cpsr::read().i();
24+
crate::interrupt::disable();
25+
atomic::compiler_fence(atomic::Ordering::SeqCst);
26+
if was_active {
27+
INT_ON
28+
} else {
29+
INT_OFF
30+
}
31+
}
32+
33+
unsafe fn release(was_active: critical_section::RawRestoreState) {
34+
use core::sync::atomic;
35+
// Only re-enable interrupts if they were enabled before the critical section.
36+
if was_active == INT_ON {
37+
atomic::compiler_fence(atomic::Ordering::SeqCst);
38+
// Safety: This is OK because we're releasing a lock that was
39+
// entered with interrupts enabled
40+
unsafe {
41+
crate::interrupt::enable();
42+
}
43+
}
44+
}
1845
}
46+
}
47+
48+
#[cfg(feature = "critical-section-multi-core")]
49+
mod multi_core {
50+
struct MultiCoreCriticalSection;
51+
52+
critical_section::set_impl!(MultiCoreCriticalSection);
53+
54+
/// The default value for our spin-lock
55+
pub const UNLOCKED: u32 = 0xFFFF_FFFF;
56+
57+
/// Indicates the critical section was entered with interrupts on, and the spin-lock unlocked
58+
pub const INT_ON_UNLOCKED: u8 = 0;
59+
60+
/// Indicates the critical section was entered with interrupts off, and the spin-lock locked (by us)
61+
pub const INT_OFF_LOCKED: u8 = 1;
62+
63+
/// Indicates the critical section was entered with interrupts off, and the spin-lock unlocked
64+
pub const INT_OFF_UNLOCKED: u8 = 2;
65+
66+
pub static CORE_SPIN_LOCK: core::sync::atomic::AtomicU32 =
67+
core::sync::atomic::AtomicU32::new(UNLOCKED);
68+
unsafe impl critical_section::Impl for MultiCoreCriticalSection {
69+
unsafe fn acquire() -> critical_section::RawRestoreState {
70+
use core::sync::atomic;
71+
72+
// the i bit means "masked"
73+
let was_active = !crate::register::Cpsr::read().i();
74+
crate::interrupt::disable();
75+
76+
let core_id = crate::asm::core_id();
77+
78+
let locked_already = loop {
79+
match CORE_SPIN_LOCK.compare_exchange(
80+
UNLOCKED,
81+
core_id,
82+
atomic::Ordering::Acquire,
83+
atomic::Ordering::Relaxed,
84+
) {
85+
Ok(_) => {
86+
// we got the lock
87+
break false;
88+
}
89+
Err(n) if n == core_id => {
90+
// we already held the lock
91+
break true;
92+
}
93+
Err(_) => {
94+
// someone else holds the lock
95+
core::hint::spin_loop();
96+
}
97+
}
98+
};
99+
100+
atomic::compiler_fence(atomic::Ordering::SeqCst);
101+
102+
match (was_active, locked_already) {
103+
(true, true) => {
104+
panic!("Invalid CS state?!");
105+
}
106+
(true, false) => {
107+
// we need to turn interrupts on, and release the lock
108+
INT_ON_UNLOCKED
109+
}
110+
(false, false) => {
111+
// we need release the lock
112+
INT_OFF_UNLOCKED
113+
}
114+
(false, true) => {
115+
// we need to do nothing
116+
INT_OFF_LOCKED
117+
}
118+
}
119+
}
120+
121+
unsafe fn release(was_active: critical_section::RawRestoreState) {
122+
use core::sync::atomic;
19123

20-
unsafe fn release(was_active: critical_section::RawRestoreState) {
21-
// Only re-enable interrupts if they were enabled before the critical section.
22-
if was_active {
23124
atomic::compiler_fence(atomic::Ordering::SeqCst);
24-
// Safety: This is OK because we're releasing a lock that was
25-
// entered with interrupts enabled
26-
unsafe {
27-
crate::interrupt::enable();
125+
match was_active {
126+
INT_OFF_LOCKED => {
127+
// do nothing
128+
}
129+
INT_OFF_UNLOCKED => {
130+
// the spin-lock was unlocked before, so unlock it
131+
CORE_SPIN_LOCK.store(UNLOCKED, atomic::Ordering::Release);
132+
}
133+
INT_ON_UNLOCKED => {
134+
// the spin-lock was unlocked before, so unlock it
135+
CORE_SPIN_LOCK.store(UNLOCKED, atomic::Ordering::Release);
136+
// Safety: This is OK because we're releasing a lock that was
137+
// entered with interrupts enabled
138+
unsafe {
139+
crate::interrupt::enable();
140+
}
141+
}
142+
_ => {
143+
unreachable!()
144+
}
28145
}
29146
}
30147
}

cortex-ar/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
#![no_std]
44

5-
#[cfg(feature = "critical-section-single-core")]
65
mod critical_section;
76

87
pub mod asm;

cortex-r-rt/link.x

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/*
22
Basic Cortex-R linker script.
33

4-
You must supply a file called `memory.x` which defines the memory regions 'CODE' and 'DATA'.
4+
You must supply a file called `memory.x` which defines the memory regions
5+
'VECTORS', 'CODE' and 'DATA'.
56

67
The stack pointer(s) will be (near) the top of the DATA region by default.
78

@@ -10,13 +11,17 @@ Based upon the linker script from https://github.com/rust-embedded/cortex-m
1011

1112
INCLUDE memory.x
1213

13-
ENTRY(_vector_table);
14+
ENTRY(_start);
1415
EXTERN(_vector_table);
16+
EXTERN(_start);
1517

1618
SECTIONS {
17-
.text : {
19+
.vector_table ORIGIN(VECTORS) : {
1820
/* The vector table must come first */
1921
*(.vector_table)
22+
} > VECTORS
23+
24+
.text : {
2025
/* Now the rest of the code */
2126
*(.text .text*)
2227
} > CODE
@@ -76,6 +81,7 @@ remainder is our system mode stack.
7681
You must keep _stack_top and the stack sizes aligned to eight byte boundaries.
7782
*/
7883
PROVIDE(_stack_top = ORIGIN(DATA) + LENGTH(DATA));
84+
PROVIDE(_hyp_stack_size = 0x400);
7985
PROVIDE(_und_stack_size = 0x400);
8086
PROVIDE(_svc_stack_size = 0x400);
8187
PROVIDE(_abt_stack_size = 0x400);

0 commit comments

Comments
 (0)