aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/multicore.rs
diff options
context:
space:
mode:
authorkalkyl <[email protected]>2022-12-13 04:02:28 +0100
committerkalkyl <[email protected]>2022-12-13 04:02:28 +0100
commiteb1d2e1295cfd2f0580355d69c93387898eaabd4 (patch)
treee4eaee5da40fcb420d49d17c481fa985f2a6e8d3 /embassy-rp/src/multicore.rs
parent96d6c7243b7b5f7f8c90dab666ded0ca0cf29c75 (diff)
Pause CORE1 execution during flash operations
Diffstat (limited to 'embassy-rp/src/multicore.rs')
-rw-r--r--embassy-rp/src/multicore.rs333
1 files changed, 197 insertions, 136 deletions
diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs
index bf635db2d..09d40b2ef 100644
--- a/embassy-rp/src/multicore.rs
+++ b/embassy-rp/src/multicore.rs
@@ -11,14 +11,19 @@
11use core::mem::ManuallyDrop; 11use core::mem::ManuallyDrop;
12use core::sync::atomic::{compiler_fence, Ordering}; 12use core::sync::atomic::{compiler_fence, Ordering};
13 13
14use crate::pac; 14use atomic_polyfill::AtomicBool;
15
16use crate::interrupt::{Interrupt, InterruptExt};
17use crate::{interrupt, pac};
18
19const PAUSE_TOKEN: u32 = 0xDEADBEEF;
20const RESUME_TOKEN: u32 = !0xDEADBEEF;
21static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false);
15 22
16/// Errors for multicore operations. 23/// Errors for multicore operations.
17#[derive(Debug)] 24#[derive(Debug)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))] 25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub enum Error { 26pub enum Error {
20 /// Operation is invalid on this core.
21 InvalidCore,
22 /// Core was unresponsive to commands. 27 /// Core was unresponsive to commands.
23 Unresponsive, 28 Unresponsive,
24} 29}
@@ -64,7 +69,7 @@ fn core1_setup(stack_bottom: *mut usize) {
64 69
65/// MultiCore execution management. 70/// MultiCore execution management.
66pub struct MultiCore { 71pub struct MultiCore {
67 pub cores: (Core, Core), 72 pub cores: (Core0, Core1),
68} 73}
69 74
70/// Data type for a properly aligned stack of N 32-bit (usize) words 75/// Data type for a properly aligned stack of N 32-bit (usize) words
@@ -85,169 +90,225 @@ impl MultiCore {
85 /// Create a new |MultiCore| instance. 90 /// Create a new |MultiCore| instance.
86 pub fn new() -> Self { 91 pub fn new() -> Self {
87 Self { 92 Self {
88 cores: (Core { id: CoreId::Core0 }, Core { id: CoreId::Core1 }), 93 cores: (Core0 {}, Core1 {}),
89 } 94 }
90 } 95 }
91 96
92 /// Get the available |Core| instances. 97 /// Get the available |Core| instances.
93 pub fn cores(&mut self) -> &mut (Core, Core) { 98 pub fn cores(&mut self) -> &mut (Core0, Core1) {
94 &mut self.cores 99 &mut self.cores
95 } 100 }
96} 101}
97 102
98/// A handle for controlling a logical core. 103/// A handle for controlling a logical core.
99pub struct Core { 104pub struct Core0 {}
100 pub id: CoreId, 105/// A handle for controlling a logical core.
106pub struct Core1 {}
107
108#[interrupt]
109#[link_section = ".data.ram_func"]
110unsafe fn SIO_IRQ_PROC1() {
111 let sio = pac::SIO;
112 // Clear IRQ
113 sio.fifo().st().write(|w| w.set_wof(false));
114
115 while sio.fifo().st().read().vld() {
116 // Pause CORE1 execution and disable interrupts
117 if fifo_read_wfe() == PAUSE_TOKEN {
118 cortex_m::interrupt::disable();
119 // Signal to CORE0 that execution is paused
120 fifo_write(PAUSE_TOKEN);
121 // Wait for `resume` signal from CORE0
122 while fifo_read_wfe() != RESUME_TOKEN {
123 cortex_m::asm::nop();
124 }
125 cortex_m::interrupt::enable();
126 // Signal to CORE0 that execution is resumed
127 fifo_write(RESUME_TOKEN);
128 }
129 }
101} 130}
102 131
103impl Core { 132impl Core1 {
104 /// Spawn a function on this core. 133 /// Spawn a function on this core
105 pub fn spawn<F>(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> 134 pub fn spawn<F>(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error>
106 where 135 where
107 F: FnOnce() -> bad::Never + Send + 'static, 136 F: FnOnce() -> bad::Never + Send + 'static,
108 { 137 {
109 fn fifo_write(value: u32) { 138 // The first two ignored `u64` parameters are there to take up all of the registers,
110 unsafe { 139 // which means that the rest of the arguments are taken from the stack,
111 let sio = pac::SIO; 140 // where we're able to put them from core 0.
112 // Wait for the FIFO to have some space 141 extern "C" fn core1_startup<F: FnOnce() -> bad::Never>(
113 while !sio.fifo().st().read().rdy() { 142 _: u64,
114 cortex_m::asm::nop(); 143 _: u64,
115 } 144 entry: &mut ManuallyDrop<F>,
116 // Signal that it's safe for core 0 to get rid of the original value now. 145 stack_bottom: *mut usize,
117 sio.fifo().wr().write_value(value); 146 ) -> ! {
118 } 147 core1_setup(stack_bottom);
119 148 let entry = unsafe { ManuallyDrop::take(entry) };
120 // Fire off an event to the other core. 149 // Signal that it's safe for core 0 to get rid of the original value now.
121 // This is required as the other core may be `wfe` (waiting for event) 150 fifo_write(1);
122 cortex_m::asm::sev(); 151
152 IS_CORE1_INIT.store(true, Ordering::Release);
153 // Enable fifo interrupt on CORE1 for `pause` functionality.
154 let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() };
155 irq.enable();
156
157 entry()
123 } 158 }
124 159
125 fn fifo_read() -> u32 { 160 // Reset the core
126 unsafe { 161 unsafe {
127 let sio = pac::SIO; 162 let psm = pac::PSM;
128 // Keep trying until FIFO has data 163 psm.frce_off().modify(|w| w.set_proc1(true));
129 loop { 164 while !psm.frce_off().read().proc1() {
130 if sio.fifo().st().read().vld() { 165 cortex_m::asm::nop();
131 return sio.fifo().rd().read();
132 } else {
133 // We expect the sending core to `sev` on write.
134 cortex_m::asm::wfe();
135 }
136 }
137 } 166 }
167 psm.frce_off().modify(|w| w.set_proc1(false));
138 } 168 }
139 169
140 fn fifo_drain() { 170 // Set up the stack
141 unsafe { 171 let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) };
142 let sio = pac::SIO; 172
143 while sio.fifo().st().read().vld() { 173 // We don't want to drop this, since it's getting moved to the other core.
144 let _ = sio.fifo().rd().read(); 174 let mut entry = ManuallyDrop::new(entry);
145 } 175
146 } 176 // Push the arguments to `core1_startup` onto the stack.
177 unsafe {
178 // Push `stack_bottom`.
179 stack_ptr = stack_ptr.sub(1);
180 stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr());
181
182 // Push `entry`.
183 stack_ptr = stack_ptr.sub(1);
184 stack_ptr.cast::<&mut ManuallyDrop<F>>().write(&mut entry);
147 } 185 }
148 186
149 match self.id { 187 // Make sure the compiler does not reorder the stack writes after to after the
150 CoreId::Core1 => { 188 // below FIFO writes, which would result in them not being seen by the second
151 // The first two ignored `u64` parameters are there to take up all of the registers, 189 // core.
152 // which means that the rest of the arguments are taken from the stack, 190 //
153 // where we're able to put them from core 0. 191 // From the compiler perspective, this doesn't guarantee that the second core
154 extern "C" fn core1_startup<F: FnOnce() -> bad::Never>( 192 // actually sees those writes. However, we know that the RP2040 doesn't have
155 _: u64, 193 // memory caches, and writes happen in-order.
156 _: u64, 194 compiler_fence(Ordering::Release);
157 entry: &mut ManuallyDrop<F>, 195
158 stack_bottom: *mut usize, 196 let p = unsafe { cortex_m::Peripherals::steal() };
159 ) -> ! { 197 let vector_table = p.SCB.vtor.read();
160 core1_setup(stack_bottom); 198
161 let entry = unsafe { ManuallyDrop::take(entry) }; 199 // After reset, core 1 is waiting to receive commands over FIFO.
162 // Signal that it's safe for core 0 to get rid of the original value now. 200 // This is the sequence to have it jump to some code.
163 fifo_write(1); 201 let cmd_seq = [
164 entry() 202 0,
203 0,
204 1,
205 vector_table as usize,
206 stack_ptr as usize,
207 core1_startup::<F> as usize,
208 ];
209
210 let mut seq = 0;
211 let mut fails = 0;
212 loop {
213 let cmd = cmd_seq[seq] as u32;
214 if cmd == 0 {
215 fifo_drain();
216 cortex_m::asm::sev();
217 }
218 fifo_write(cmd);
219
220 let response = fifo_read();
221 if cmd == response {
222 seq += 1;
223 } else {
224 seq = 0;
225 fails += 1;
226 if fails > 16 {
227 // The second core isn't responding, and isn't going to take the entrypoint,
228 // so we have to drop it ourselves.
229 drop(ManuallyDrop::into_inner(entry));
230 return Err(Error::Unresponsive);
165 } 231 }
232 }
233 if seq >= cmd_seq.len() {
234 break;
235 }
236 }
166 237
167 // Reset the core 238 // Wait until the other core has copied `entry` before returning.
168 unsafe { 239 fifo_read();
169 let psm = pac::PSM;
170 psm.frce_off().modify(|w| w.set_proc1(true));
171 while !psm.frce_off().read().proc1() {
172 cortex_m::asm::nop();
173 }
174 psm.frce_off().modify(|w| w.set_proc1(false));
175 }
176 240
177 // Set up the stack 241 Ok(())
178 let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; 242 }
243}
179 244
180 // We don't want to drop this, since it's getting moved to the other core. 245/// Pause execution on CORE1.
181 let mut entry = ManuallyDrop::new(entry); 246pub fn pause_core1() {
247 if IS_CORE1_INIT.load(Ordering::Acquire) {
248 fifo_write(PAUSE_TOKEN);
249 // Wait for CORE1 to signal it has paused execution.
250 while fifo_read() != PAUSE_TOKEN {}
251 }
252}
182 253
183 // Push the arguments to `core1_startup` onto the stack. 254/// Resume CORE1 execution.
184 unsafe { 255pub fn resume_core1() {
185 // Push `stack_bottom`. 256 if IS_CORE1_INIT.load(Ordering::Acquire) {
186 stack_ptr = stack_ptr.sub(1); 257 fifo_write(RESUME_TOKEN);
187 stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); 258 // Wait for CORE1 to signal it has resumed execution.
259 while fifo_read() != RESUME_TOKEN {}
260 }
261}
188 262
189 // Push `entry`. 263// Push a value to the inter-core FIFO, block until space is available
190 stack_ptr = stack_ptr.sub(1); 264#[inline(always)]
191 stack_ptr.cast::<&mut ManuallyDrop<F>>().write(&mut entry); 265fn fifo_write(value: u32) {
192 } 266 unsafe {
267 let sio = pac::SIO;
268 // Wait for the FIFO to have enough space
269 while !sio.fifo().st().read().rdy() {
270 cortex_m::asm::nop();
271 }
272 sio.fifo().wr().write_value(value);
273 }
274 // Fire off an event to the other core.
275 // This is required as the other core may be `wfe` (waiting for event)
276 cortex_m::asm::sev();
277}
193 278
194 // Make sure the compiler does not reorder the stack writes after to after the 279// Pop a value from inter-core FIFO, block until available
195 // below FIFO writes, which would result in them not being seen by the second 280#[inline(always)]
196 // core. 281fn fifo_read() -> u32 {
197 // 282 unsafe {
198 // From the compiler perspective, this doesn't guarantee that the second core 283 let sio = pac::SIO;
199 // actually sees those writes. However, we know that the RP2040 doesn't have 284 // Wait until FIFO has data
200 // memory caches, and writes happen in-order. 285 while !sio.fifo().st().read().vld() {
201 compiler_fence(Ordering::Release); 286 cortex_m::asm::nop();
202 287 }
203 let p = unsafe { cortex_m::Peripherals::steal() }; 288 sio.fifo().rd().read()
204 let vector_table = p.SCB.vtor.read(); 289 }
205 290}
206 // After reset, core 1 is waiting to receive commands over FIFO.
207 // This is the sequence to have it jump to some code.
208 let cmd_seq = [
209 0,
210 0,
211 1,
212 vector_table as usize,
213 stack_ptr as usize,
214 core1_startup::<F> as usize,
215 ];
216
217 let mut seq = 0;
218 let mut fails = 0;
219 loop {
220 let cmd = cmd_seq[seq] as u32;
221 if cmd == 0 {
222 fifo_drain();
223 cortex_m::asm::sev();
224 }
225 fifo_write(cmd);
226
227 let response = fifo_read();
228 if cmd == response {
229 seq += 1;
230 } else {
231 seq = 0;
232 fails += 1;
233 if fails > 16 {
234 // The second core isn't responding, and isn't going to take the entrypoint,
235 // so we have to drop it ourselves.
236 drop(ManuallyDrop::into_inner(entry));
237 return Err(Error::Unresponsive);
238 }
239 }
240 if seq >= cmd_seq.len() {
241 break;
242 }
243 }
244 291
245 // Wait until the other core has copied `entry` before returning. 292// Pop a value from inter-core FIFO, `wfe` until available
246 fifo_read(); 293#[inline(always)]
294fn fifo_read_wfe() -> u32 {
295 unsafe {
296 let sio = pac::SIO;
297 // Wait until FIFO has data
298 while !sio.fifo().st().read().vld() {
299 cortex_m::asm::wfe();
300 }
301 sio.fifo().rd().read()
302 }
303}
247 304
248 Ok(()) 305// Drain inter-core FIFO
249 } 306#[inline(always)]
250 _ => Err(Error::InvalidCore), 307fn fifo_drain() {
308 unsafe {
309 let sio = pac::SIO;
310 while sio.fifo().st().read().vld() {
311 let _ = sio.fifo().rd().read();
251 } 312 }
252 } 313 }
253} 314}