atsamd_hal/sercom/spi/impl_ehal/
dma.rs

1//! `embedded-hal` and `embedded-io` implementations for DMA-enabled [`Spi`]s
2
3use num_traits::{AsPrimitive, PrimInt};
4
5use crate::dmac::{channel, sram::DmacDescriptor, AnyChannel, Beat, Buffer, Ready};
6use crate::ehal::spi::SpiBus;
7use crate::sercom::dma::{
8    read_dma, read_dma_linked, write_dma, write_dma_linked, SercomPtr, SharedSliceBuffer,
9    SinkSourceBuffer,
10};
11
12use super::{
13    Capability, Config, DataWidth, Duplex, Error, MasterMode, OpMode, Receive, Sercom, Size, Slave,
14    Spi, Transmit, ValidConfig, ValidPads, Word,
15};
16
17impl<P, M, Z, D, R, T> Spi<Config<P, M, Z>, D, R, T>
18where
19    P: ValidPads,
20    M: OpMode,
21    Z: Size,
22    Config<P, M, Z>: ValidConfig,
23    D: Capability,
24    Z::Word: Beat,
25{
26    #[inline]
27    pub(in super::super) fn sercom_ptr(&self) -> SercomPtr<Z::Word> {
28        SercomPtr(self.config.regs.spi().data().as_ptr() as *mut _)
29    }
30}
31
32// Write implementation is the same for Master and Slave SPIs.
33impl<P, M, Z, D, R, T, S> Spi<Config<P, M, Z>, D, R, T>
34where
35    P: ValidPads,
36    M: OpMode,
37    Z: Size + 'static,
38    Config<P, M, Z>: ValidConfig<Sercom = S>,
39    D: Transmit,
40    S: Sercom,
41    Z::Word: PrimInt + AsPrimitive<DataWidth> + Beat,
42    DataWidth: AsPrimitive<Z::Word>,
43    T: AnyChannel<Status = Ready>,
44{
45    pub(super) fn write_dma(&mut self, buf: &[Z::Word]) -> Result<usize, Error> {
46        if buf.is_empty() {
47            return Ok(0);
48        }
49
50        // Ignore RX buffer overflows by disabling the receiver
51        self.config.as_mut().regs.rx_disable();
52
53        let sercom_ptr = self.sercom_ptr();
54        let tx = self._tx_channel.as_mut();
55        let mut words = crate::sercom::dma::SharedSliceBuffer::from_slice(buf);
56
57        // SAFETY: We make sure that any DMA transfer is complete or stopped before
58        // returning. The order of operations is important; the RX transfer
59        // must be ready to receive before the TX transfer is initiated.
60        unsafe {
61            crate::sercom::dma::write_dma::<_, _, S>(tx, sercom_ptr, &mut words);
62        }
63
64        while !tx.xfer_complete() {
65            core::hint::spin_loop();
66        }
67
68        // Defensively disable channels
69        tx.stop();
70
71        // Reenable receiver only if necessary
72        if D::RX_ENABLE {
73            self.config.as_mut().regs.rx_enable();
74        }
75
76        self._tx_channel.as_mut().xfer_success()?;
77        Ok(buf.len())
78    }
79}
80
81impl<P, M, S, C, D, R, T> Spi<Config<P, M, C>, D, R, T>
82where
83    Config<P, M, C>: ValidConfig<Sercom = S>,
84    S: Sercom,
85    P: ValidPads,
86    M: MasterMode,
87    C: Size + 'static,
88    C::Word: PrimInt + AsPrimitive<DataWidth> + Beat,
89    DataWidth: AsPrimitive<C::Word>,
90    D: Capability,
91    R: AnyChannel<Status = Ready>,
92    T: AnyChannel<Status = Ready>,
93{
94    #[inline]
95    fn transfer_blocking<Source: Buffer<Beat = C::Word>, Dest: Buffer<Beat = C::Word>>(
96        &mut self,
97        dest: &mut Dest,
98        source: &mut Source,
99    ) -> Result<(), Error> {
100        let sercom_ptr = self.sercom_ptr();
101        let rx = self._rx_channel.as_mut();
102        let tx = self._tx_channel.as_mut();
103
104        // SAFETY: We make sure that any DMA transfer is complete or stopped before
105        // returning. The order of operations is important; the RX transfer
106        // must be ready to receive before the TX transfer is initiated.
107        unsafe {
108            read_dma::<_, _, S>(rx, sercom_ptr.clone(), dest);
109            write_dma::<_, _, S>(tx, sercom_ptr, source);
110        }
111
112        while !(rx.xfer_complete() && tx.xfer_complete()) {
113            core::hint::spin_loop();
114        }
115
116        // Defensively disable channels
117        tx.stop();
118        rx.stop();
119
120        // Check for overflows or DMA errors
121        self.read_status().check_bus_error()?;
122        self._rx_channel
123            .as_mut()
124            .xfer_success()
125            .and(self._tx_channel.as_mut().xfer_success())?;
126        Ok(())
127    }
128
129    #[inline]
130    pub(super) fn read_dma_master(&mut self, mut words: &mut [C::Word]) -> Result<(), Error> {
131        if words.is_empty() {
132            return Ok(());
133        }
134
135        let mut source_word = self.config.nop_word.as_();
136        let mut source = SinkSourceBuffer::new(&mut source_word, words.len());
137
138        self.transfer_blocking(&mut words, &mut source)
139    }
140}
141
142/// [`SpiBus`] implementation for [`Spi`], using DMA transfers.
143impl<P, M, S, C, R, T> SpiBus<Word<C>> for Spi<Config<P, M, C>, Duplex, R, T>
144where
145    Config<P, M, C>: ValidConfig<Sercom = S>,
146    S: Sercom,
147    P: ValidPads,
148    M: MasterMode,
149    C: Size + 'static,
150    C::Word: PrimInt + AsPrimitive<DataWidth> + Beat,
151    DataWidth: AsPrimitive<C::Word>,
152    R: AnyChannel<Status = Ready>,
153    T: AnyChannel<Status = Ready>,
154{
155    #[inline]
156    fn read(&mut self, words: &mut [C::Word]) -> Result<(), Self::Error> {
157        self.read_dma_master(words)
158    }
159
160    #[inline]
161    fn write(&mut self, words: &[C::Word]) -> Result<(), Self::Error> {
162        self.write_dma(words)?;
163        Ok(())
164    }
165
166    #[inline]
167    fn transfer(&mut self, mut read: &mut [C::Word], write: &[C::Word]) -> Result<(), Self::Error> {
168        use core::cmp::Ordering;
169
170        // No work to do here
171        if write.is_empty() && read.is_empty() {
172            return Ok(());
173        }
174
175        // Handle 0-length special cases
176        if write.is_empty() {
177            return self.read_dma_master(read);
178        } else if read.is_empty() {
179            self.write_dma(write)?;
180            return Ok(());
181        }
182
183        // Reserve space for a DMAC SRAM descriptor if we need to make a linked
184        // transfer. Must not be dropped until all transfers have completed
185        // or have been stopped.
186        let mut linked_descriptor = DmacDescriptor::default();
187
188        // If read < write, the incoming words will be written to this memory location;
189        // it will be discarded after. If read > write, all writes after the
190        // buffer has been exhausted will write the nop word to "stimulate" the slave
191        // into sending data. Must not be dropped until all transfers have
192        // completed or have been stopped.
193        let mut source_sink_word = self.config.nop_word.as_();
194        let mut sercom_ptr = self.sercom_ptr();
195
196        let (read_link, write_link) = match read.len().cmp(&write.len()) {
197            Ordering::Equal => {
198                let mut write = SharedSliceBuffer::from_slice(write);
199                return self.transfer_blocking(&mut read, &mut write);
200            }
201
202            // `read` is shorter; link transfer to sink incoming words after the buffer has been
203            // filled.
204            Ordering::Less => {
205                let mut sink =
206                    SinkSourceBuffer::new(&mut source_sink_word, write.len() - read.len());
207                unsafe {
208                    channel::write_descriptor(
209                        &mut linked_descriptor,
210                        &mut sercom_ptr,
211                        &mut sink,
212                        // Add a null descriptor pointer to end the transfer.
213                        core::ptr::null_mut(),
214                    );
215                }
216
217                (Some(&mut linked_descriptor), None)
218            }
219
220            // `write` is shorter; link transfer to send NOP word after the buffer has been
221            // exhausted.
222            Ordering::Greater => {
223                let mut source =
224                    SinkSourceBuffer::new(&mut source_sink_word, read.len() - write.len());
225                unsafe {
226                    channel::write_descriptor(
227                        &mut linked_descriptor,
228                        &mut source,
229                        &mut sercom_ptr,
230                        // Add a null descriptor pointer to end the transfer.
231                        core::ptr::null_mut(),
232                    );
233                }
234
235                (None, Some(&mut linked_descriptor))
236            }
237        };
238
239        let rx = self._rx_channel.as_mut();
240        let tx = self._tx_channel.as_mut();
241
242        let mut write = SharedSliceBuffer::from_slice(write);
243
244        // SAFETY: We make sure that any DMA transfer is complete or stopped before
245        // returning. The order of operations is important; the RX transfer
246        // must be ready to receive before the TX transfer is initiated.
247        unsafe {
248            read_dma_linked::<_, _, S>(rx, sercom_ptr.clone(), &mut read, read_link);
249            write_dma_linked::<_, _, S>(tx, sercom_ptr, &mut write, write_link);
250        }
251
252        while !(rx.xfer_complete() && tx.xfer_complete()) {
253            core::hint::spin_loop();
254        }
255
256        // Defensively disable channels
257        tx.stop();
258        rx.stop();
259
260        // Check for overflows or DMA errors
261        self.read_status().check_bus_error()?;
262        self._rx_channel
263            .as_mut()
264            .xfer_success()
265            .and(self._tx_channel.as_mut().xfer_success())?;
266        Ok(())
267    }
268
269    #[inline]
270    fn transfer_in_place(&mut self, words: &mut [C::Word]) -> Result<(), Self::Error> {
271        // Safety: Aliasing the buffer is only safe because the DMA read will always be
272        // lagging one word behind the write, so they don't overlap on the same memory.
273        // It's preferable to use two `SharedSliceBuffer`s here; using the `words` slice
274        // directly as a buffer could potentially cause UB issues if not careful when
275        // aliasing, as it could be easy to create two `&mut` references pointing to the
276        // same buffer. `read_buf` and `write_buf` may only be read/written to by the
277        // DMAC, otherwise an `UnsafeCell` would be necessary.
278        unsafe {
279            let mut read_buf = SharedSliceBuffer::from_slice_unchecked(words);
280            let mut write_buf = SharedSliceBuffer::from_slice(words);
281            self.transfer_blocking(&mut read_buf, &mut write_buf)
282        }
283    }
284
285    #[inline]
286    fn flush(&mut self) -> Result<(), Error> {
287        self.flush_tx();
288        Ok(())
289    }
290}
291
292/// [`embedded_io::Write`] implementation for [`Transmit`] [`Spi`]s in either
293/// [`Slave`] or [`MasterMode`], using DMA transfers.
294impl<P, M, Z, D, R, T, S> embedded_io::Write for Spi<Config<P, M, Z>, D, R, T>
295where
296    P: ValidPads,
297    M: OpMode,
298    Z: Size<Word = u8> + 'static,
299    Config<P, M, Z>: ValidConfig<Sercom = S>,
300    D: Transmit,
301    S: Sercom,
302    T: AnyChannel<Status = Ready>,
303{
304    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
305        Spi::write_dma(self, buf)
306    }
307
308    fn flush(&mut self) -> Result<(), Self::Error> {
309        self.flush_tx();
310        Ok(())
311    }
312}
313
314/// [`embedded_io::Read`] implementation for [`Receive`] [`Spi`]s in
315/// [`MasterMode`], using DMA transfers.
316impl<P, M, Z, D, R, T, S> embedded_io::Read for Spi<Config<P, M, Z>, D, R, T>
317where
318    P: ValidPads,
319    M: MasterMode,
320    Z: Size<Word = u8> + 'static,
321    Config<P, M, Z>: ValidConfig<Sercom = S>,
322    D: Receive,
323    S: Sercom,
324    R: AnyChannel<Status = Ready>,
325    T: AnyChannel<Status = Ready>,
326{
327    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
328        self.read_dma_master(buf)?;
329        Ok(buf.len())
330    }
331}
332
333/// [`embedded_io::Read`] implementation for [`Receive`] [`Spi`]s in [`Slave`]
334/// mode, using DMA transfers.
335impl<P, Z, D, R, T, S> embedded_io::Read for Spi<Config<P, Slave, Z>, D, R, T>
336where
337    P: ValidPads,
338    Z: Size<Word = u8> + 'static,
339    Config<P, Slave, Z>: ValidConfig<Sercom = S>,
340    D: Receive,
341    S: Sercom,
342    R: AnyChannel<Status = Ready>,
343{
344    fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Self::Error> {
345        if buf.is_empty() {
346            return Ok(0);
347        }
348
349        // In Slave mode, RX words can come in even if we haven't sent anything. This
350        // means some words can arrive asynchronously while we weren't looking (similar
351        // to UART RX). We need to check if we haven't missed any.
352        self.flush_rx()?;
353        let sercom_ptr = self.sercom_ptr();
354        let rx = self._rx_channel.as_mut();
355
356        // SAFETY: We make sure that any DMA transfer is complete or stopped before
357        // returning.
358        unsafe {
359            read_dma::<_, _, S>(rx, sercom_ptr.clone(), &mut buf);
360        }
361
362        while !(rx.xfer_complete()) {
363            core::hint::spin_loop();
364        }
365
366        // Defensively disable channel
367        rx.stop();
368
369        // Check for overflows or DMA errors
370        self.read_status().check_bus_error()?;
371        self._rx_channel.as_mut().xfer_success()?;
372        Ok(buf.len())
373    }
374}