From 967a98fd4448a8e00c8cbfb509c1cc32e2bdcd29 Mon Sep 17 00:00:00 2001 From: Alessandro Gasbarroni Date: Tue, 17 Dec 2024 13:25:58 +0100 Subject: [PATCH 1/2] nrf: Add IPC peripheral for nRF5340 --- embassy-nrf/src/chips/nrf5340_app.rs | 5 + embassy-nrf/src/chips/nrf5340_net.rs | 5 + embassy-nrf/src/ipc.rs | 360 +++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 2 + 4 files changed, 372 insertions(+) create mode 100644 embassy-nrf/src/ipc.rs diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 0103fa7ae..99cf29487 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -262,6 +262,9 @@ embassy_hal_internal::peripherals! { PPI_GROUP4, PPI_GROUP5, + // IPC + IPC, + // GPIO port 0 #[cfg(feature = "lfxo-pins-as-gpio")] P0_00, @@ -327,6 +330,8 @@ embassy_hal_internal::peripherals! { EGU5, } +impl_ipc!(IPC, IPC, IPC); + impl_usb!(USBD, USBD, USBD); impl_uarte!(SERIAL0, UARTE0, SERIAL0); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 22d33d080..c2932be31 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -141,6 +141,9 @@ embassy_hal_internal::peripherals! { PPI_GROUP4, PPI_GROUP5, + // IPC + IPC, + // GPIO port 0 P0_00, P0_01, @@ -200,6 +203,8 @@ embassy_hal_internal::peripherals! { EGU0, } +impl_ipc!(IPC, IPC, IPC); + impl_uarte!(SERIAL0, UARTE0, SERIAL0); impl_spim!(SERIAL0, SPIM0, SERIAL0); impl_spis!(SERIAL0, SPIS0, SERIAL0); diff --git a/embassy-nrf/src/ipc.rs b/embassy-nrf/src/ipc.rs new file mode 100644 index 000000000..410783ef4 --- /dev/null +++ b/embassy-nrf/src/ipc.rs @@ -0,0 +1,360 @@ +//! InterProcessor Communication (IPC) + +#![macro_use] + +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::peripherals::IPC; +use crate::{interrupt, pac}; + +/// IPC Event +#[derive(Debug, Clone, Copy)] +pub enum IpcEvent { + /// IPC Event 0 + Event0 = 0, + /// IPC Event 1 + Event1 = 1, + /// IPC Event 2 + Event2 = 2, + /// IPC Event 3 + Event3 = 3, + /// IPC Event 4 + Event4 = 4, + /// IPC Event 5 + Event5 = 5, + /// IPC Event 6 + Event6 = 6, + /// IPC Event 7 + Event7 = 7, + /// IPC Event 8 + Event8 = 8, + /// IPC Event 9 + Event9 = 9, + /// IPC Event 10 + Event10 = 10, + /// IPC Event 11 + Event11 = 11, + /// IPC Event 12 + Event12 = 12, + /// IPC Event 13 + Event13 = 13, + /// IPC Event 14 + Event14 = 14, + /// IPC Event 15 + Event15 = 15, +} + +const EVENTS: [IpcEvent; 16] = [ + IpcEvent::Event0, + IpcEvent::Event1, + IpcEvent::Event2, + IpcEvent::Event3, + IpcEvent::Event4, + IpcEvent::Event5, + IpcEvent::Event6, + IpcEvent::Event7, + IpcEvent::Event8, + IpcEvent::Event9, + IpcEvent::Event10, + IpcEvent::Event11, + IpcEvent::Event12, + IpcEvent::Event13, + IpcEvent::Event14, + IpcEvent::Event15, +]; + +/// IPC Channel +#[derive(Debug, Clone, Copy)] +pub enum IpcChannel { + /// IPC Channel 0 + Channel0, + /// IPC Channel 1 + Channel1, + /// IPC Channel 2 + Channel2, + /// IPC Channel 3 + Channel3, + /// IPC Channel 4 + Channel4, + /// IPC Channel 5 + Channel5, + /// IPC Channel 6 + Channel6, + /// IPC Channel 7 + Channel7, + /// IPC Channel 8 + Channel8, + /// IPC Channel 9 + Channel9, + /// IPC Channel 10 + Channel10, + /// IPC Channel 11 + Channel11, + /// IPC Channel 12 + Channel12, + /// IPC Channel 13 + Channel13, + /// IPC Channel 14 + Channel14, + /// IPC Channel 15 + Channel15, +} + +/// Interrupt Handler +pub struct InterruptHandler {} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = IPC::regs(); + + // Check if an event was generated, and if it was, trigger the corresponding waker + for event in EVENTS { + if regs.events_receive(event as usize).read() & 0x01 == 0x01 { + // Event is set. Reset and wake waker + regs.events_receive(event as usize).write_value(0); + IPC::state().waker_for(event); + } + + // Ensure the state is actually cleared + // Ref: nRF5340 PS v1.5 7.1.9.1 p.153 + compiler_fence(Ordering::SeqCst); + while regs.events_receive(event as usize).read() & 0x01 != 0x00 {} + } + } +} + +/// IPC driver +pub struct Ipc<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> From> for Ipc<'d, T> { + fn from(value: PeripheralRef<'d, T>) -> Self { + Self { _peri: value } + } +} + +impl<'d, T: Instance> Ipc<'d, T> { + /// Create IPC driver + pub fn new(ipc: impl Peripheral

+ 'd) -> Self { + into_ref!(ipc); + + Self { _peri: ipc } + } + + /// Duplicates the peripheral singleton + /// + /// # Safety + /// + /// Ensure manually that only one peripheral is in use at one time + pub unsafe fn clone_unchecked(&self) -> Self { + Self { + _peri: self._peri.clone_unchecked(), + } + } + + /// Configures the sending of events + /// + /// Events can be configured to broadcast on one or multiple IPC channels. + pub fn configure_send_event>(&self, ev: IpcEvent, channels: I) { + let regs = T::regs(); + + regs.send_cnf(ev as usize).write(|w| { + for channel in channels { + match channel { + IpcChannel::Channel0 => w.set_chen0(true), + IpcChannel::Channel1 => w.set_chen1(true), + IpcChannel::Channel2 => w.set_chen2(true), + IpcChannel::Channel3 => w.set_chen3(true), + IpcChannel::Channel4 => w.set_chen4(true), + IpcChannel::Channel5 => w.set_chen5(true), + IpcChannel::Channel6 => w.set_chen6(true), + IpcChannel::Channel7 => w.set_chen7(true), + IpcChannel::Channel8 => w.set_chen8(true), + IpcChannel::Channel9 => w.set_chen9(true), + IpcChannel::Channel10 => w.set_chen10(true), + IpcChannel::Channel11 => w.set_chen11(true), + IpcChannel::Channel12 => w.set_chen12(true), + IpcChannel::Channel13 => w.set_chen13(true), + IpcChannel::Channel14 => w.set_chen14(true), + IpcChannel::Channel15 => w.set_chen15(true), + } + } + }) + } + + /// Configures the receiving of events + /// + /// Events can be configured to be received by one or multiple IPC channels. + pub fn configure_receive_event>(&self, ev: IpcEvent, channels: I) { + let regs = T::regs(); + + regs.receive_cnf(ev as usize).write(|w| { + for channel in channels { + match channel { + IpcChannel::Channel0 => w.set_chen0(true), + IpcChannel::Channel1 => w.set_chen1(true), + IpcChannel::Channel2 => w.set_chen2(true), + IpcChannel::Channel3 => w.set_chen3(true), + IpcChannel::Channel4 => w.set_chen4(true), + IpcChannel::Channel5 => w.set_chen5(true), + IpcChannel::Channel6 => w.set_chen6(true), + IpcChannel::Channel7 => w.set_chen7(true), + IpcChannel::Channel8 => w.set_chen8(true), + IpcChannel::Channel9 => w.set_chen9(true), + IpcChannel::Channel10 => w.set_chen10(true), + IpcChannel::Channel11 => w.set_chen11(true), + IpcChannel::Channel12 => w.set_chen12(true), + IpcChannel::Channel13 => w.set_chen13(true), + IpcChannel::Channel14 => w.set_chen14(true), + IpcChannel::Channel15 => w.set_chen15(true), + } + } + }); + } + + /// Triggers an event + pub fn trigger_event(&self, ev: IpcEvent) { + let regs = T::regs(); + + regs.tasks_send(ev as usize).write_value(0x01); + } + + /// Wait for event to be triggered + pub async fn wait_for_event(&self, ev: IpcEvent) { + let regs = T::regs(); + + // Enable interrupt + match ev { + IpcEvent::Event0 => { + regs.inten().modify(|m| m.set_receive0(true)); + } + IpcEvent::Event1 => { + regs.inten().modify(|m| m.set_receive1(true)); + } + IpcEvent::Event2 => { + regs.inten().modify(|m| m.set_receive2(true)); + } + IpcEvent::Event3 => { + regs.inten().modify(|m| m.set_receive3(true)); + } + IpcEvent::Event4 => { + regs.inten().modify(|m| m.set_receive4(true)); + } + IpcEvent::Event5 => { + regs.inten().modify(|m| m.set_receive5(true)); + } + IpcEvent::Event6 => { + regs.inten().modify(|m| m.set_receive6(true)); + } + IpcEvent::Event7 => { + regs.inten().modify(|m| m.set_receive7(true)); + } + IpcEvent::Event8 => { + regs.inten().modify(|m| m.set_receive8(true)); + } + IpcEvent::Event9 => { + regs.inten().modify(|m| m.set_receive9(true)); + } + IpcEvent::Event10 => { + regs.inten().modify(|m| m.set_receive10(true)); + } + IpcEvent::Event11 => { + regs.inten().modify(|m| m.set_receive11(true)); + } + IpcEvent::Event12 => { + regs.inten().modify(|m| m.set_receive12(true)); + } + IpcEvent::Event13 => { + regs.inten().modify(|m| m.set_receive13(true)); + } + IpcEvent::Event14 => { + regs.inten().modify(|m| m.set_receive14(true)); + } + IpcEvent::Event15 => { + regs.inten().modify(|m| m.set_receive15(true)); + } + }; + + poll_fn(|cx| { + IPC::state().waker_for(ev).register(cx.waker()); + + if regs.events_receive(ev as usize).read() & 0x01 == 0x01 { + regs.events_receive(ev as usize).write_value(0x00); + + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + } +} + +pub(crate) struct State { + wakers: [AtomicWaker; 16], +} + +impl State { + pub(crate) const fn new() -> Self { + const WAKER: AtomicWaker = AtomicWaker::new(); + + Self { wakers: [WAKER; 16] } + } + + const fn waker_for(&self, ev: IpcEvent) -> &AtomicWaker { + match ev { + IpcEvent::Event0 => &self.wakers[0], + IpcEvent::Event1 => &self.wakers[1], + IpcEvent::Event2 => &self.wakers[2], + IpcEvent::Event3 => &self.wakers[3], + IpcEvent::Event4 => &self.wakers[4], + IpcEvent::Event5 => &self.wakers[5], + IpcEvent::Event6 => &self.wakers[6], + IpcEvent::Event7 => &self.wakers[7], + IpcEvent::Event8 => &self.wakers[8], + IpcEvent::Event9 => &self.wakers[9], + IpcEvent::Event10 => &self.wakers[10], + IpcEvent::Event11 => &self.wakers[11], + IpcEvent::Event12 => &self.wakers[12], + IpcEvent::Event13 => &self.wakers[13], + IpcEvent::Event14 => &self.wakers[14], + IpcEvent::Event15 => &self.wakers[15], + } + } +} + +pub(crate) trait SealedInstance { + fn regs() -> pac::ipc::Ipc; + fn state() -> &'static State; +} + +/// IPC peripheral instance. +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_ipc { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::ipc::SealedInstance for peripherals::$type { + fn regs() -> pac::ipc::Ipc { + pac::$pac_type + } + + fn state() -> &'static crate::ipc::State { + static STATE: crate::ipc::State = crate::ipc::State::new(); + &STATE + } + } + impl crate::ipc::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 07ba2f6d4..5bce65a98 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -88,6 +88,8 @@ pub mod gpiote; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; +#[cfg(feature = "_nrf5340")] +pub mod ipc; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any( feature = "nrf52832", From a0a339d01ade57aa6e66835a27b1e6ea54333b54 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Thu, 8 May 2025 23:26:10 -0500 Subject: [PATCH 2/2] nrf: Rework IPC module --- embassy-nrf/src/ipc.rs | 423 +++++++++++++++++++++-------------------- 1 file changed, 213 insertions(+), 210 deletions(-) diff --git a/embassy-nrf/src/ipc.rs b/embassy-nrf/src/ipc.rs index 410783ef4..a8a08c911 100644 --- a/embassy-nrf/src/ipc.rs +++ b/embassy-nrf/src/ipc.rs @@ -3,18 +3,20 @@ #![macro_use] use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -use crate::peripherals::IPC; -use crate::{interrupt, pac}; +use crate::interrupt::typelevel::Interrupt; +use crate::{interrupt, pac, ppi}; + +const EVENT_COUNT: usize = 16; /// IPC Event -#[derive(Debug, Clone, Copy)] -pub enum IpcEvent { +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum EventNumber { /// IPC Event 0 Event0 = 0, /// IPC Event 1 @@ -49,27 +51,27 @@ pub enum IpcEvent { Event15 = 15, } -const EVENTS: [IpcEvent; 16] = [ - IpcEvent::Event0, - IpcEvent::Event1, - IpcEvent::Event2, - IpcEvent::Event3, - IpcEvent::Event4, - IpcEvent::Event5, - IpcEvent::Event6, - IpcEvent::Event7, - IpcEvent::Event8, - IpcEvent::Event9, - IpcEvent::Event10, - IpcEvent::Event11, - IpcEvent::Event12, - IpcEvent::Event13, - IpcEvent::Event14, - IpcEvent::Event15, +const EVENTS: [EventNumber; EVENT_COUNT] = [ + EventNumber::Event0, + EventNumber::Event1, + EventNumber::Event2, + EventNumber::Event3, + EventNumber::Event4, + EventNumber::Event5, + EventNumber::Event6, + EventNumber::Event7, + EventNumber::Event8, + EventNumber::Event9, + EventNumber::Event10, + EventNumber::Event11, + EventNumber::Event12, + EventNumber::Event13, + EventNumber::Event14, + EventNumber::Event15, ]; /// IPC Channel -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum IpcChannel { /// IPC Channel 0 Channel0, @@ -105,188 +107,124 @@ pub enum IpcChannel { Channel15, } -/// Interrupt Handler -pub struct InterruptHandler {} +impl IpcChannel { + fn mask(self) -> u32 { + 1 << (self as u32) + } +} -impl interrupt::typelevel::Handler for InterruptHandler { +/// Interrupt Handler +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let regs = IPC::regs(); + let regs = T::regs(); // Check if an event was generated, and if it was, trigger the corresponding waker for event in EVENTS { if regs.events_receive(event as usize).read() & 0x01 == 0x01 { - // Event is set. Reset and wake waker - regs.events_receive(event as usize).write_value(0); - IPC::state().waker_for(event); + regs.intenclr().write(|w| w.0 = 0x01 << event as u32); + T::state().wakers[event as usize].wake(); } - - // Ensure the state is actually cleared - // Ref: nRF5340 PS v1.5 7.1.9.1 p.153 - compiler_fence(Ordering::SeqCst); - while regs.events_receive(event as usize).read() & 0x01 != 0x00 {} } } } /// IPC driver +#[non_exhaustive] pub struct Ipc<'d, T: Instance> { - _peri: PeripheralRef<'d, T>, -} - -impl<'d, T: Instance> From> for Ipc<'d, T> { - fn from(value: PeripheralRef<'d, T>) -> Self { - Self { _peri: value } - } + /// Event 0 + pub event0: Event<'d, T>, + /// Event 1 + pub event1: Event<'d, T>, + /// Event 2 + pub event2: Event<'d, T>, + /// Event 3 + pub event3: Event<'d, T>, + /// Event 4 + pub event4: Event<'d, T>, + /// Event 5 + pub event5: Event<'d, T>, + /// Event 6 + pub event6: Event<'d, T>, + /// Event 7 + pub event7: Event<'d, T>, + /// Event 8 + pub event8: Event<'d, T>, + /// Event 9 + pub event9: Event<'d, T>, + /// Event 10 + pub event10: Event<'d, T>, + /// Event 11 + pub event11: Event<'d, T>, + /// Event 12 + pub event12: Event<'d, T>, + /// Event 13 + pub event13: Event<'d, T>, + /// Event 14 + pub event14: Event<'d, T>, + /// Event 15 + pub event15: Event<'d, T>, } impl<'d, T: Instance> Ipc<'d, T> { - /// Create IPC driver - pub fn new(ipc: impl Peripheral

