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
//! Direct memory access (DMA).
//!
//! The DMA unit of the RP2040 seems very simplistic at first when compared to other MCUs. For
//! example, the individual DMA channels do not support chaining multiple buffers. However, within
//! certain limits, the DMA engine supports a wide range of transfer and buffer types, often by
//! combining multiple DMA channels:
//!
//! * Simple RX/TX transfers filling a single buffer or transferring data from one peripheral to
//!   another.
//! * RX/TX transfers that use multiple chained buffers: These transfers require two channels to
//!   be combined, where the first DMA channel configures the second DMA channel. An example for
//!   this transfer type can be found in the datasheet.
//! * Repeated transfers from/to a set of buffers: By allocating one channel per buffer and
//!   chaining the channels together, continuous transfers to a set of ring buffers can be
//!   achieved. Note, however, that the MCU manually needs to reconfigure the DMA units unless the
//!   buffer addresses and sizes are aligned, in which case the ring buffer functionality of the
//!   DMA engine can be used. Even then, however, at least two DMA channels are required as a
//!   channel cannot be chained to itself.
//!
//! This API tries to provide three types of buffers: Single buffers, double-buffered transfers
//! where the user can specify the next buffer while the previous is being transferred, and
//! automatic continuous ring buffers consisting of two aligned buffers being read or written
//! alternatingly.

use core::marker::PhantomData;
use embedded_dma::{ReadBuffer, WriteBuffer};

use crate::{
    pac::{self, DMA},
    resets::SubsystemReset,
    typelevel::Sealed,
};
// Export these types for easier use by external code
pub use crate::dma::single_channel::SingleChannel;

// Bring in our submodules
pub mod bidirectional;
pub mod double_buffer;
pub mod single_buffer;
mod single_channel;

/// DMA unit.
pub trait DMAExt: Sealed {
    /// Splits the DMA unit into its individual channels.
    fn split(self, resets: &mut pac::RESETS) -> Channels;
    /// Splits the DMA unit into its individual channels with runtime ownership
    fn dyn_split(self, resets: &mut pac::RESETS) -> DynChannels;
}

/// DMA channel.
pub struct Channel<CH: ChannelIndex> {
    _phantom: PhantomData<CH>,
}

/// DMA channel identifier.
pub trait ChannelIndex: Sealed {
    /// Numerical index of the DMA channel (0..11).
    fn id() -> u8;
}

macro_rules! channels {
    (
        $($CHX:ident: ($chX:ident, $x:expr),)+
    ) => {
        impl DMAExt for DMA {
            fn split(self, resets: &mut pac::RESETS) -> Channels {
                self.reset_bring_down(resets);
                self.reset_bring_up(resets);

                Channels {
                    $(
                        $chX: Channel {
                            _phantom: PhantomData,
                        },
                    )+
                }
            }

            fn dyn_split(self, resets: &mut pac::RESETS) -> DynChannels{
                self.reset_bring_down(resets);
                self.reset_bring_up(resets);

                DynChannels {
                    $(
                        $chX: Some(Channel {
                            _phantom: PhantomData,
                        }),
                    )+
                }
            }
        }

        impl Sealed for DMA {}

        /// Set of DMA channels.
        pub struct Channels {
            $(
                /// DMA channel.
                pub $chX: Channel<$CHX>,
            )+
        }
        $(
            /// DMA channel identifier.
            pub struct $CHX;
            impl ChannelIndex for $CHX {
                fn id() -> u8 {
                    $x
                }
            }

            impl Sealed for $CHX {}
        )+

        /// Set of DMA channels with runtime ownership.
        pub struct DynChannels {
            $(
                /// DMA channel.
                pub $chX: Option<Channel<$CHX>>,
            )+
        }
    }
}

channels! {
    CH0: (ch0, 0),
    CH1: (ch1, 1),
    CH2: (ch2, 2),
    CH3: (ch3, 3),
    CH4: (ch4, 4),
    CH5: (ch5, 5),
    CH6: (ch6, 6),
    CH7: (ch7, 7),
    CH8: (ch8, 8),
    CH9: (ch9, 9),
    CH10:(ch10, 10),
    CH11:(ch11, 11),
}

trait ChannelRegs {
    unsafe fn ptr() -> *const pac::dma::CH;
    fn regs(&self) -> &pac::dma::CH;
}

impl<CH: ChannelIndex> ChannelRegs for Channel<CH> {
    unsafe fn ptr() -> *const pac::dma::CH {
        (*pac::DMA::ptr()).ch(CH::id() as usize)
    }

    fn regs(&self) -> &pac::dma::CH {
        unsafe { &*Self::ptr() }
    }
}

