Merge branch 'embassy-rs:main' into dfu-msos

This commit is contained in:
Gerhard de Clercq 2025-05-13 14:59:03 +02:00 committed by GitHub
commit f7c796e3cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 379 additions and 1 deletions

View File

@ -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);

View File

@ -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);

363
embassy-nrf/src/ipc.rs Normal file
View File

@ -0,0 +1,363 @@
//! InterProcessor Communication (IPC)
#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, pac, ppi};
const EVENT_COUNT: usize = 16;
/// IPC Event
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum EventNumber {
/// 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: [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, PartialEq, Eq, PartialOrd, Ord, Hash)]
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,
}
impl IpcChannel {
fn mask(self) -> u32 {
1 << (self as u32)
}
}
/// Interrupt Handler
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
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 {
regs.intenclr().write(|w| w.0 = 0x01 << event as u32);
T::state().wakers[event as usize].wake();
}
}
}
}
/// IPC driver
#[non_exhaustive]
pub struct Ipc<'d, T: Instance> {
/// 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 a new IPC driver.
pub fn new(
_p: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
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| {
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
}
})
.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<I: IntoIterator<Item = IpcChannel>>(&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<I: IntoIterator<Item = IpcChannel>>(&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<T: Instance> 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; EVENT_COUNT],
}
impl State {
pub(crate) const fn new() -> Self {
Self {
wakers: [const { AtomicWaker::new() }; EVENT_COUNT],
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> pac::ipc::Ipc;
fn state() -> &'static State;
}
/// IPC peripheral instance.
#[allow(private_bounds)]
pub trait Instance: PeripheralType + 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;
}
};
}

View File

@ -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",

View File

@ -26,7 +26,10 @@ features = ["defmt", "unstable-pac", "time-driver", "rp2040"]
[features]
default = [ "rt" ]
## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization.
## Enable the `rt` feature of [`rp-pac`](https://docs.rs/rp-pac).
## With `rt` enabled the PAC provides interrupt vectors instead of letting [`cortex-m-rt`](https://docs.rs/cortex-m-rt) do that.
## See <https://docs.rs/cortex-m-rt/latest/cortex_m_rt/#device> for more info.
rt = [ "rp-pac/rt" ]
## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.