aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/src/lib.rs2
-rw-r--r--embassy-rp/src/multicore.rs237
-rw-r--r--examples/rp/src/bin/multicore.rs5
-rw-r--r--tests/rp/src/bin/multicore.rs47
4 files changed, 152 insertions, 139 deletions
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 2cd99f456..c6442c56e 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -106,6 +106,8 @@ embassy_hal_common::peripherals! {
106 FLASH, 106 FLASH,
107 107
108 ADC, 108 ADC,
109
110 CORE1,
109} 111}
110 112
111#[link_section = ".boot2"] 113#[link_section = ".boot2"]
diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs
index 3703fe819..2dfa215b5 100644
--- a/embassy-rp/src/multicore.rs
+++ b/embassy-rp/src/multicore.rs
@@ -36,20 +36,13 @@ use core::sync::atomic::{compiler_fence, Ordering};
36use atomic_polyfill::AtomicBool; 36use atomic_polyfill::AtomicBool;
37 37
38use crate::interrupt::{Interrupt, InterruptExt}; 38use crate::interrupt::{Interrupt, InterruptExt};
39use crate::peripherals::CORE1;
39use crate::{interrupt, pac}; 40use crate::{interrupt, pac};
40 41
41const PAUSE_TOKEN: u32 = 0xDEADBEEF; 42const PAUSE_TOKEN: u32 = 0xDEADBEEF;
42const RESUME_TOKEN: u32 = !0xDEADBEEF; 43const RESUME_TOKEN: u32 = !0xDEADBEEF;
43static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); 44static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false);
44 45
45/// Errors for multicore operations.
46#[derive(Debug)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub enum Error {
49 /// Core was unresponsive to commands.
50 Unresponsive,
51}
52
53#[inline(always)] 46#[inline(always)]
54fn install_stack_guard(stack_bottom: *mut usize) { 47fn install_stack_guard(stack_bottom: *mut usize) {
55 let core = unsafe { cortex_m::Peripherals::steal() }; 48 let core = unsafe { cortex_m::Peripherals::steal() };
@@ -81,44 +74,20 @@ fn core1_setup(stack_bottom: *mut usize) {
81 install_stack_guard(stack_bottom); 74 install_stack_guard(stack_bottom);
82} 75}
83 76
84/// MultiCore execution management. 77/// Data type for a properly aligned stack of N bytes
85pub struct MultiCore {
86 pub cores: (Core0, Core1),
87}
88
89/// Data type for a properly aligned stack of N 32-bit (usize) words
90#[repr(C, align(32))] 78#[repr(C, align(32))]
91pub struct Stack<const SIZE: usize> { 79pub struct Stack<const SIZE: usize> {
92 /// Memory to be used for the stack 80 /// Memory to be used for the stack
93 pub mem: [usize; SIZE], 81 pub mem: [u8; SIZE],
94} 82}
95 83
96impl<const SIZE: usize> Stack<SIZE> { 84impl<const SIZE: usize> Stack<SIZE> {
97 /// Construct a stack of length SIZE, initialized to 0 85 /// Construct a stack of length SIZE, initialized to 0
98 pub const fn new() -> Stack<SIZE> { 86 pub const fn new() -> Stack<SIZE> {
99 Stack { mem: [0; SIZE] } 87 Stack { mem: [0_u8; SIZE] }
100 } 88 }
101} 89}
102 90
103impl MultiCore {
104 /// Create a new |MultiCore| instance.
105 pub fn new() -> Self {
106 Self {
107 cores: (Core0 {}, Core1 {}),
108 }
109 }
110
111 /// Get the available |Core| instances.
112 pub fn cores(&mut self) -> &mut (Core0, Core1) {
113 &mut self.cores
114 }
115}
116
117/// A handle for controlling a logical core.
118pub struct Core0 {}
119/// A handle for controlling a logical core.
120pub struct Core1 {}
121
122#[interrupt] 91#[interrupt]
123#[link_section = ".data.ram_func"] 92#[link_section = ".data.ram_func"]
124unsafe fn SIO_IRQ_PROC1() { 93unsafe fn SIO_IRQ_PROC1() {
@@ -143,117 +112,113 @@ unsafe fn SIO_IRQ_PROC1() {
143 } 112 }
144} 113}
145 114
146impl Core1 { 115/// Spawn a function on this core
147 /// Spawn a function on this core 116pub fn spawn_core1<F, const SIZE: usize>(_core1: CORE1, stack: &'static mut Stack<SIZE>, entry: F)
148 pub fn spawn<F>(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> 117where
149 where 118 F: FnOnce() -> bad::Never + Send + 'static,
150 F: FnOnce() -> bad::Never + Send + 'static, 119{
151 { 120 // The first two ignored `u64` parameters are there to take up all of the registers,
152 // The first two ignored `u64` parameters are there to take up all of the registers, 121 // which means that the rest of the arguments are taken from the stack,
153 // which means that the rest of the arguments are taken from the stack, 122 // where we're able to put them from core 0.
154 // where we're able to put them from core 0. 123 extern "C" fn core1_startup<F: FnOnce() -> bad::Never>(
155 extern "C" fn core1_startup<F: FnOnce() -> bad::Never>( 124 _: u64,
156 _: u64, 125 _: u64,
157 _: u64, 126 entry: &mut ManuallyDrop<F>,
158 entry: &mut ManuallyDrop<F>, 127 stack_bottom: *mut usize,
159 stack_bottom: *mut usize, 128 ) -> ! {
160 ) -> ! { 129 core1_setup(stack_bottom);
161 core1_setup(stack_bottom); 130 let entry = unsafe { ManuallyDrop::take(entry) };
162 let entry = unsafe { ManuallyDrop::take(entry) }; 131 // Signal that it's safe for core 0 to get rid of the original value now.
163 // Signal that it's safe for core 0 to get rid of the original value now. 132 fifo_write(1);
164 fifo_write(1); 133
165 134 IS_CORE1_INIT.store(true, Ordering::Release);
166 IS_CORE1_INIT.store(true, Ordering::Release); 135 // Enable fifo interrupt on CORE1 for `pause` functionality.
167 // Enable fifo interrupt on CORE1 for `pause` functionality. 136 let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() };
168 let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() }; 137 irq.enable();
169 irq.enable(); 138
170 139 entry()
171 entry() 140 }
172 }
173 141
174 // Reset the core 142 // Reset the core
175 unsafe { 143 unsafe {
176 let psm = pac::PSM; 144 let psm = pac::PSM;
177 psm.frce_off().modify(|w| w.set_proc1(true)); 145 psm.frce_off().modify(|w| w.set_proc1(true));
178 while !psm.frce_off().read().proc1() { 146 while !psm.frce_off().read().proc1() {
179 cortex_m::asm::nop(); 147 cortex_m::asm::nop();
180 }
181 psm.frce_off().modify(|w| w.set_proc1(false));
182 } 148 }
149 psm.frce_off().modify(|w| w.set_proc1(false));
150 }
183 151
184 // Set up the stack 152 let mem = unsafe { core::slice::from_raw_parts_mut(stack.mem.as_mut_ptr() as *mut usize, stack.mem.len() / 4) };
185 let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) };
186 153
187 // We don't want to drop this, since it's getting moved to the other core. 154 // Set up the stack
188 let mut entry = ManuallyDrop::new(entry); 155 let mut stack_ptr = unsafe { mem.as_mut_ptr().add(mem.len()) };
189 156
190 // Push the arguments to `core1_startup` onto the stack. 157 // We don't want to drop this, since it's getting moved to the other core.
191 unsafe { 158 let mut entry = ManuallyDrop::new(entry);
192 // Push `stack_bottom`.
193 stack_ptr = stack_ptr.sub(1);
194 stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr());
195 159
196 // Push `entry`. 160 // Push the arguments to `core1_startup` onto the stack.
197 stack_ptr = stack_ptr.sub(1); 161 unsafe {
198 stack_ptr.cast::<&mut ManuallyDrop<F>>().write(&mut entry); 162 // Push `stack_bottom`.
199 } 163 stack_ptr = stack_ptr.sub(1);
164 stack_ptr.cast::<*mut usize>().write(mem.as_mut_ptr());
200 165
201 // Make sure the compiler does not reorder the stack writes after to after the 166 // Push `entry`.
202 // below FIFO writes, which would result in them not being seen by the second 167 stack_ptr = stack_ptr.sub(1);
203 // core. 168 stack_ptr.cast::<&mut ManuallyDrop<F>>().write(&mut entry);
204 // 169 }
205 // From the compiler perspective, this doesn't guarantee that the second core 170
206 // actually sees those writes. However, we know that the RP2040 doesn't have 171 // Make sure the compiler does not reorder the stack writes after to after the
207 // memory caches, and writes happen in-order. 172 // below FIFO writes, which would result in them not being seen by the second
208 compiler_fence(Ordering::Release); 173 // core.
209 174 //
210 let p = unsafe { cortex_m::Peripherals::steal() }; 175 // From the compiler perspective, this doesn't guarantee that the second core
211 let vector_table = p.SCB.vtor.read(); 176 // actually sees those writes. However, we know that the RP2040 doesn't have
212 177 // memory caches, and writes happen in-order.
213 // After reset, core 1 is waiting to receive commands over FIFO. 178 compiler_fence(Ordering::Release);
214 // This is the sequence to have it jump to some code. 179
215 let cmd_seq = [ 180 let p = unsafe { cortex_m::Peripherals::steal() };
216 0, 181 let vector_table = p.SCB.vtor.read();
217 0, 182
218 1, 183 // After reset, core 1 is waiting to receive commands over FIFO.
219 vector_table as usize, 184 // This is the sequence to have it jump to some code.
220 stack_ptr as usize, 185 let cmd_seq = [
221 core1_startup::<F> as usize, 186 0,
222 ]; 187 0,
223 188 1,
224 let mut seq = 0; 189 vector_table as usize,
225 let mut fails = 0; 190 stack_ptr as usize,
226 loop { 191 core1_startup::<F> as usize,
227 let cmd = cmd_seq[seq] as u32; 192 ];
228 if cmd == 0 { 193
229 fifo_drain(); 194 let mut seq = 0;
230 cortex_m::asm::sev(); 195 let mut fails = 0;
231 } 196 loop {
232 fifo_write(cmd); 197 let cmd = cmd_seq[seq] as u32;
233 198 if cmd == 0 {
234 let response = fifo_read(); 199 fifo_drain();
235 if cmd == response { 200 cortex_m::asm::sev();
236 seq += 1; 201 }
237 } else { 202 fifo_write(cmd);
238 seq = 0; 203
239 fails += 1; 204 let response = fifo_read();
240 if fails > 16 { 205 if cmd == response {
241 // The second core isn't responding, and isn't going to take the entrypoint, 206 seq += 1;
242 // so we have to drop it ourselves. 207 } else {
243 drop(ManuallyDrop::into_inner(entry)); 208 seq = 0;
244 return Err(Error::Unresponsive); 209 fails += 1;
245 } 210 if fails > 16 {
246 } 211 // The second core isn't responding, and isn't going to take the entrypoint
247 if seq >= cmd_seq.len() { 212 panic!("CORE1 not responding");
248 break;
249 } 213 }
250 } 214 }
251 215 if seq >= cmd_seq.len() {
252 // Wait until the other core has copied `entry` before returning. 216 break;
253 fifo_read(); 217 }
254
255 Ok(())
256 } 218 }
219
220 // Wait until the other core has copied `entry` before returning.
221 fifo_read();
257} 222}
258 223
259/// Pause execution on CORE1. 224/// Pause execution on CORE1.
diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs
index 53941da60..376b2b61e 100644
--- a/examples/rp/src/bin/multicore.rs
+++ b/examples/rp/src/bin/multicore.rs
@@ -6,7 +6,7 @@ use defmt::*;
6use embassy_executor::Executor; 6use embassy_executor::Executor;
7use embassy_executor::_export::StaticCell; 7use embassy_executor::_export::StaticCell;
8use embassy_rp::gpio::{Level, Output}; 8use embassy_rp::gpio::{Level, Output};
9use embassy_rp::multicore::{MultiCore, Stack}; 9use embassy_rp::multicore::{spawn_core1, Stack};
10use embassy_rp::peripherals::PIN_25; 10use embassy_rp::peripherals::PIN_25;
11use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 11use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
12use embassy_sync::channel::Channel; 12use embassy_sync::channel::Channel;
@@ -28,8 +28,7 @@ fn main() -> ! {
28 let p = embassy_rp::init(Default::default()); 28 let p = embassy_rp::init(Default::default());
29 let led = Output::new(p.PIN_25, Level::Low); 29 let led = Output::new(p.PIN_25, Level::Low);
30 30
31 let mut mc = MultiCore::new(); 31 spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
32 let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || {
33 let executor1 = EXECUTOR1.init(Executor::new()); 32 let executor1 = EXECUTOR1.init(Executor::new());
34 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); 33 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led))));
35 }); 34 });
diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs
new file mode 100644
index 000000000..da78e887a
--- /dev/null
+++ b/tests/rp/src/bin/multicore.rs
@@ -0,0 +1,47 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy_executor::Executor;
7use embassy_executor::_export::StaticCell;
8use embassy_rp::multicore::{spawn_core1, Stack};
9use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
10use embassy_sync::channel::Channel;
11use {defmt_rtt as _, panic_probe as _};
12
13static mut CORE1_STACK: Stack<1024> = Stack::new();
14static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
15static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
16static CHANNEL0: Channel<CriticalSectionRawMutex, bool, 1> = Channel::new();
17static CHANNEL1: Channel<CriticalSectionRawMutex, bool, 1> = Channel::new();
18
19#[cortex_m_rt::entry]
20fn main() -> ! {
21 let p = embassy_rp::init(Default::default());
22 spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
23 let executor1 = EXECUTOR1.init(Executor::new());
24 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task())));
25 });
26 let executor0 = EXECUTOR0.init(Executor::new());
27 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
28}
29
30#[embassy_executor::task]
31async fn core0_task() {
32 info!("CORE0 is running");
33 let ping = true;
34 CHANNEL0.send(ping).await;
35 let pong = CHANNEL1.recv().await;
36 assert_eq!(ping, pong);
37
38 info!("Test OK");
39 cortex_m::asm::bkpt();
40}
41
42#[embassy_executor::task]
43async fn core1_task() {
44 info!("CORE1 is running");
45 let ping = CHANNEL0.recv().await;
46 CHANNEL1.send(ping).await;
47}