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}