aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/src/flash.rs29
-rw-r--r--embassy-rp/src/multicore.rs333
2 files changed, 218 insertions, 144 deletions
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs
index a972d5f69..f2137ebe1 100644
--- a/embassy-rp/src/flash.rs
+++ b/embassy-rp/src/flash.rs
@@ -6,6 +6,7 @@ use embedded_storage::nor_flash::{
6 ReadNorFlash, 6 ReadNorFlash,
7}; 7};
8 8
9use crate::pac;
9use crate::peripherals::FLASH; 10use crate::peripherals::FLASH;
10 11
11pub const FLASH_BASE: usize = 0x10000000; 12pub const FLASH_BASE: usize = 0x10000000;
@@ -28,6 +29,7 @@ pub enum Error {
28 OutOfBounds, 29 OutOfBounds,
29 /// Unaligned operation or using unaligned buffers. 30 /// Unaligned operation or using unaligned buffers.
30 Unaligned, 31 Unaligned,
32 InvalidCore,
31 Other, 33 Other,
32} 34}
33 35
@@ -46,7 +48,7 @@ impl NorFlashError for Error {
46 match self { 48 match self {
47 Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, 49 Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,
48 Self::Unaligned => NorFlashErrorKind::NotAligned, 50 Self::Unaligned => NorFlashErrorKind::NotAligned,
49 Self::Other => NorFlashErrorKind::Other, 51 _ => NorFlashErrorKind::Other,
50 } 52 }
51 } 53 }
52} 54}
@@ -87,7 +89,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
87 89
88 let len = to - from; 90 let len = to - from;
89 91
90 unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) }; 92 unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true))? };
91 93
92 Ok(()) 94 Ok(())
93 } 95 }
@@ -112,7 +114,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
112 114
113 let unaligned_offset = offset as usize - start; 115 let unaligned_offset = offset as usize - start;
114 116
115 unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } 117 unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? }
116 } 118 }
117 119
118 let remaining_len = bytes.len() - start_padding; 120 let remaining_len = bytes.len() - start_padding;
@@ -130,12 +132,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
130 if bytes.as_ptr() as usize >= 0x2000_0000 { 132 if bytes.as_ptr() as usize >= 0x2000_0000 {
131 let aligned_data = &bytes[start_padding..end_padding]; 133 let aligned_data = &bytes[start_padding..end_padding];
132 134
133 unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } 135 unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true))? }
134 } else { 136 } else {
135 for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) { 137 for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {
136 let mut ram_buf = [0xFF_u8; PAGE_SIZE]; 138 let mut ram_buf = [0xFF_u8; PAGE_SIZE];
137 ram_buf.copy_from_slice(chunk); 139 ram_buf.copy_from_slice(chunk);
138 unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true)) } 140 unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true))? }
139 aligned_offset += PAGE_SIZE; 141 aligned_offset += PAGE_SIZE;
140 } 142 }
141 } 143 }
@@ -150,7 +152,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
150 152
151 let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset); 153 let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);
152 154
153 unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } 155 unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? }
154 } 156 }
155 157
156 Ok(()) 158 Ok(())
@@ -159,10 +161,17 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
159 /// Make sure to uphold the contract points with rp2040-flash. 161 /// Make sure to uphold the contract points with rp2040-flash.
160 /// - interrupts must be disabled 162 /// - interrupts must be disabled
161 /// - DMA must not access flash memory 163 /// - DMA must not access flash memory
162 unsafe fn in_ram(&mut self, operation: impl FnOnce()) { 164 unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> {
163 let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; 165 let dma_status = &mut [false; crate::dma::CHANNEL_COUNT];
164 166
165 // TODO: Make sure CORE1 is paused during the entire duration of the RAM function 167 // Make sure we're running on CORE0
168 let core_id: u32 = unsafe { pac::SIO.cpuid().read() };
169 if core_id != 0 {
170 return Err(Error::InvalidCore);
171 }
172
173 // Make sure CORE1 is paused during the entire duration of the RAM function
174 crate::multicore::pause_core1();
166 175
167 critical_section::with(|_| { 176 critical_section::with(|_| {
168 // Pause all DMA channels for the duration of the ram operation 177 // Pause all DMA channels for the duration of the ram operation
@@ -185,6 +194,10 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
185 } 194 }
186 } 195 }
187 }); 196 });
197
198 // Resume CORE1 execution
199 crate::multicore::resume_core1();
200 Ok(())
188 } 201 }
189} 202}
190 203
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}