From 87795cbca84a67ce9c5bdb5c3043b41251d75bbc Mon Sep 17 00:00:00 2001 From: Fabian Kunze Date: Sun, 7 May 2023 01:00:13 +0200 Subject: [PATCH] added example multi priority executors rp2040 --- embassy-rp/Cargo.toml | 2 +- embassy-rp/src/interrupt.rs | 6 ++ examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/multiprio.rs | 152 +++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 examples/rp/src/bin/multiprio.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 59d0bf338..ea8f54f36 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -61,7 +61,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { version = "2", features = ["rt"] } +rp-pac = { version = "2", features = ["rt"], path = "../../rp-pac" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} diff --git a/embassy-rp/src/interrupt.rs b/embassy-rp/src/interrupt.rs index f21a5433b..989f5dc2d 100644 --- a/embassy-rp/src/interrupt.rs +++ b/embassy-rp/src/interrupt.rs @@ -34,3 +34,9 @@ declare!(ADC_IRQ_FIFO); declare!(I2C0_IRQ); declare!(I2C1_IRQ); declare!(RTC_IRQ); +declare!(SWI_IRQ_0); +declare!(SWI_IRQ_1); +declare!(SWI_IRQ_2); +declare!(SWI_IRQ_3); +declare!(SWI_IRQ_4); +declare!(SWI_IRQ_5); diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 57f6f5c67..3f74129a4 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs new file mode 100644 index 000000000..2f79ba49e --- /dev/null +++ b/examples/rp/src/bin/multiprio.rs @@ -0,0 +1,152 @@ +//! This example showcases how to create multiple Executor instances to run tasks at +//! different priority levels. +//! +//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling +//! there's work in the queue, and `wfe` for waiting for work. +//! +//! Medium and high priority executors run in two interrupts with different priorities. +//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since +//! when there's work the interrupt will trigger and run the executor. +//! +//! Sample output below. Note that high priority ticks can interrupt everything else, and +//! medium priority computations can interrupt low priority computations, making them to appear +//! to take significantly longer time. +//! +//! ```not_rust +//! [med] Starting long computation +//! [med] done in 992 ms +//! [high] tick! +//! [low] Starting long computation +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [low] done in 3972 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! ``` +//! +//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. +//! You will get an output like the following. Note that no computation is ever interrupted. +//! +//! ```not_rust +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [low] Starting long computation +//! [low] done in 992 ms +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! [low] Starting long computation +//! [low] done in 992 ms +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! ``` +//! + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::mem; + +use cortex_m::peripheral::NVIC; +use cortex_m_rt::entry; +use defmt::{info, unwrap}; +use embassy_rp::executor::{Executor, InterruptExecutor}; +use embassy_rp::interrupt; +use embassy_rp::pac::Interrupt; +use embassy_time::{Duration, Instant, Timer, TICK_HZ}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn run_high() { + loop { + info!(" [high] tick!"); + Timer::after(Duration::from_ticks(673740)).await; + } +} + +#[embassy_executor::task] +async fn run_med() { + loop { + let start = Instant::now(); + info!(" [med] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(125_000_000); // ~1 second + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; + info!(" [med] done in {} ms", ms); + + Timer::after(Duration::from_ticks(53421)).await; + } +} + +#[embassy_executor::task] +async fn run_low() { + loop { + let start = Instant::now(); + info!("[low] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(250_000_000); // ~2 seconds + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; + info!("[low] done in {} ms", ms); + + Timer::after(Duration::from_ticks(82983)).await; + } +} + +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_LOW: StaticCell = StaticCell::new(); + +#[interrupt] +unsafe fn SWI_IRQ_1() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn SWI_IRQ_0() { + EXECUTOR_MED.on_interrupt() +} + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let _p = embassy_rp::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; + + // High-priority executor: SWI_IRQ_1, priority level 2 + unsafe { nvic.set_priority(Interrupt::SWI_IRQ_1, 2 << 6) }; + info!("bla: {}", NVIC::get_priority(Interrupt::SWI_IRQ_1)); + let spawner = EXECUTOR_HIGH.start(Interrupt::SWI_IRQ_1); + unwrap!(spawner.spawn(run_high())); + + // Medium-priority executor: SWI_IRQ_0, priority level 3 + unsafe { nvic.set_priority(Interrupt::SWI_IRQ_0, 3 << 6) }; + info!("bla: {}", NVIC::get_priority(Interrupt::SWI_IRQ_0)); + let spawner = EXECUTOR_MED.start(Interrupt::SWI_IRQ_0); + unwrap!(spawner.spawn(run_med())); + + // Low priority executor: runs in thread mode, using WFE/SEV + let executor = EXECUTOR_LOW.init(Executor::new()); + executor.run(|spawner| { + unwrap!(spawner.spawn(run_low())); + }); +}