1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#![macro_use]

//! Programmable Peripheral Interconnect (PPI/DPPI) driver.
//!
//! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability
//! between peripherals through their events and tasks. There are fixed PPI channels and fully
//! configurable ones. Fixed channels can only connect specific events to specific tasks. For fully
//! configurable channels, it is possible to choose, via software, the event and the task that it
//! will triggered by the event.
//!
//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task
//! to be triggered by the same event, even fixed PPI channels have a configurable fork task.
//!
//! The DPPI for nRF53 and nRF91 devices works in a different way. Every channel can support infinitely
//! many tasks and events, but any single task or event can only be coupled with one channel.
//!

use core::marker::PhantomData;
use core::ptr::NonNull;

use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};

use crate::{peripherals, Peripheral};

#[cfg_attr(feature = "_dppi", path = "dppi.rs")]
#[cfg_attr(feature = "_ppi", path = "ppi.rs")]
mod _version;
pub(crate) use _version::*;

/// PPI channel driver.
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
    ch: PeripheralRef<'d, C>,
    #[cfg(feature = "_dppi")]
    events: [Event<'d>; EVENT_COUNT],
    #[cfg(feature = "_dppi")]
    tasks: [Task<'d>; TASK_COUNT],
}

/// PPI channel group driver.
pub struct PpiGroup<'d, G: Group> {
    g: PeripheralRef<'d, G>,
}

impl<'d, G: Group> PpiGroup<'d, G> {
    /// Create a new PPI group driver.
    ///
    /// The group is initialized as containing no channels.
    pub fn new(g: impl Peripheral<P = G> + 'd) -> Self {
        into_ref!(g);

        let r = regs();
        let n = g.number();
        r.chg[n].write(|w| unsafe { w.bits(0) });

        Self { g }
    }

    /// Add a PPI channel to this group.
    ///
    /// If the channel is already in the group, this is a no-op.
    pub fn add_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
        &mut self,
        ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
    ) {
        let r = regs();
        let ng = self.g.number();
        let nc = ch.ch.number();
        r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() | 1 << nc) });
    }

    /// Remove a PPI channel from this group.
    ///
    /// If the channel is already not in the group, this is a no-op.
    pub fn remove_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
        &mut self,
        ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
    ) {
        let r = regs();
        let ng = self.g.number();
        let nc = ch.ch.number();
        r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() & !(1 << nc)) });
    }

    /// Enable all the channels in this group.
    pub fn enable_all(&mut self) {
        let n = self.g.number();
        regs().tasks_chg[n].en.write(|w| unsafe { w.bits(1) });
    }

    /// Disable all the channels in this group.
    pub fn disable_all(&mut self) {
        let n = self.g.number();
        regs().tasks_chg[n].dis.write(|w| unsafe { w.bits(1) });
    }

    /// Get a reference to the "enable all" task.
    ///
    /// When triggered, it will enable all the channels in this group.
    pub fn task_enable_all(&self) -> Task<'d> {
        let n = self.g.number();
        Task::from_reg(&regs().tasks_chg[n].en)
    }

    /// Get a reference to the "disable all" task.
    ///
    /// When triggered, it will disable all the channels in this group.
    pub fn task_disable_all(&self) -> Task<'d> {
        let n = self.g.number();
        Task::from_reg(&regs().tasks_chg[n].dis)
    }
}

impl<'d, G: Group> Drop for PpiGroup<'d, G> {
    fn drop(&mut self) {
        let r = regs();
        let n = self.g.number();
        r.chg[n].write(|w| unsafe { w.bits(0) });
    }
}

#[cfg(feature = "_dppi")]
const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();

/// Represents a task that a peripheral can do.
///
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
/// a published event.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);

impl<'d> Task<'d> {
    /// Create a new `Task` from a task register pointer
    ///
    /// # Safety
    ///
    /// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
    pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
        Self(ptr, PhantomData)
    }

    /// Triggers this task.
    pub fn trigger(&mut self) {
        unsafe { self.0.as_ptr().write_volatile(1) };
    }

    pub(crate) fn from_reg<T>(reg: &T) -> Self {
        Self(
            unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
            PhantomData,
        )
    }

    /// Address of subscription register for this task.
    #[cfg(feature = "_dppi")]
    pub fn subscribe_reg(&self) -> *mut u32 {
        unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
    }
}

/// # Safety
///
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
unsafe impl Send for Task<'_> {}

/// Represents an event that a peripheral can publish.
///
/// An event can be set to publish on a PPI channel when the event happens.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);

impl<'d> Event<'d> {
    /// Create a new `Event` from an event register pointer
    ///
    /// # Safety
    ///
    /// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
    pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
        Self(ptr, PhantomData)
    }

    pub(crate) fn from_reg<T>(reg: &'d T) -> Self {
        Self(
            unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
            PhantomData,
        )
    }

    /// Describes whether this Event is currently in a triggered state.
    pub fn is_triggered(&self) -> bool {
        unsafe { self.0.as_ptr().read_volatile() == 1 }
    }

    /// Clear the current register's triggered state, reverting it to 0.
    pub fn clear(&mut self) {
        unsafe { self.0.as_ptr().write_volatile(0) };
    }

    /// Address of publish register for this event.
    #[cfg(feature = "_dppi")]
    pub fn publish_reg(&self) -> *mut u32 {
        unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
    }
}

