From f0b46c0f58e5dece6963b753b098751de5046d67 Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Sat, 5 Mar 2022 14:55:32 +1100 Subject: [PATCH 01/10] Remove the alloc requirement for `Core::spawn` --- rp2040-hal/Cargo.toml | 1 - rp2040-hal/examples/multicore_fifo_blink.rs | 13 ++-- rp2040-hal/src/multicore.rs | 80 +++++++++++++-------- 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index 8822de4..d4333b3 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -52,7 +52,6 @@ rt = ["rp2040-pac/rt"] # This is commented out so that we can publish to crates.io # # embassy-traits = ["embassy_traits", "futures"] -alloc = [] rom-func-cache = [] disable-intrinsics = [] rom-v2-intrinsics = [] diff --git a/rp2040-hal/examples/multicore_fifo_blink.rs b/rp2040-hal/examples/multicore_fifo_blink.rs index 7b305a9..f6cae88 100644 --- a/rp2040-hal/examples/multicore_fifo_blink.rs +++ b/rp2040-hal/examples/multicore_fifo_blink.rs @@ -57,7 +57,7 @@ const CORE1_TASK_COMPLETE: u32 = 0xEE; /// the stack guard to take up the least amount of usable RAM. static mut CORE1_STACK: Stack<4096> = Stack::new(); -fn core1_task() -> ! { +fn core1_task(sys_freq: u32) -> ! { let mut pac = unsafe { pac::Peripherals::steal() }; let core = unsafe { pac::CorePeripherals::steal() }; @@ -70,10 +70,6 @@ fn core1_task() -> ! { ); let mut led_pin = pins.gpio25.into_push_pull_output(); - // The first thing core0 sends us is the system bus frequency. - // The systick is based on this frequency, so we need that to - // be accurate when sleeping via cortex_m::delay::Delay - let sys_freq = sio.fifo.read_blocking(); let mut delay = cortex_m::delay::Delay::new(core.SYST, sys_freq); loop { let input = sio.fifo.read(); @@ -114,17 +110,16 @@ fn main() -> ! { .ok() .unwrap(); + let sys_freq = clocks.system_clock.freq().integer(); + // The single-cycle I/O block controls our GPIO pins let mut sio = hal::sio::Sio::new(pac.SIO); let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio); let cores = mc.cores(); let core1 = &mut cores[1]; - let _test = core1.spawn(core1_task, unsafe { &mut CORE1_STACK.mem }); + let _test = core1.spawn(move || core1_task(sys_freq), unsafe { &mut CORE1_STACK.mem }); - // Let core1 know how fast the system clock is running - let sys_freq = clocks.system_clock.freq().integer(); - sio.fifo.write_blocking(sys_freq); /// How much we adjust the LED period every cycle const LED_PERIOD_INCREMENT: i32 = 2; diff --git a/rp2040-hal/src/multicore.rs b/rp2040-hal/src/multicore.rs index a16b46a..31f661f 100644 --- a/rp2040-hal/src/multicore.rs +++ b/rp2040-hal/src/multicore.rs @@ -33,10 +33,13 @@ //! //! For a detailed example, see [examples/multicore_fifo_blink.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/multicore_fifo_blink.rs) -use crate::pac; +use core::mem; +use core::mem::ManuallyDrop; -#[cfg(feature = "alloc")] -extern crate alloc; +use pac::Peripherals; + +use crate::pac; +use crate::Sio; /// Errors for multicore operations. #[derive(Debug)] @@ -52,7 +55,7 @@ pub enum Error { // rust can read here. Ideally this would be a // #[naked] function but that is not stable yet. static MULTICORE_TRAMPOLINE: [u16; 2] = [ - 0xbd03, // pop {r0, r1, pc} - call wrapper (pc) with r0 and r1 + 0xbd07, // pop {r0, r1, r2, pc} - call wrapper (pc) with r0, r1 and r2 0x46c0, // nop - pad this out to 32 bits long ]; @@ -143,7 +146,7 @@ impl<'p> Core<'p> { fn inner_spawn( &mut self, wrapper: *mut (), - entry: *mut (), + entry: u64, stack: &'static mut [usize], ) -> Result<(), Error> { if let Some((psm, ppb, sio)) = self.inner.as_mut() { @@ -164,6 +167,7 @@ impl<'p> Core<'p> { push(wrapper as usize); push(stack.as_mut_ptr() as usize); + push((entry >> 32) as usize); push(entry as usize); let vector_table = ppb.vtor.read().bits(); @@ -210,41 +214,59 @@ impl<'p> Core<'p> { } /// Spawn a function on this core. - #[cfg(not(feature = "alloc"))] - pub fn spawn(&mut self, entry: fn() -> !, stack: &'static mut [usize]) -> Result<(), Error> { - #[allow(improper_ctypes_definitions)] - extern "C" fn core1_no_alloc(entry: fn() -> !, stack_bottom: *mut usize) -> ! { - core1_setup(stack_bottom); - entry(); - } - - self.inner_spawn(core1_no_alloc as _, entry as _, stack) - } - - /// Spawn a function on this core. - #[cfg(feature = "alloc")] pub fn spawn(&mut self, entry: F, stack: &'static mut [usize]) -> Result<(), Error> where F: FnOnce() -> bad::Never, F: Send + 'static, { - use alloc::boxed::Box; - - let main: Box bad::Never> = Box::new(move || entry()); - let p = Box::into_raw(Box::new(main)); - - extern "C" fn core1_alloc(entry: *mut (), stack_bottom: *mut usize) -> ! { - core1_setup(stack_bottom); - let main = unsafe { Box::from_raw(entry as *mut Box bad::Never>) }; - main(); + // idea stolen from https://users.rust-lang.org/t/invoke-mut-dyn-fnonce/59356/4 + trait Core1Main { + /// # Safety + /// + /// Must only be called once. + unsafe fn run(&mut self) -> !; } - self.inner_spawn(core1_alloc as _, p as _, stack) + impl bad::Never> Core1Main for T { + unsafe fn run(&mut self) -> ! { + let f = (self as *mut Self).read(); + + // Signal that it's safe for the other core to get rid of the original value now + let peripherals = Peripherals::steal(); + let sio = Sio::new(peripherals.SIO); + let mut fifo = sio.fifo; + fifo.write_blocking(1); + + f() + } + } + + extern "C" fn core1(entry: u64, stack_bottom: *mut usize) -> ! { + core1_setup(stack_bottom); + let main: *mut dyn Core1Main = unsafe { mem::transmute(entry) }; + unsafe { (*main).run() } + } + + // We don't want to drop this, since it's getting moved to the other core. + let mut entry = ManuallyDrop::new(entry); + + let ptr = &mut *entry as &mut dyn Core1Main; + let ptr = unsafe { mem::transmute(ptr) }; + + self.inner_spawn(core1 as _, ptr, stack)?; + + // If `inner_spawn` succeeded, this must not have been `None`, + // so it's fine to unwrap it. + let (_, _, sio) = self.inner.as_mut().unwrap(); + + // Wait until the other core has copied `entry` before dropping it. + sio.fifo.read_blocking(); + + Ok(()) } } // https://github.com/nvzqz/bad-rs/blob/master/src/never.rs -#[cfg(feature = "alloc")] mod bad { pub(crate) type Never = ::Output; From 51cd52bb8c70e872134e9a11d0b057449f8b4448 Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Sat, 5 Mar 2022 18:53:11 +1100 Subject: [PATCH 02/10] fmt --- rp2040-hal/examples/multicore_fifo_blink.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rp2040-hal/examples/multicore_fifo_blink.rs b/rp2040-hal/examples/multicore_fifo_blink.rs index f6cae88..5b8af29 100644 --- a/rp2040-hal/examples/multicore_fifo_blink.rs +++ b/rp2040-hal/examples/multicore_fifo_blink.rs @@ -118,7 +118,9 @@ fn main() -> ! { let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio); let cores = mc.cores(); let core1 = &mut cores[1]; - let _test = core1.spawn(move || core1_task(sys_freq), unsafe { &mut CORE1_STACK.mem }); + let _test = core1.spawn(move || core1_task(sys_freq), unsafe { + &mut CORE1_STACK.mem + }); /// How much we adjust the LED period every cycle const LED_PERIOD_INCREMENT: i32 = 2; From 56aa96769d0f146d4d8bac4fab28ca9640ee832b Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Sat, 5 Mar 2022 21:57:35 +1100 Subject: [PATCH 03/10] Don't assume `usize` is 32 bits --- rp2040-hal/src/multicore.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rp2040-hal/src/multicore.rs b/rp2040-hal/src/multicore.rs index 31f661f..9400229 100644 --- a/rp2040-hal/src/multicore.rs +++ b/rp2040-hal/src/multicore.rs @@ -146,7 +146,7 @@ impl<'p> Core<'p> { fn inner_spawn( &mut self, wrapper: *mut (), - entry: u64, + entry: [usize; 2], stack: &'static mut [usize], ) -> Result<(), Error> { if let Some((psm, ppb, sio)) = self.inner.as_mut() { @@ -167,8 +167,8 @@ impl<'p> Core<'p> { push(wrapper as usize); push(stack.as_mut_ptr() as usize); - push((entry >> 32) as usize); - push(entry as usize); + push(entry[1]); + push(entry[0]); let vector_table = ppb.vtor.read().bits(); @@ -241,9 +241,9 @@ impl<'p> Core<'p> { } } - extern "C" fn core1(entry: u64, stack_bottom: *mut usize) -> ! { + extern "C" fn core1(entry0: usize, entry1: usize, stack_bottom: *mut usize) -> ! { core1_setup(stack_bottom); - let main: *mut dyn Core1Main = unsafe { mem::transmute(entry) }; + let main: *mut dyn Core1Main = unsafe { mem::transmute([entry0, entry1]) }; unsafe { (*main).run() } } From 8a261b050ca6ff7adba01c56e98a9d4b8b8823ac Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Wed, 20 Apr 2022 19:30:32 +1000 Subject: [PATCH 04/10] Remove the multicore trampoline and make the core 1 startup function generic I managed to avoid the multicore trampoline by messing with the signature of the core 1 startup function. While the first couple arguments to a function with the arm C abi are passed in registers, once they're filled up, the rest of the arguments go on the stack; so, I put some dummy arguments before the real arguments to force them to go onto the stack. That allows it to be used directly, without needing the trampoline to move the arguments from the stack to registers. I also changed the startup function to be generic over the function type passed, which avoids the mess of dealing with `Core1Main` and fat pointers and all that. --- rp2040-hal/src/multicore.rs | 112 ++++++++++++------------------------ 1 file changed, 38 insertions(+), 74 deletions(-) diff --git a/rp2040-hal/src/multicore.rs b/rp2040-hal/src/multicore.rs index 9400229..6c46848 100644 --- a/rp2040-hal/src/multicore.rs +++ b/rp2040-hal/src/multicore.rs @@ -33,11 +33,8 @@ //! //! For a detailed example, see [examples/multicore_fifo_blink.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/multicore_fifo_blink.rs) -use core::mem; use core::mem::ManuallyDrop; -use pac::Peripherals; - use crate::pac; use crate::Sio; @@ -50,15 +47,6 @@ pub enum Error { Unresponsive, } -// We pass data to cores via the stack, so we read -// the data off the stack and into parameters that -// rust can read here. Ideally this would be a -// #[naked] function but that is not stable yet. -static MULTICORE_TRAMPOLINE: [u16; 2] = [ - 0xbd07, // pop {r0, r1, r2, pc} - call wrapper (pc) with r0, r1 and r2 - 0x46c0, // nop - pad this out to 32 bits long -]; - #[inline(always)] fn install_stack_guard(stack_bottom: *mut usize) { let core = unsafe { pac::CorePeripherals::steal() }; @@ -143,13 +131,36 @@ impl<'p> Core<'p> { } } - fn inner_spawn( - &mut self, - wrapper: *mut (), - entry: [usize; 2], - stack: &'static mut [usize], - ) -> Result<(), Error> { + /// Spawn a function on this core. + pub fn spawn(&mut self, entry: F, stack: &'static mut [usize]) -> Result<(), Error> + where + F: FnOnce() -> bad::Never + Send + 'static, + { if let Some((psm, ppb, sio)) = self.inner.as_mut() { + // The first two ignored `u64` parameters are there to take up all of the registers, + // which means that the rest of the arguments are taken from the stack, + // where we're able to put them from core 0. + extern "C" fn core1_startup bad::Never>( + _: u64, + _: u64, + entry: &mut ManuallyDrop, + stack_bottom: *mut usize, + ) -> ! { + core1_setup(stack_bottom); + + let entry = unsafe { ManuallyDrop::take(entry) }; + + // Signal that it's safe for core 0 to get rid of the original value now. + // + // We don't have any way to get at core 1's SIO without using `Peripherals::steal` right now, + // since svd2rust doesn't really support multiple cores properly. + let peripherals = unsafe { pac::Peripherals::steal() }; + let mut sio = Sio::new(peripherals.SIO); + sio.fifo.write_blocking(1); + + entry() + } + // Reset the core psm.frce_off.modify(|_, w| w.proc1().set_bit()); while !psm.frce_off.read().proc1().bit_is_set() { @@ -165,10 +176,12 @@ impl<'p> Core<'p> { stack_ptr.write(v); }; - push(wrapper as usize); + // We don't want to drop this, since it's getting moved to the other core. + let mut entry = ManuallyDrop::new(entry); + + // Push the arguments to `core1_startup` onto the stack. push(stack.as_mut_ptr() as usize); - push(entry[1]); - push(entry[0]); + push(&mut entry as *mut _ as usize); let vector_table = ppb.vtor.read().bits(); @@ -180,7 +193,7 @@ impl<'p> Core<'p> { 1, vector_table as usize, stack_ptr as usize, - MULTICORE_TRAMPOLINE.as_ptr() as usize + 1, + core1_startup:: as usize, ]; let mut seq = 0; @@ -207,63 +220,14 @@ impl<'p> Core<'p> { } } + // Wait until the other core has copied `entry` before returning. + sio.fifo.read_blocking(); + Ok(()) } else { Err(Error::InvalidCore) } } - - /// Spawn a function on this core. - pub fn spawn(&mut self, entry: F, stack: &'static mut [usize]) -> Result<(), Error> - where - F: FnOnce() -> bad::Never, - F: Send + 'static, - { - // idea stolen from https://users.rust-lang.org/t/invoke-mut-dyn-fnonce/59356/4 - trait Core1Main { - /// # Safety - /// - /// Must only be called once. - unsafe fn run(&mut self) -> !; - } - - impl bad::Never> Core1Main for T { - unsafe fn run(&mut self) -> ! { - let f = (self as *mut Self).read(); - - // Signal that it's safe for the other core to get rid of the original value now - let peripherals = Peripherals::steal(); - let sio = Sio::new(peripherals.SIO); - let mut fifo = sio.fifo; - fifo.write_blocking(1); - - f() - } - } - - extern "C" fn core1(entry0: usize, entry1: usize, stack_bottom: *mut usize) -> ! { - core1_setup(stack_bottom); - let main: *mut dyn Core1Main = unsafe { mem::transmute([entry0, entry1]) }; - unsafe { (*main).run() } - } - - // We don't want to drop this, since it's getting moved to the other core. - let mut entry = ManuallyDrop::new(entry); - - let ptr = &mut *entry as &mut dyn Core1Main; - let ptr = unsafe { mem::transmute(ptr) }; - - self.inner_spawn(core1 as _, ptr, stack)?; - - // If `inner_spawn` succeeded, this must not have been `None`, - // so it's fine to unwrap it. - let (_, _, sio) = self.inner.as_mut().unwrap(); - - // Wait until the other core has copied `entry` before dropping it. - sio.fifo.read_blocking(); - - Ok(()) - } } // https://github.com/nvzqz/bad-rs/blob/master/src/never.rs From 67ceb65703d73d81c3a27945a49703534c1c25f2 Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Thu, 21 Apr 2022 14:38:36 +1000 Subject: [PATCH 05/10] Update documentation and drop entrypoint on error --- rp2040-hal/src/multicore.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/rp2040-hal/src/multicore.rs b/rp2040-hal/src/multicore.rs index 6c46848..a2d2048 100644 --- a/rp2040-hal/src/multicore.rs +++ b/rp2040-hal/src/multicore.rs @@ -3,20 +3,20 @@ //! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1. //! It provides functionality for setting up the stack, and starting core1. //! -//! The options for an entrypoint for core1 are -//! - a function that never returns - eg -//! `fn core1_task() -> ! { loop{} }; ` -//! - a lambda (note: This requires a global allocator which requires a nightly compiler. Not recommended for beginners) +//! The entrypoint for core1 can be any function that never returns, including closures. //! //! # Usage //! //! ```no_run +//! use rp2040_hal::{pac, gpio::Pins, sio::Sio, multicore::{Multicore, Stack}}; +//! //! static mut CORE1_STACK: Stack<4096> = Stack::new(); +//! //! fn core1_task() -> ! { -//! loop{} +//! loop {} //! } -//! // fn main() -> ! { -//! use rp2040_hal::{pac, gpio::Pins, sio::Sio, multicore::{Multicore, Stack}}; +//! +//! fn main() -> ! { //! let mut pac = pac::Peripherals::take().unwrap(); //! let mut sio = Sio::new(pac.SIO); //! // Other init code above this line @@ -25,7 +25,8 @@ //! let core1 = &mut cores[1]; //! let _test = core1.spawn(core1_task, unsafe { &mut CORE1_STACK.mem }); //! // The rest of your application below this line -//! //} +//! # loop {} +//! } //! //! ``` //! @@ -212,6 +213,9 @@ impl<'p> Core<'p> { seq = 0; fails += 1; if fails > 16 { + // The second core isn't responding, and isn't going to take the entrypoint, + // so we have to drop it ourselves. + drop(ManuallyDrop::into_inner(entry)); return Err(Error::Unresponsive); } } From c0fafc76940ea0a7470f9e24a09f8c987345224f Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Sun, 8 May 2022 18:24:49 +1000 Subject: [PATCH 06/10] Make `Multicore` take `SioFifo` rather than the whole `Sio` & make the `spawn` closure the last argument --- rp2040-hal/examples/multicore_fifo_blink.rs | 6 +++--- rp2040-hal/src/multicore.rs | 24 ++++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/rp2040-hal/examples/multicore_fifo_blink.rs b/rp2040-hal/examples/multicore_fifo_blink.rs index b9e636f..6826ee7 100644 --- a/rp2040-hal/examples/multicore_fifo_blink.rs +++ b/rp2040-hal/examples/multicore_fifo_blink.rs @@ -115,11 +115,11 @@ fn main() -> ! { // The single-cycle I/O block controls our GPIO pins let mut sio = hal::sio::Sio::new(pac.SIO); - let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio); + let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo); let cores = mc.cores(); let core1 = &mut cores[1]; - let _test = core1.spawn(move || core1_task(sys_freq), unsafe { - &mut CORE1_STACK.mem + let _test = core1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { + core1_task(sys_freq) }); /// How much we adjust the LED period every cycle diff --git a/rp2040-hal/src/multicore.rs b/rp2040-hal/src/multicore.rs index 638adf7..b1a4d28 100644 --- a/rp2040-hal/src/multicore.rs +++ b/rp2040-hal/src/multicore.rs @@ -101,7 +101,11 @@ impl Stack { impl<'p> Multicore<'p> { /// Create a new |Multicore| instance. - pub fn new(psm: &'p mut pac::PSM, ppb: &'p mut pac::PPB, sio: &'p mut crate::Sio) -> Self { + pub fn new( + psm: &'p mut pac::PSM, + ppb: &'p mut pac::PPB, + sio: &'p mut crate::sio::SioFifo, + ) -> Self { Self { cores: [ Core { inner: None }, @@ -120,7 +124,11 @@ impl<'p> Multicore<'p> { /// A handle for controlling a logical core. pub struct Core<'p> { - inner: Option<(&'p mut pac::PSM, &'p mut pac::PPB, &'p mut crate::Sio)>, + inner: Option<( + &'p mut pac::PSM, + &'p mut pac::PPB, + &'p mut crate::sio::SioFifo, + )>, } impl<'p> Core<'p> { @@ -133,11 +141,11 @@ impl<'p> Core<'p> { } /// Spawn a function on this core. - pub fn spawn(&mut self, entry: F, stack: &'static mut [usize]) -> Result<(), Error> + pub fn spawn(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> where F: FnOnce() -> bad::Never + Send + 'static, { - if let Some((psm, ppb, sio)) = self.inner.as_mut() { + if let Some((psm, ppb, fifo)) = self.inner.as_mut() { // The first two ignored `u64` parameters are there to take up all of the registers, // which means that the rest of the arguments are taken from the stack, // where we're able to put them from core 0. @@ -202,11 +210,11 @@ impl<'p> Core<'p> { loop { let cmd = cmd_seq[seq] as u32; if cmd == 0 { - sio.fifo.drain(); + fifo.drain(); cortex_m::asm::sev(); } - sio.fifo.write_blocking(cmd); - let response = sio.fifo.read_blocking(); + fifo.write_blocking(cmd); + let response = fifo.read_blocking(); if cmd == response { seq += 1; } else { @@ -225,7 +233,7 @@ impl<'p> Core<'p> { } // Wait until the other core has copied `entry` before returning. - sio.fifo.read_blocking(); + fifo.read_blocking(); Ok(()) } else { From 3e1e762d20c33176655f8ed949b3d1a3d7c2d356 Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Sun, 8 May 2022 18:29:27 +1000 Subject: [PATCH 07/10] Add `multicore_polyblink` example --- rp2040-hal/examples/multicore_polyblink.rs | 133 +++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 rp2040-hal/examples/multicore_polyblink.rs diff --git a/rp2040-hal/examples/multicore_polyblink.rs b/rp2040-hal/examples/multicore_polyblink.rs new file mode 100644 index 0000000..2744def --- /dev/null +++ b/rp2040-hal/examples/multicore_polyblink.rs @@ -0,0 +1,133 @@ +//! # Multicore Blinking Example +//! +//! This application blinks two LEDs on GPIOs 2 and 3 at different rates (3Hz +//! and 4Hz respectively.) +//! +//! See the `Cargo.toml` file for Copyright and licence details. + +#![no_std] +#![no_main] + +use cortex_m::delay::Delay; +// The macro for our start-up function +use cortex_m_rt::entry; + +use embedded_time::fixed_point::FixedPoint; +use hal::clocks::Clock; +use hal::gpio::Pins; +use hal::multicore::{Multicore, Stack}; +use hal::sio::Sio; +// Ensure we halt the program on panic (if we don't mention this crate it won't +// be linked) +use panic_halt as _; + +// Alias for our HAL crate +use rp2040_hal as hal; + +// A shorter alias for the Peripheral Access Crate, which provides low-level +// register access +use hal::pac; + +// Some traits we need +use embedded_hal::digital::v2::ToggleableOutputPin; + +/// The linker will place this boot block at the start of our program image. We +/// need this to help the ROM bootloader get our code up and running. +#[link_section = ".boot2"] +#[used] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust +/// if your board has a different frequency +const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// The frequency at which core 0 will blink its LED (Hz). +const CORE0_FREQ: u32 = 3; +/// The frequency at which core 1 will blink its LED (Hz). +const CORE1_FREQ: u32 = 4; +/// The delay between each toggle of core 0's LED (us). +const CORE0_DELAY: u32 = 1_000_000 / CORE0_FREQ; +/// The delay between each toggle of core 1's LED (us). +const CORE1_DELAY: u32 = 1_000_000 / CORE1_FREQ; + +/// Stack for core 1 +/// +/// Core 0 gets its stack via the normal route - any memory not used by static +/// values is reserved for stack and initialised by cortex-m-rt. +/// To get the same for Core 1, we would need to compile everything seperately +/// and modify the linker file for both programs, and that's quite annoying. +/// So instead, core1.spawn takes a [usize] which gets used for the stack. +/// NOTE: We use the `Stack` struct here to ensure that it has 32-byte +/// alignment, which allows the stack guard to take up the least amount of +/// usable RAM. +static mut CORE1_STACK: Stack<4096> = Stack::new(); + +/// Entry point to our bare-metal application. +/// +/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function +/// as soon as all global variables are initialised. +#[entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); + + // Set up the watchdog driver - needed by the clock setup code + let mut watchdog = hal::watchdog::Watchdog::new(pac.WATCHDOG); + + // Configure the clocks + let clocks = hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + // Set up the GPIO pins + let mut sio = Sio::new(pac.SIO); + let pins = Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + let mut led1 = pins.gpio2.into_push_pull_output(); + let mut led2 = pins.gpio3.into_push_pull_output(); + + // Set up the delay for the first core. + let sys_freq = clocks.system_clock.freq().integer(); + let mut delay = Delay::new(core.SYST, sys_freq); + + // Start up the second core to blink the second LED + let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo); + let cores = mc.cores(); + let core1 = &mut cores[1]; + core1 + .spawn(unsafe { &mut CORE1_STACK.mem }, move || { + // Get the second core's copy of the `CorePeripherals`, which are per-core. + // Unfortunately, `cortex-m` doesn't support this properly right now, + // so we have to use `steal`. + let core = unsafe { pac::CorePeripherals::steal() }; + // Set up the delay for the second core. + let mut delay = Delay::new(core.SYST, sys_freq); + // Blink the second LED. + loop { + led2.toggle().unwrap(); + delay.delay_us(CORE1_DELAY) + } + }) + .unwrap(); + + // Blink the first LED. + loop { + led1.toggle().unwrap(); + delay.delay_us(CORE0_DELAY) + } +} + +// End of file From 0e5fdcfd9cbdd0efbf4af1315366a5b4c2e17b10 Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Sun, 8 May 2022 20:14:40 +1000 Subject: [PATCH 08/10] Fix example --- rp2040-hal/src/multicore.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rp2040-hal/src/multicore.rs b/rp2040-hal/src/multicore.rs index b1a4d28..64f05a9 100644 --- a/rp2040-hal/src/multicore.rs +++ b/rp2040-hal/src/multicore.rs @@ -20,10 +20,10 @@ //! let mut pac = pac::Peripherals::take().unwrap(); //! let mut sio = Sio::new(pac.SIO); //! // Other init code above this line -//! let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio); +//! let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo); //! let cores = mc.cores(); //! let core1 = &mut cores[1]; -//! let _test = core1.spawn(core1_task, unsafe { &mut CORE1_STACK.mem }); +//! let _test = core1.spawn(unsafe { &mut CORE1_STACK.mem }, core1_task); //! // The rest of your application below this line //! # loop {} //! } From 9848f849bd5faeea8b01a5c68b59eeccf112ec1d Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Sun, 29 May 2022 19:56:20 +1000 Subject: [PATCH 09/10] Write core 1 arguments directly to RAM without casting to usize --- rp2040-hal/src/multicore.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/rp2040-hal/src/multicore.rs b/rp2040-hal/src/multicore.rs index 64f05a9..ed76f33 100644 --- a/rp2040-hal/src/multicore.rs +++ b/rp2040-hal/src/multicore.rs @@ -180,17 +180,19 @@ impl<'p> Core<'p> { // Set up the stack let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; - let mut push = |v: usize| unsafe { - stack_ptr = stack_ptr.sub(1); - stack_ptr.write(v); - }; - // We don't want to drop this, since it's getting moved to the other core. let mut entry = ManuallyDrop::new(entry); // Push the arguments to `core1_startup` onto the stack. - push(stack.as_mut_ptr() as usize); - push(&mut entry as *mut _ as usize); + unsafe { + // Push `stack_bottom`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); + + // Push `entry`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); + } let vector_table = ppb.vtor.read().bits(); From e9534ace04661aa0a86f7fdbcc487c175c1838ee Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Wed, 1 Jun 2022 17:33:37 +1000 Subject: [PATCH 10/10] Add a fence after writing the arguments to the stack Co-authored-by: Jan Niehusmann --- rp2040-hal/src/multicore.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rp2040-hal/src/multicore.rs b/rp2040-hal/src/multicore.rs index ed76f33..341f583 100644 --- a/rp2040-hal/src/multicore.rs +++ b/rp2040-hal/src/multicore.rs @@ -35,6 +35,8 @@ //! For a detailed example, see [examples/multicore_fifo_blink.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/multicore_fifo_blink.rs) use core::mem::ManuallyDrop; +use core::sync::atomic::compiler_fence; +use core::sync::atomic::Ordering; use crate::pac; use crate::Sio; @@ -194,6 +196,15 @@ impl<'p> Core<'p> { stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); } + // Make sure the compiler does not reorder the stack writes after to after the + // below FIFO writes, which would result in them not being seen by the second + // core. + // + // From the compiler perspective, this doesn't guarantee that the second core + // actually sees those writes. However, we know that the RP2040 doesn't have + // memory caches, and writes happen in-order. + compiler_fence(Ordering::Release); + let vector_table = ppb.vtor.read().bits(); // After reset, core 1 is waiting to receive commands over FIFO.