+ 'd) -> Self { - into_ref!(ipc); + /// Create a new IPC driver. + pub fn new( + _p: Peri<'d, T>, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; - Self { _peri: ipc } - } - - /// Duplicates the peripheral singleton - /// - /// # Safety - /// - /// Ensure manually that only one peripheral is in use at one time - pub unsafe fn clone_unchecked(&self) -> Self { - Self { - _peri: self._peri.clone_unchecked(), - } - } - - /// Configures the sending of events - /// - /// Events can be configured to broadcast on one or multiple IPC channels. - pub fn configure_send_event>(&self, ev: IpcEvent, channels: I) { - let regs = T::regs(); - - regs.send_cnf(ev as usize).write(|w| { - for channel in channels { - match channel { - IpcChannel::Channel0 => w.set_chen0(true), - IpcChannel::Channel1 => w.set_chen1(true), - IpcChannel::Channel2 => w.set_chen2(true), - IpcChannel::Channel3 => w.set_chen3(true), - IpcChannel::Channel4 => w.set_chen4(true), - IpcChannel::Channel5 => w.set_chen5(true), - IpcChannel::Channel6 => w.set_chen6(true), - IpcChannel::Channel7 => w.set_chen7(true), - IpcChannel::Channel8 => w.set_chen8(true), - IpcChannel::Channel9 => w.set_chen9(true), - IpcChannel::Channel10 => w.set_chen10(true), - IpcChannel::Channel11 => w.set_chen11(true), - IpcChannel::Channel12 => w.set_chen12(true), - IpcChannel::Channel13 => w.set_chen13(true), - IpcChannel::Channel14 => w.set_chen14(true), - IpcChannel::Channel15 => w.set_chen15(true), - } - } - }) - } - - /// Configures the receiving of events - /// - /// Events can be configured to be received by one or multiple IPC channels. - pub fn configure_receive_event>(&self, ev: IpcEvent, channels: I) { - let regs = T::regs(); - - regs.receive_cnf(ev as usize).write(|w| { - for channel in channels { - match channel { - IpcChannel::Channel0 => w.set_chen0(true), - IpcChannel::Channel1 => w.set_chen1(true), - IpcChannel::Channel2 => w.set_chen2(true), - IpcChannel::Channel3 => w.set_chen3(true), - IpcChannel::Channel4 => w.set_chen4(true), - IpcChannel::Channel5 => w.set_chen5(true), - IpcChannel::Channel6 => w.set_chen6(true), - IpcChannel::Channel7 => w.set_chen7(true), - IpcChannel::Channel8 => w.set_chen8(true), - IpcChannel::Channel9 => w.set_chen9(true), - IpcChannel::Channel10 => w.set_chen10(true), - IpcChannel::Channel11 => w.set_chen11(true), - IpcChannel::Channel12 => w.set_chen12(true), - IpcChannel::Channel13 => w.set_chen13(true), - IpcChannel::Channel14 => w.set_chen14(true), - IpcChannel::Channel15 => w.set_chen15(true), - } - } - }); - } - - /// Triggers an event - pub fn trigger_event(&self, ev: IpcEvent) { - let regs = T::regs(); - - regs.tasks_send(ev as usize).write_value(0x01); - } - - /// Wait for event to be triggered - pub async fn wait_for_event(&self, ev: IpcEvent) { - let regs = T::regs(); - - // Enable interrupt - match ev { - IpcEvent::Event0 => { - regs.inten().modify(|m| m.set_receive0(true)); - } - IpcEvent::Event1 => { - regs.inten().modify(|m| m.set_receive1(true)); - } - IpcEvent::Event2 => { - regs.inten().modify(|m| m.set_receive2(true)); - } - IpcEvent::Event3 => { - regs.inten().modify(|m| m.set_receive3(true)); - } - IpcEvent::Event4 => { - regs.inten().modify(|m| m.set_receive4(true)); - } - IpcEvent::Event5 => { - regs.inten().modify(|m| m.set_receive5(true)); - } - IpcEvent::Event6 => { - regs.inten().modify(|m| m.set_receive6(true)); - } - IpcEvent::Event7 => { - regs.inten().modify(|m| m.set_receive7(true)); - } - IpcEvent::Event8 => { - regs.inten().modify(|m| m.set_receive8(true)); - } - IpcEvent::Event9 => { - regs.inten().modify(|m| m.set_receive9(true)); - } - IpcEvent::Event10 => { - regs.inten().modify(|m| m.set_receive10(true)); - } - IpcEvent::Event11 => { - regs.inten().modify(|m| m.set_receive11(true)); - } - IpcEvent::Event12 => { - regs.inten().modify(|m| m.set_receive12(true)); - } - IpcEvent::Event13 => { - regs.inten().modify(|m| m.set_receive13(true)); - } - IpcEvent::Event14 => { - regs.inten().modify(|m| m.set_receive14(true)); - } - IpcEvent::Event15 => { - regs.inten().modify(|m| m.set_receive15(true)); - } + let _phantom = PhantomData; + #[rustfmt::skip] + let r = Self { // attributes on expressions are experimental + event0: Event { number: EventNumber::Event0, _phantom }, + event1: Event { number: EventNumber::Event1, _phantom }, + event2: Event { number: EventNumber::Event2, _phantom }, + event3: Event { number: EventNumber::Event3, _phantom }, + event4: Event { number: EventNumber::Event4, _phantom }, + event5: Event { number: EventNumber::Event5, _phantom }, + event6: Event { number: EventNumber::Event6, _phantom }, + event7: Event { number: EventNumber::Event7, _phantom }, + event8: Event { number: EventNumber::Event8, _phantom }, + event9: Event { number: EventNumber::Event9, _phantom }, + event10: Event { number: EventNumber::Event10, _phantom }, + event11: Event { number: EventNumber::Event11, _phantom }, + event12: Event { number: EventNumber::Event12, _phantom }, + event13: Event { number: EventNumber::Event13, _phantom }, + event14: Event { number: EventNumber::Event14, _phantom }, + event15: Event { number: EventNumber::Event15, _phantom }, }; + r + } +} +/// IPC event +pub struct Event<'d, T: Instance> { + number: EventNumber, + _phantom: PhantomData<&'d T>, +} + +impl<'d, T: Instance> Event<'d, T> { + /// Trigger the event. + pub fn trigger(&self) { + let nr = self.number; + T::regs().tasks_send(nr as usize).write_value(1); + } + + /// Wait for the event to be triggered. + pub async fn wait(&mut self) { + let regs = T::regs(); + let nr = self.number as usize; + regs.intenset().write(|w| w.0 = 1 << nr); poll_fn(|cx| { - IPC::state().waker_for(ev).register(cx.waker()); - - if regs.events_receive(ev as usize).read() & 0x01 == 0x01 { - regs.events_receive(ev as usize).write_value(0x00); + T::state().wakers[nr].register(cx.waker()); + if regs.events_receive(nr).read() == 1 { + regs.events_receive(nr).write_value(0x00); Poll::Ready(()) } else { Poll::Pending @@ -294,37 +232,102 @@ impl<'d, T: Instance> Ipc<'d, T> { }) .await; } + + /// Returns the [`EventNumber`] of the event. + pub fn number(&self) -> EventNumber { + self.number + } + + /// Create a handle that can trigger the event. + pub fn trigger_handle(&self) -> EventTrigger<'d, T> { + EventTrigger { + number: self.number, + _phantom: PhantomData, + } + } + + /// Configure the channels the event will broadcast to + pub fn configure_trigger>(&mut self, channels: I) { + T::regs().send_cnf(self.number as usize).write(|w| { + for channel in channels { + w.0 |= channel.mask(); + } + }) + } + + /// Configure the channels the event will listen on + pub fn configure_wait>(&mut self, channels: I) { + T::regs().receive_cnf(self.number as usize).write(|w| { + for channel in channels { + w.0 |= channel.mask(); + } + }); + } + + /// Get the task for the IPC event to use with PPI. + pub fn task(&self) -> ppi::Task<'d> { + let nr = self.number as usize; + let regs = T::regs(); + ppi::Task::from_reg(regs.tasks_send(nr)) + } + + /// Get the event for the IPC event to use with PPI. + pub fn event(&self) -> ppi::Event<'d> { + let nr = self.number as usize; + let regs = T::regs(); + ppi::Event::from_reg(regs.events_receive(nr)) + } + + /// Reborrow into a "child" Event. + /// + /// `self` will stay borrowed until the child Event is dropped. + pub fn reborrow(&mut self) -> Event<'_, T> { + Self { ..*self } + } + + /// Steal an IPC event by number. + /// + /// # Safety + /// + /// The event number must not be in use by another [`Event`]. + pub unsafe fn steal(number: EventNumber) -> Self { + Self { + number, + _phantom: PhantomData, + } + } +} + +/// A handle that can trigger an IPC event. +/// +/// This `struct` is returned by [`Event::trigger_handle`]. +#[derive(Debug, Copy, Clone)] +pub struct EventTrigger<'d, T: Instance> { + number: EventNumber, + _phantom: PhantomData<&'d T>, +} + +impl EventTrigger<'_, T> { + /// Trigger the event. + pub fn trigger(&self) { + let nr = self.number; + T::regs().tasks_send(nr as usize).write_value(1); + } + + /// Returns the [`EventNumber`] of the event. + pub fn number(&self) -> EventNumber { + self.number + } } pub(crate) struct State { - wakers: [AtomicWaker; 16], + wakers: [AtomicWaker; EVENT_COUNT], } impl State { pub(crate) const fn new() -> Self { - const WAKER: AtomicWaker = AtomicWaker::new(); - - Self { wakers: [WAKER; 16] } - } - - const fn waker_for(&self, ev: IpcEvent) -> &AtomicWaker { - match ev { - IpcEvent::Event0 => &self.wakers[0], - IpcEvent::Event1 => &self.wakers[1], - IpcEvent::Event2 => &self.wakers[2], - IpcEvent::Event3 => &self.wakers[3], - IpcEvent::Event4 => &self.wakers[4], - IpcEvent::Event5 => &self.wakers[5], - IpcEvent::Event6 => &self.wakers[6], - IpcEvent::Event7 => &self.wakers[7], - IpcEvent::Event8 => &self.wakers[8], - IpcEvent::Event9 => &self.wakers[9], - IpcEvent::Event10 => &self.wakers[10], - IpcEvent::Event11 => &self.wakers[11], - IpcEvent::Event12 => &self.wakers[12], - IpcEvent::Event13 => &self.wakers[13], - IpcEvent::Event14 => &self.wakers[14], - IpcEvent::Event15 => &self.wakers[15], + Self { + wakers: [const { AtomicWaker::new() }; EVENT_COUNT], } } } @@ -336,7 +339,7 @@ pub(crate) trait SealedInstance { /// IPC peripheral instance. #[allow(private_bounds)] -pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { +pub trait Instance: PeripheralType + SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; }