/// # Safety
///
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
unsafe impl Send for Event<'_> {}

// ======================
//       traits

pub(crate) mod sealed {
    pub trait Channel {}
    pub trait Group {}
}

/// Interface for PPI channels.
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized + 'static {
    /// Returns the number of the channel
    fn number(&self) -> usize;
}

/// Interface for PPI channels that can be configured.
pub trait ConfigurableChannel: Channel + Into<AnyConfigurableChannel> {
    /// Convert into a type erased configurable channel.
    fn degrade(self) -> AnyConfigurableChannel;
}

/// Interface for PPI channels that cannot be configured.
pub trait StaticChannel: Channel + Into<AnyStaticChannel> {
    /// Convert into a type erased static channel.
    fn degrade(self) -> AnyStaticChannel;
}

/// Interface for a group of PPI channels.
pub trait Group: sealed::Group + Peripheral<P = Self> + Into<AnyGroup> + Sized + 'static {
    /// Returns the number of the group.
    fn number(&self) -> usize;
    /// Convert into a type erased group.
    fn degrade(self) -> AnyGroup {
        AnyGroup {
            number: self.number() as u8,
        }
    }
}

// ======================
//       channels

/// The any channel can represent any static channel at runtime.
/// This can be used to have fewer generic parameters in some places.
pub struct AnyStaticChannel {
    pub(crate) number: u8,
}
impl_peripheral!(AnyStaticChannel);
impl sealed::Channel for AnyStaticChannel {}
impl Channel for AnyStaticChannel {
    fn number(&self) -> usize {
        self.number as usize
    }
}
impl StaticChannel for AnyStaticChannel {
    fn degrade(self) -> AnyStaticChannel {
        self
    }
}

/// The any configurable channel can represent any configurable channel at runtime.
/// This can be used to have fewer generic parameters in some places.
pub struct AnyConfigurableChannel {
    pub(crate) number: u8,
}
impl_peripheral!(AnyConfigurableChannel);
impl sealed::Channel for AnyConfigurableChannel {}
impl Channel for AnyConfigurableChannel {
    fn number(&self) -> usize {
        self.number as usize
    }
}
impl ConfigurableChannel for AnyConfigurableChannel {
    fn degrade(self) -> AnyConfigurableChannel {
        self
    }
}

macro_rules! impl_ppi_channel {
    ($type:ident, $number:expr) => {
        impl crate::ppi::sealed::Channel for peripherals::$type {}
        impl crate::ppi::Channel for peripherals::$type {
            fn number(&self) -> usize {
                $number
            }
        }
    };
    ($type:ident, $number:expr => static) => {
        impl_ppi_channel!($type, $number);
        impl crate::ppi::StaticChannel for peripherals::$type {
            fn degrade(self) -> crate::ppi::AnyStaticChannel {
                use crate::ppi::Channel;
                crate::ppi::AnyStaticChannel {
                    number: self.number() as u8,
                }
            }
        }

        impl From<peripherals::$type> for crate::ppi::AnyStaticChannel {
            fn from(val: peripherals::$type) -> Self {
                crate::ppi::StaticChannel::degrade(val)
            }
        }
    };
    ($type:ident, $number:expr => configurable) => {
        impl_ppi_channel!($type, $number);
        impl crate::ppi::ConfigurableChannel for peripherals::$type {
            fn degrade(self) -> crate::ppi::AnyConfigurableChannel {
                use crate::ppi::Channel;
                crate::ppi::AnyConfigurableChannel {
                    number: self.number() as u8,
                }
            }
        }

        impl From<peripherals::$type> for crate::ppi::AnyConfigurableChannel {
            fn from(val: peripherals::$type) -> Self {
                crate::ppi::ConfigurableChannel::degrade(val)
            }
        }
    };
}

// ======================
//       groups

/// A type erased PPI group.
pub struct AnyGroup {
    number: u8,
}
impl_peripheral!(AnyGroup);
impl sealed::Group for AnyGroup {}
impl Group for AnyGroup {
    fn number(&self) -> usize {
        self.number as usize
    }
}

macro_rules! impl_group {
    ($type:ident, $number:expr) => {
        impl sealed::Group for peripherals::$type {}
        impl Group for peripherals::$type {
            fn number(&self) -> usize {
                $number
            }
        }

        impl From<peripherals::$type> for crate::ppi::AnyGroup {
            fn from(val: peripherals::$type) -> Self {
                crate::ppi::Group::degrade(val)
            }
        }
    };
}

impl_group!(PPI_GROUP0, 0);
impl_group!(PPI_GROUP1, 1);
impl_group!(PPI_GROUP2, 2);
impl_group!(PPI_GROUP3, 3);
#[cfg(not(feature = "nrf51"))]
impl_group!(PPI_GROUP4, 4);
#[cfg(not(feature = "nrf51"))]
impl_group!(PPI_GROUP5, 5);