/// Trait which is implemented by anything that can be read via DMA.
///
/// # Safety
///
/// The implementing type must be safe to use for DMA reads. This means:
///
/// - The range returned by rx_address_count must point to a valid address,
///   and if rx_increment is true, count must fit into the allocated buffer.
/// - As long as no `&mut self` method is called on the implementing object:
///   - `rx_address_count` must always return the same value, if called multiple
///     times.
///   - The memory specified by the pointer and size returned by `rx_address_count`
///     must not be freed during the transfer it is used in as long as `self` is not dropped.
pub unsafe trait ReadTarget {
    /// Type which is transferred in a single DMA transfer.
    type ReceivedWord;

    /// Returns the DREQ number for this data source (`None` for memory buffers).
    fn rx_treq() -> Option<u8>;

    /// Returns the address and the maximum number of words that can be transferred from this data
    /// source in a single DMA operation.
    ///
    /// For peripherals, the count should likely be u32::MAX. If a data source implements
    /// EndlessReadTarget, it is suitable for infinite transfers from or to ring buffers. Note that
    /// ring buffers designated for endless transfers, but with a finite buffer size, should return
    /// the size of their individual buffers here.
    ///
    /// # Safety
    ///
    /// This function has the same safety guarantees as `ReadBuffer::read_buffer`.
    fn rx_address_count(&self) -> (u32, u32);

    /// Returns whether the address shall be incremented after each transfer.
    fn rx_increment(&self) -> bool;
}

/// Marker which signals that `rx_address_count()` can be called multiple times.
///
/// The DMA code will never call `rx_address_count()` to request more than two buffers to configure
/// two DMA channels. In the case of peripherals, the function can always return the same values.
pub trait EndlessReadTarget: ReadTarget {}

/// Safety: ReadBuffer and ReadTarget have the same safety requirements.
unsafe impl<B: ReadBuffer> ReadTarget for B {
    type ReceivedWord = <B as ReadBuffer>::Word;

    fn rx_treq() -> Option<u8> {
        None
    }

    fn rx_address_count(&self) -> (u32, u32) {
        let (ptr, len) = unsafe { self.read_buffer() };
        (ptr as u32, len as u32)
    }

    fn rx_increment(&self) -> bool {
        true
    }
}

/// Trait which is implemented by anything that can be written via DMA.
///
/// # Safety
///
/// The implementing type must be safe to use for DMA writes. This means:
///
/// - The range returned by tx_address_count must point to a valid address,
///   and if tx_increment is true, count must fit into the allocated buffer.
/// - As long as no other `&mut self` method is called on the implementing object:
///   - `tx_address_count` must always return the same value, if called multiple
///     times.
///   - The memory specified by the pointer and size returned by `tx_address_count`
///     must not be freed during the transfer it is used in as long as `self` is not dropped.
pub unsafe trait WriteTarget {
    /// Type which is transferred in a single DMA transfer.
    type TransmittedWord;

    /// Returns the DREQ number for this data sink (`None` for memory buffers).
    fn tx_treq() -> Option<u8>;

    /// Returns the address and the maximum number of words that can be transferred from this data
    /// source in a single DMA operation.
    ///
    /// See `ReadTarget::rx_address_count` for a complete description of the semantics of this
    /// function.
    fn tx_address_count(&mut self) -> (u32, u32);

    /// Returns whether the address shall be incremented after each transfer.
    fn tx_increment(&self) -> bool;
}

/// Marker which signals that `tx_address_count()` can be called multiple times.
///
/// The DMA code will never call `tx_address_count()` to request more than two buffers to configure
/// two DMA channels. In the case of peripherals, the function can always return the same values.
pub trait EndlessWriteTarget: WriteTarget {}

/// Safety: WriteBuffer and WriteTarget have the same safety requirements.
unsafe impl<B: WriteBuffer> WriteTarget for B {
    type TransmittedWord = <B as WriteBuffer>::Word;

    fn tx_treq() -> Option<u8> {
        None
    }

    fn tx_address_count(&mut self) -> (u32, u32) {
        let (ptr, len) = unsafe { self.write_buffer() };
        (ptr as u32, len as u32)
    }

    fn tx_increment(&self) -> bool {
        true
    }
}

/// Pacing for DMA transfers.
///
/// Generally, while memory-to-memory DMA transfers can operate at maximum possible throughput,
/// transfers involving peripherals commonly have to wait for data to be available or for available
/// space in write queues. This type defines whether the sink or the source shall pace the transfer
/// for peripheral-to-peripheral transfers.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Pace {
    /// The DREQ signal from the source is used, if available. If not, the sink's DREQ signal is
    /// used.
    PreferSource,
    /// The DREQ signal from the sink is used, if available. If not, the source's DREQ signal is
    /// used.
    PreferSink,
    // TODO: Timers?
}

/// Error during DMA configuration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DMAError {
    /// Buffers were not aligned to their size even though they needed to be.
    Alignment,
    /// An illegal configuration (i.e., buffer sizes not suitable for a memory-to-memory transfer)
    /// was specified.
    IllegalConfig,
}