aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/ppi
diff options
context:
space:
mode:
authorDion Dokter <[email protected]>2021-10-19 10:13:08 +0200
committerDario Nieuwenhuis <[email protected]>2021-10-26 14:47:31 +0200
commita6c84cb91552fc0442a28126d3fae60031aa6622 (patch)
tree260555ec0c58eb5e2053f4031889b4260bb9ebea /embassy-nrf/src/ppi
parent531dfcffb3c203dff15dcb53fe25577c121c7784 (diff)
- Interconnect is now PPI again
- Scary pointer math is now contained in the tasks and events - ppi now sets the tasks and events immediately and the struct is now zero-sized - StaticToOne is renamed to ZeroToOne - Used DPPI tasks and events now panic when enabled twice
Diffstat (limited to 'embassy-nrf/src/ppi')
-rw-r--r--embassy-nrf/src/ppi/dppi.rs61
-rw-r--r--embassy-nrf/src/ppi/mod.rs325
-rw-r--r--embassy-nrf/src/ppi/ppi.rs77
3 files changed, 463 insertions, 0 deletions
diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs
new file mode 100644
index 000000000..083ec858a
--- /dev/null
+++ b/embassy-nrf/src/ppi/dppi.rs
@@ -0,0 +1,61 @@
1use super::{Channel, Event, Ppi, Task};
2
3const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
4const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
5
6impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
7 Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
8{
9 pub(super) fn enable_task(task: &Task, channel: &C, _index: usize) {
10 unsafe {
11 if task.subscribe_reg().read_volatile() != 0 {
12 panic!("Task is already in use");
13 }
14 task.subscribe_reg()
15 .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
16 }
17 }
18
19 pub(super) fn disable_task(task: &Task, _channel: &C, _index: usize) {
20 unsafe {
21 task.subscribe_reg().write_volatile(0);
22 }
23 }
24
25 pub(super) fn enable_event(event: &Event, channel: &C, _index: usize) {
26 unsafe {
27 if event.publish_reg().read_volatile() != 0 {
28 panic!("Task is already in use");
29 }
30 event
31 .publish_reg()
32 .write_volatile(DPPI_ENABLE_BIT | (channel.number() as u32 & DPPI_CHANNEL_MASK));
33 }
34 }
35
36 pub(super) fn disable_event(event: &Event, _channel: &C, _index: usize) {
37 unsafe {
38 event.publish_reg().write_volatile(0);
39 }
40 }
41
42 /// Enables all tasks and events
43 pub(super) fn enable_all(tasks: &[Task], events: &[Event], channel: &C) {
44 for (index, task) in tasks.iter().enumerate() {
45 Self::enable_task(task, channel, index);
46 }
47 for (index, event) in events.iter().enumerate() {
48 Self::enable_event(event, channel, index);
49 }
50 }
51
52 /// Disable all tasks and events
53 pub(super) fn disable_all(&self) {
54 for (index, task) in self.tasks.iter().enumerate() {
55 Self::disable_task(task, &self.ch, index);
56 }
57 for (index, event) in self.events.iter().enumerate() {
58 Self::disable_event(event, &self.ch, index);
59 }
60 }
61}
diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs
new file mode 100644
index 000000000..ffb6af020
--- /dev/null
+++ b/embassy-nrf/src/ppi/mod.rs
@@ -0,0 +1,325 @@
1#![macro_use]
2
3//! HAL interface for the PPI and DPPI peripheral.
4//!
5//! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability
6//! between peripherals through their events and tasks. There are fixed PPI channels and fully
7//! configurable ones. Fixed channels can only connect specific events to specific tasks. For fully
8//! configurable channels, it is possible to choose, via software, the event and the task that it
9//! will triggered by the event.
10//!
11//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task
12//! to be triggered by the same event, even fixed PPI channels have a configurable fork task.
13//!
14//! The DPPI for nRF53 and nRF91 devices works in a different way. Every channel can support infinitely
15//! many tasks and events, but any single task or event can only be coupled with one channel.
16//!
17
18use crate::{pac, peripherals};
19use core::marker::PhantomData;
20use core::ptr::NonNull;
21use embassy::util::Unborrow;
22use embassy_hal_common::{unborrow, unsafe_impl_unborrow};
23
24#[cfg(feature = "_dppi")]
25mod dppi;
26#[cfg(feature = "_ppi")]
27mod ppi;
28
29pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
30 ch: C,
31 #[cfg(feature = "_dppi")]
32 events: [Event; EVENT_COUNT],
33 #[cfg(feature = "_dppi")]
34 tasks: [Task; TASK_COUNT],
35 phantom: PhantomData<&'d mut C>,
36}
37
38impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
39 Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
40{
41 pub fn degrade(self) -> Ppi<'d, AnyChannel, EVENT_COUNT, TASK_COUNT> {
42 Ppi {
43 ch: AnyChannel {
44 number: self.ch.number() as u8,
45 #[cfg(feature = "_ppi")]
46 has_configurable_task: self.ch.is_task_configurable(),
47 },
48 #[cfg(feature = "_dppi")]
49 events: self.events,
50 #[cfg(feature = "_dppi")]
51 tasks: self.tasks,
52 phantom: PhantomData,
53 }
54 }
55
56 /// Enables the channel.
57 pub fn enable(&mut self) {
58 let r = unsafe { &*pac::PPI::ptr() };
59 r.chenset
60 .write(|w| unsafe { w.bits(1 << self.ch.number()) });
61 }
62
63 /// Disables the channel.
64 pub fn disable(&mut self) {
65 let r = unsafe { &*pac::PPI::ptr() };
66 r.chenclr
67 .write(|w| unsafe { w.bits(1 << self.ch.number()) });
68 }
69}
70
71impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop
72 for Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
73{
74 fn drop(&mut self) {
75 self.disable();
76 self.disable_all();
77 }
78}
79
80impl<'d, C: ZeroToOneChannel> Ppi<'d, C, 0, 1> {
81 pub fn new_static_to_one(ch: impl Unborrow<Target = C> + 'd, task: Task) -> Self {
82 unborrow!(ch);
83
84 let events = [];
85 let tasks = [task];
86
87 Self::enable_all(&tasks, &events, &ch);
88
89 Self {
90 ch,
91 #[cfg(feature = "_dppi")]
92 events,
93 #[cfg(feature = "_dppi")]
94 tasks,
95 phantom: PhantomData,
96 }
97 }
98}
99
100impl<'d, C: OneToOneChannel> Ppi<'d, C, 1, 1> {
101 pub fn new_one_to_one(ch: impl Unborrow<Target = C> + 'd, event: Event, task: Task) -> Self {
102 unborrow!(ch);
103
104 let events = [event];
105 let tasks = [task];
106
107 Self::enable_all(&tasks, &events, &ch);
108
109 Self {
110 ch,
111 #[cfg(feature = "_dppi")]
112 events,
113 #[cfg(feature = "_dppi")]
114 tasks,
115 phantom: PhantomData,
116 }
117 }
118}
119
120impl<'d, C: OneToTwoChannel> Ppi<'d, C, 1, 2> {
121 pub fn new_one_to_two(
122 ch: impl Unborrow<Target = C> + 'd,
123 event: Event,
124 task1: Task,
125 task2: Task,
126 ) -> Self {
127 unborrow!(ch);
128
129 let events = [event];
130 let tasks = [task1, task2];
131
132 Self::enable_all(&tasks, &events, &ch);
133
134 Self {
135 ch,
136 #[cfg(feature = "_dppi")]
137 events,
138 #[cfg(feature = "_dppi")]
139 tasks,
140 phantom: PhantomData,
141 }
142 }
143}
144
145impl<'d, C: ManyToManyChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
146 Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
147{
148 pub fn new_many_to_many(
149 ch: impl Unborrow<Target = C> + 'd,
150 events: [Event; EVENT_COUNT],
151 tasks: [Task; TASK_COUNT],
152 ) -> Self {
153 unborrow!(ch);
154
155 Self::enable_all(&tasks, &events, &ch);
156
157 Self {
158 ch,
159 #[cfg(feature = "_dppi")]
160 events,
161 #[cfg(feature = "_dppi")]
162 tasks,
163 phantom: PhantomData,
164 }
165 }
166}
167
168const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
169
170/// Represents a task that a peripheral can do.
171/// When a task is subscribed to a PPI channel it will run when the channel is triggered by
172/// a published event.
173///
174/// The pointer is to a task register
175#[derive(PartialEq, Eq, Clone, Copy)]
176pub struct Task(pub NonNull<u32>);
177impl Task {
178 pub(crate) fn from_reg<T>(reg: &T) -> Self {
179 Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
180 }
181
182 pub fn subscribe_reg(&self) -> *mut u32 {
183 unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
184 }
185}
186
187/// # Safety
188///
189/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
190unsafe impl Send for Task {}
191
192/// Represents an event that a peripheral can publish.
193/// An event can be set to publish on a PPI channel when the event happens.
194///
195/// The pointer is to an event register
196#[derive(PartialEq, Eq, Clone, Copy)]
197pub struct Event(pub NonNull<u32>);
198impl Event {
199 pub(crate) fn from_reg<T>(reg: &T) -> Self {
200 Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
201 }
202
203 pub fn publish_reg(&self) -> *mut u32 {
204 unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
205 }
206}
207
208/// # Safety
209///
210/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
211unsafe impl Send for Event {}
212
213// ======================
214// traits
215
216pub(crate) mod sealed {
217 pub trait Channel {}
218 pub trait Group {}
219}
220
221pub trait Channel: sealed::Channel + Unborrow<Target = Self> + Sized {
222 /// Returns the number of the channel
223 fn number(&self) -> usize;
224 #[cfg(feature = "_ppi")]
225 fn is_task_configurable(&self) -> bool;
226}
227
228pub trait ZeroToOneChannel: Channel {}
229pub trait OneToOneChannel: ZeroToOneChannel {}
230pub trait OneToTwoChannel: OneToOneChannel {}
231pub trait ManyToManyChannel: OneToTwoChannel {}
232
233pub trait Group: sealed::Group + Sized {
234 fn number(&self) -> usize;
235}
236
237// ======================
238// channels
239
240pub struct AnyChannel {
241 number: u8,
242 #[cfg(feature = "_ppi")]
243 has_configurable_task: bool,
244}
245unsafe_impl_unborrow!(AnyChannel);
246impl sealed::Channel for AnyChannel {}
247impl Channel for AnyChannel {
248 fn number(&self) -> usize {
249 self.number as usize
250 }
251
252 #[cfg(feature = "_ppi")]
253 fn is_task_configurable(&self) -> bool {
254 self.has_configurable_task
255 }
256}
257
258macro_rules! impl_ppi_channel {
259 ($type:ident, $number:expr, $has_configurable_task:expr) => {
260 impl crate::ppi::sealed::Channel for peripherals::$type {}
261 impl crate::ppi::Channel for peripherals::$type {
262 fn number(&self) -> usize {
263 $number
264 }
265
266 #[cfg(feature = "_ppi")]
267 fn is_task_configurable(&self) -> bool {
268 $has_configurable_task
269 }
270 }
271 };
272 ($type:ident, $number:expr, $has_configurable_task:expr, 0, 0) => {
273 impl_ppi_channel!($type, $number, $has_configurable_task);
274 };
275 ($type:ident, $number:expr, $has_configurable_task:expr, 0, 1) => {
276 impl_ppi_channel!($type, $number, $has_configurable_task, 0, 0);
277 impl crate::ppi::ZeroToOneChannel for peripherals::$type {}
278 };
279 ($type:ident, $number:expr, $has_configurable_task:expr, 1, 1) => {
280 impl_ppi_channel!($type, $number, $has_configurable_task, 0, 1);
281 impl crate::ppi::OneToOneChannel for peripherals::$type {}
282 };
283 ($type:ident, $number:expr, $has_configurable_task:expr, 1, 2) => {
284 impl_ppi_channel!($type, $number, $has_configurable_task, 1, 1);
285 impl crate::ppi::OneToTwoChannel for peripherals::$type {}
286 };
287 ($type:ident, $number:expr, $has_configurable_task:expr, many, many) => {
288 impl_ppi_channel!($type, $number, $has_configurable_task, 1, 2);
289 impl crate::ppi::ManyToManyChannel for peripherals::$type {}
290 };
291}
292
293// ======================
294// groups
295
296pub struct AnyGroup {
297 number: u8,
298}
299unsafe_impl_unborrow!(AnyGroup);
300impl sealed::Group for AnyGroup {}
301impl Group for AnyGroup {
302 fn number(&self) -> usize {
303 self.number as usize
304 }
305}
306
307macro_rules! impl_group {
308 ($type:ident, $number:expr) => {
309 impl sealed::Group for peripherals::$type {}
310 impl Group for peripherals::$type {
311 fn number(&self) -> usize {
312 $number
313 }
314 }
315 };
316}
317
318impl_group!(PPI_GROUP0, 0);
319impl_group!(PPI_GROUP1, 1);
320impl_group!(PPI_GROUP2, 2);
321impl_group!(PPI_GROUP3, 3);
322#[cfg(not(feature = "nrf51"))]
323impl_group!(PPI_GROUP4, 4);
324#[cfg(not(feature = "nrf51"))]
325impl_group!(PPI_GROUP5, 5);
diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs
new file mode 100644
index 000000000..67d2086c3
--- /dev/null
+++ b/embassy-nrf/src/ppi/ppi.rs
@@ -0,0 +1,77 @@
1use super::{Channel, Event, Ppi, Task};
2use crate::pac;
3
4impl<'d, C: Channel + 'd, const EVENT_COUNT: usize, const TASK_COUNT: usize>
5 Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
6{
7 fn set_main_task(task: Option<&Task>, channel: usize) {
8 let r = unsafe { &*pac::PPI::ptr() };
9 if let Some(task) = task {
10 r.ch[channel]
11 .tep
12 .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
13 } else {
14 r.ch[channel].tep.write(|w| unsafe { w.bits(0) })
15 }
16 }
17
18 fn set_fork_task(task: Option<&Task>, channel: usize) {
19 let r = unsafe { &*pac::PPI::ptr() };
20 if let Some(task) = task {
21 r.fork[channel]
22 .tep
23 .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
24 } else {
25 r.fork[channel].tep.write(|w| unsafe { w.bits(0) })
26 }
27 }
28
29 fn set_event(event: Option<&Event>, channel: usize) {
30 let r = unsafe { &*pac::PPI::ptr() };
31 if let Some(event) = event {
32 r.ch[channel]
33 .eep
34 .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
35 } else {
36 r.ch[channel].eep.write(|w| unsafe { w.bits(0) })
37 }
38 }
39
40 /// Enables all tasks and events
41 pub(super) fn enable_all(tasks: &[Task], events: &[Event], channel: &C) {
42 // One configurable task, no fork
43 if channel.is_task_configurable() && TASK_COUNT == 1 {
44 Self::set_main_task(Some(&tasks[0]), channel.number());
45 }
46
47 // One configurable task, as fork
48 if !channel.is_task_configurable() && TASK_COUNT == 1 {
49 Self::set_fork_task(Some(&tasks[0]), channel.number());
50 }
51
52 // Two configurable tasks (main + fork)
53 if TASK_COUNT == 2 {
54 Self::set_main_task(Some(&tasks[0]), channel.number());
55 Self::set_fork_task(Some(&tasks[1]), channel.number());
56 }
57
58 if EVENT_COUNT == 1 {
59 Self::set_event(Some(&events[0]), channel.number());
60 }
61 }
62
63 /// Disable all tasks and events
64 pub(super) fn disable_all(&self) {
65 if self.ch.is_task_configurable() {
66 Self::set_main_task(None, self.ch.number());
67 }
68
69 if TASK_COUNT == 1 && !self.ch.is_task_configurable() || TASK_COUNT == 2 {
70 Self::set_fork_task(None, self.ch.number());
71 }
72
73 if EVENT_COUNT == 1 {
74 Self::set_event(None, self.ch.number());
75 }
76 }
77}