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,
}