imxrt_dma/peripheral.rs
1//! DMA support for hardware peripherals.
2//!
3//! If a driver is compatible with this API, it implements some or all of
4//! the traits in this module. Consult your HAL for more information.
5//!
6//! Each future documents when it resolves. To wake the executor, you can
7//! route the DMA channel's interrupt handler to [`on_interrupt()`](crate::Dma::on_interrupt).
8//! Otherwise, you can poll the future in a loop.
9
10use super::{
11 channel::{self, Channel, Configuration},
12 Element, Error, Transfer,
13};
14
15use core::{
16 future::Future,
17 marker::PhantomData,
18 pin::Pin,
19 task::{Context, Poll},
20};
21
22/// A peripheral that can be the source of DMA data
23///
24/// By 'source,' we mean that it provides data for a DMA transfer.
25/// A source would be a hardware device writing data into memory,
26/// like a UART receiver.
27///
28/// # Safety
29///
30/// `Source` should only be implemented on peripherals that are
31/// DMA capable. This trait should be implemented by HAL authors
32/// who are exposing DMA capable peripherals.
33pub unsafe trait Source<E: Element> {
34 /// Peripheral source request signal
35 ///
36 /// See Table 4-3 of the reference manual. A source may
37 /// has a qualifier like 'receive' in the name.
38 fn source_signal(&self) -> u32;
39 /// Returns a pointer to the register from which the DMA channel
40 /// reads data
41 ///
42 /// This is the register that software reads to acquire data from
43 /// a device. The type of the pointer describes the type of reads
44 /// the DMA channel performs when transferring data.
45 ///
46 /// This memory is assumed to be static. Repeated `source` calls
47 /// should always return the same address.
48 fn source_address(&self) -> *const E;
49 /// Perform any actions necessary to enable DMA transfers
50 ///
51 /// Callers use this method to put the peripheral in a state where
52 /// it can supply the DMA channel with data.
53 fn enable_source(&mut self);
54 /// Perform any actions necessary to disable or cancel DMA transfers
55 ///
56 /// This may include undoing the actions in `enable_source`.
57 fn disable_source(&mut self);
58}
59
60/// A peripheral that can be the destination for DMA data
61///
62/// By 'destination,' we mean that it receives data from a DMA transfer.
63/// A destination would be a peripheral that could send data out of
64/// processor memory, like a UART transmitter.
65///
66/// # Safety
67///
68/// `Destination` should only be implemented on peripherals that are
69/// DMA capable. This trait should be implemented by HAL authors
70/// who are exposing DMA capable peripherals.
71pub unsafe trait Destination<E: Element> {
72 /// Peripheral destination request signal
73 ///
74 /// See Table 4-3 of the reference manual. A destination mave
75 /// has a qualifier like 'transfer' in the name.
76 fn destination_signal(&self) -> u32;
77 /// Returns a pointer to the register into which the DMA channel
78 /// writes data
79 ///
80 /// This is the register that software writes to when sending data to a
81 /// device. The type of the pointer describes the type of reads the
82 /// DMA channel performs when transferring data.
83 fn destination_address(&self) -> *const E;
84 /// Perform any actions necessary to enable DMA transfers
85 ///
86 /// Callers use this method to put the peripheral into a state where
87 /// it can accept transfers from a DMA channel.
88 fn enable_destination(&mut self);
89 /// Perform any actions necessary to disable or cancel DMA transfers
90 ///
91 /// This may include undoing the actions in `enable_destination`.
92 fn disable_destination(&mut self);
93}
94
95/// A DMA transfer that receives data from hardware
96///
97/// The future resolves when the peripheral has provided all
98/// expected data. Use [`read()`](crate::peripheral::read) to construct
99/// this future.
100pub struct Read<'a, S, E>
101where
102 S: Source<E>,
103 E: Element,
104{
105 channel: &'a Channel,
106 source: &'a mut S,
107 transfer: Transfer<'a>,
108 _elem: PhantomData<&'a mut E>,
109}
110
111impl<S, E> Future for Read<'_, S, E>
112where
113 S: Source<E>,
114 E: Element,
115{
116 type Output = Result<(), Error>;
117 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
118 // Safety: no movement from transfer future...
119 unsafe { self.map_unchecked_mut(|this| &mut this.transfer) }.poll(cx)
120 }
121}
122
123impl<S, E> Drop for Read<'_, S, E>
124where
125 S: Source<E>,
126 E: Element,
127{
128 fn drop(&mut self) {
129 self.source.disable_source();
130 while self.channel.is_hardware_signaling() {}
131 // Drop `transfer` to finish cancellation...
132 }
133}
134
135fn prepare_read<S, E>(channel: &mut Channel, source: &mut S, buffer: &mut [E])
136where
137 S: Source<E>,
138 E: Element,
139{
140 channel.disable();
141
142 channel.set_disable_on_completion(true);
143 channel.set_channel_configuration(Configuration::enable(source.source_signal()));
144 // Safety: hardware source address must be valid, otherwise impl is unsound.
145 // Destination buffer lifetime captured by future. The combination of minor
146 // loops and transfer iterations ensure that we do not exceed the end of the
147 // destination.
148 unsafe {
149 channel::set_source_hardware(channel, source.source_address());
150 channel::set_destination_linear_buffer(channel, buffer);
151 channel.set_minor_loop_bytes(core::mem::size_of::<E>() as u32);
152 channel.set_transfer_iterations(buffer.len() as u16);
153 }
154
155 source.enable_source();
156}
157
158/// Use a DMA channel to receive a `buffer` of elements from the source peripheral.
159///
160/// Consider using a DMA interrupt handler that calls [`on_interrupt()`](crate::Dma::on_interrupt)
161/// to wake the executor when the transfer completes. Otherwise, poll the future.
162///
163/// # Example
164///
165/// Receive 32 bytes from a LPUART peripheral. Wake the executor when the transfer completes.
166///
167/// ```no_run
168/// use imxrt_dma::{peripheral, channel::Channel};
169/// # static DMA: imxrt_dma::Dma<32> = unsafe { imxrt_dma::Dma::new(core::ptr::null(), core::ptr::null()) };
170/// # struct X;
171/// # unsafe impl peripheral::Source<u8> for X {
172/// # fn source_signal(&self) -> u32 { 0 }
173/// # fn source_address(&self) -> *const u8 { panic!() }
174/// # fn enable_source(&mut self) { panic!() }
175/// # fn disable_source(&mut self) { panic!() }
176/// # }
177///
178/// // #[cortex_m_rt::interrupt]
179/// fn DMA7() {
180/// // Safety: DMA channel 7 valid and used by a future.
181/// unsafe { DMA.on_interrupt(7) };
182/// }
183///
184/// # async fn f() -> imxrt_dma::Result<()> {
185/// let mut lpuart = // A LPUART peripheral
186/// # X;
187/// let mut channel_7: Channel = // DMA channel 7
188/// # unsafe { DMA.channel(7) };
189/// channel_7.set_interrupt_on_completion(true);
190/// // TODO unmask interrupts in NVIC!
191///
192/// let mut buffer = [0u8; 32];
193///
194/// peripheral::read(
195/// &mut channel_7,
196/// &mut lpuart,
197/// &mut buffer,
198/// ).await?;
199/// # Ok(()) }
200/// ```
201pub fn read<'a, S, E>(
202 channel: &'a mut Channel,
203 source: &'a mut S,
204 buffer: &'a mut [E],
205) -> Read<'a, S, E>
206where
207 S: Source<E>,
208 E: Element,
209{
210 prepare_read(channel, source, buffer);
211 Read {
212 channel,
213 // Safety: transfer is correctly defined
214 transfer: unsafe { Transfer::new(channel) },
215 source,
216 _elem: PhantomData,
217 }
218}
219
220/// A DMA transfer that sends data to hardware
221///
222/// The future resolves when the device has sent all provided data.
223/// Use [`write()`](crate::peripheral::write) to construct this future.
224pub struct Write<'a, D, E>
225where
226 D: Destination<E>,
227 E: Element,
228{
229 channel: &'a Channel,
230 destination: &'a mut D,
231 transfer: Transfer<'a>,
232 _elem: PhantomData<&'a E>,
233}
234
235impl<D, E> Future for Write<'_, D, E>
236where
237 D: Destination<E>,
238 E: Element,
239{
240 type Output = Result<(), Error>;
241 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
242 // Safety: no movement from transfer future...
243 unsafe { self.map_unchecked_mut(|this| &mut this.transfer) }.poll(cx)
244 }
245}
246
247impl<D, E> Drop for Write<'_, D, E>
248where
249 D: Destination<E>,
250 E: Element,
251{
252 fn drop(&mut self) {
253 self.destination.disable_destination();
254 while self.channel.is_hardware_signaling() {}
255 // Drop `transfer` to finish cancellation...
256 }
257}
258
259fn prepare_write<D, E>(channel: &mut Channel, buffer: &[E], destination: &mut D)
260where
261 D: Destination<E>,
262 E: Element,
263{
264 channel.disable();
265 channel.set_disable_on_completion(true);
266 channel.set_channel_configuration(Configuration::enable(destination.destination_signal()));
267 // Safety: hardware address must be valid, otherwise impl is unsound.
268 // Source buffer lifetime captured by future. The combination of minor
269 // loops and transfer iterations ensure that we do not exceed the end of the
270 // source.
271 unsafe {
272 channel::set_source_linear_buffer(channel, buffer);
273 channel::set_destination_hardware(channel, destination.destination_address());
274 channel.set_minor_loop_bytes(core::mem::size_of::<E>() as u32);
275 channel.set_transfer_iterations(buffer.len() as u16);
276 }
277
278 destination.enable_destination();
279}
280
281/// Use a DMA channel to send a `buffer` of data to the destination peripheral.
282///
283/// Consider using a DMA interrupt handler that calls [`on_interrupt()`](crate::Dma::on_interrupt)
284/// to wake the executor when the transfer completes. Otherwise, poll the future.
285///
286/// # Example
287///
288/// Send five bytes to a LPUART device. Wake the executor when the transfer completes.
289///
290/// ```no_run
291/// use imxrt_dma::{peripheral, channel::Channel};
292/// # static DMA: imxrt_dma::Dma<32> = unsafe { imxrt_dma::Dma::new(core::ptr::null(), core::ptr::null()) };
293/// # struct X;
294/// # unsafe impl peripheral::Destination<u8> for X {
295/// # fn destination_signal(&self) -> u32 { 0 }
296/// # fn destination_address(&self) -> *const u8 { panic!() }
297/// # fn enable_destination(&mut self) { panic!() }
298/// # fn disable_destination(&mut self) { panic!() }
299/// # }
300///
301/// // #[cortex_m_rt::interrupt]
302/// fn DMA7() {
303/// // Safety: DMA channel 7 valid and used by a future.
304/// unsafe { DMA.on_interrupt(7) };
305/// }
306///
307/// # async fn f() -> imxrt_dma::Result<()> {
308/// let mut lpuart = // A LPUART peripheral
309/// # X;
310/// let mut channel_7: Channel = // DMA channel 7
311/// # unsafe { DMA.channel(7) };
312///
313/// channel_7.set_interrupt_on_completion(true);
314/// // TODO unmask interrupts in NVIC!
315///
316/// let buffer = [4u8, 5, 6, 7, 8];
317///
318/// peripheral::write(
319/// &mut channel_7,
320/// &buffer,
321/// &mut lpuart,
322/// ).await?;
323/// # Ok(()) }
324/// ```
325pub fn write<'a, D, E>(
326 channel: &'a mut Channel,
327 buffer: &'a [E],
328 destination: &'a mut D,
329) -> Write<'a, D, E>
330where
331 D: Destination<E>,
332 E: Element,
333{
334 prepare_write(channel, buffer, destination);
335 Write {
336 channel,
337 destination,
338 // Safety: transfer is correctly defined
339 transfer: unsafe { Transfer::new(channel) },
340 _elem: PhantomData,
341 }
342}
343
344/// Indicates that a peripheral can read and write from a single buffer
345/// using two simultaneous DMA transfers
346///
347/// It's expected that the TX operation will drive the RX operation.
348/// A SPI transfer can be modeled in this manner.
349///
350/// # Safety
351///
352/// `Bidirectional` assumes the same safety requirements as source and
353/// destination. Addtionally, you ensure that the peripheral is capable
354/// of this kind of transfer from a single buffer.
355pub unsafe trait Bidirectional<E: Element>: Source<E> + Destination<E> {}
356
357/// A full-duplex DMA transfer from a single buffer
358///
359/// `FullDuplex` only works with [`Bidirectional`](crate::peripheral::Bidirectional)
360/// peripherals. The transfer acts on a single buffer, sending and receiving data
361/// element by element. It yields when all elements are sent and received.
362///
363/// To create this future, use [`full_duplex()`].
364pub struct FullDuplex<'a, P, E>
365where
366 P: Bidirectional<E>,
367 E: Element,
368{
369 rx_channel: &'a Channel,
370 rx_transfer: Transfer<'a>,
371 rx_done: bool,
372 tx_channel: &'a Channel,
373 tx_transfer: Transfer<'a>,
374 tx_done: bool,
375 peripheral: &'a mut P,
376 _elem: PhantomData<E>,
377}
378
379/// Perform a full-duplex DMA transfer using two DMA channels
380/// that read and write from a single buffer.
381///
382/// Consider using a DMA interrupt handler that calls [`on_interrupt()`](crate::Dma::on_interrupt)
383/// to wake the executor when the transfer completes. Otherwise, poll the future.
384///
385/// # Example
386///
387/// Perform a full-duplex transfer of five `u32`s with a LPSPI peripheral. Generate an interrupt
388/// after receiving the final LPSPI word.
389///
390/// ```no_run
391/// use imxrt_dma::{peripheral, channel::Channel};
392/// # static DMA: imxrt_dma::Dma<32> = unsafe { imxrt_dma::Dma::new(core::ptr::null(), core::ptr::null()) };
393/// # struct X;
394/// # unsafe impl peripheral::Source<u32> for X {
395/// # fn source_signal(&self) -> u32 { 0 }
396/// # fn source_address(&self) -> *const u32 { panic!() }
397/// # fn enable_source(&mut self) { panic!() }
398/// # fn disable_source(&mut self) { panic!() }
399/// # }
400/// # unsafe impl peripheral::Destination<u32> for X {
401/// # fn destination_signal(&self) -> u32 { 0 }
402/// # fn destination_address(&self) -> *const u32 { panic!() }
403/// # fn enable_destination(&mut self) { panic!() }
404/// # fn disable_destination(&mut self) { panic!() }
405/// # }
406/// # unsafe impl peripheral::Bidirectional<u32> for X {}
407///
408/// // #[cortex_m_rt::interrupt]
409/// fn DMA7() {
410/// // Safety: DMA channel 7 valid and used by a future.
411/// unsafe { DMA.on_interrupt(7) };
412/// }
413///
414/// # async fn f() -> imxrt_dma::Result<()> {
415/// let mut lpspi = // A LPSPI peripheral
416/// # X;
417/// let mut channel_7: Channel = // DMA channel 7
418/// # unsafe { DMA.channel(7) };
419/// let mut channel_8: Channel = // DMA channel 8
420/// # unsafe { DMA.channel(8) };
421///
422/// // Using channel_7 for the receive data. Once we've received
423/// // the last word from the LPSPI peripheral, generate an interrupt.
424/// channel_7.set_interrupt_on_completion(true);
425/// // Don't trigger an interrupt after we _send_ the last LPSPI
426/// // word from memory; we're still waiting for one word response.
427/// channel_8.set_interrupt_on_completion(false);
428/// // TODO unmask interrupts in NVIC!
429///
430/// let mut buffer = [4u32, 5, 6, 7, 8];
431///
432/// peripheral::full_duplex(
433/// &mut channel_7,
434/// &mut channel_8,
435/// &mut lpspi,
436/// &mut buffer,
437/// ).await?;
438/// # Ok(()) }
439/// ```
440pub fn full_duplex<'a, P, E>(
441 rx_channel: &'a mut Channel,
442 tx_channel: &'a mut Channel,
443 peripheral: &'a mut P,
444 buffer: &'a mut [E],
445) -> FullDuplex<'a, P, E>
446where
447 P: Bidirectional<E>,
448 E: Element,
449{
450 prepare_write(tx_channel, buffer, peripheral);
451 prepare_read(rx_channel, peripheral, buffer);
452
453 FullDuplex {
454 rx_channel,
455 rx_transfer: unsafe { Transfer::new(rx_channel) },
456 rx_done: false,
457 tx_channel,
458 tx_transfer: unsafe { Transfer::new(tx_channel) },
459 tx_done: false,
460 peripheral,
461 _elem: PhantomData,
462 }
463}
464
465impl<P, E> Future for FullDuplex<'_, P, E>
466where
467 P: Bidirectional<E>,
468 E: Element,
469{
470 type Output = Result<(), Error>;
471
472 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
473 if !self.rx_done {
474 // Safety: pin projection OK, no movement from future...
475 let poll = unsafe {
476 self.as_mut()
477 .map_unchecked_mut(|this| &mut this.rx_transfer)
478 }
479 .poll(cx)?;
480 // Safety: OK to toggle a bool...
481 *unsafe { &mut self.as_mut().get_unchecked_mut().rx_done } = poll.is_ready();
482 }
483
484 if !self.tx_done {
485 // Safety: pin projection OK, no movement from future...
486 let poll = unsafe {
487 self.as_mut()
488 .map_unchecked_mut(|this| &mut this.tx_transfer)
489 }
490 .poll(cx)?;
491 // Safety: OK to toggle a bool...
492 *unsafe { &mut self.as_mut().get_unchecked_mut().tx_done } = poll.is_ready();
493 }
494
495 if self.tx_done && self.rx_done {
496 Poll::Ready(Ok(()))
497 } else {
498 Poll::Pending
499 }
500 }
501}
502
503impl<P, E> Drop for FullDuplex<'_, P, E>
504where
505 P: Bidirectional<E>,
506 E: Element,
507{
508 fn drop(&mut self) {
509 self.peripheral.disable_destination();
510 self.peripheral.disable_source();
511 while self.tx_channel.is_hardware_signaling() {}
512 while self.rx_channel.is_hardware_signaling() {}
513 // Drop the transfers to finish cancellation...
514 }
515}