aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/dma
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-04-26 23:57:26 +0200
committerDario Nieuwenhuis <[email protected]>2022-04-27 01:16:14 +0200
commit009bb8e4e1b7afbe9d9d7d89135f8d4dd3c4e808 (patch)
treed734f3e82f9fb3c22b7517a70e1f47969624d248 /embassy-stm32/src/dma
parenta39d796c3de9c96ea4df6b9da525cb0d5ef60fc0 (diff)
stm32: add stm32u5 GPDMA, SPIv4 support, add HIL tests.
Diffstat (limited to 'embassy-stm32/src/dma')
-rw-r--r--embassy-stm32/src/dma/gpdma.rs293
-rw-r--r--embassy-stm32/src/dma/mod.rs29
2 files changed, 318 insertions, 4 deletions
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs
new file mode 100644
index 000000000..0cb986b32
--- /dev/null
+++ b/embassy-stm32/src/dma/gpdma.rs
@@ -0,0 +1,293 @@
1use core::sync::atomic::{fence, Ordering};
2use core::task::Waker;
3
4use embassy::interrupt::{Interrupt, InterruptExt};
5use embassy::waitqueue::AtomicWaker;
6
7use crate::_generated::GPDMA_CHANNEL_COUNT;
8use crate::interrupt;
9use crate::pac;
10use crate::pac::gpdma::{vals, Gpdma};
11
12use super::{Request, TransferOptions, Word, WordSize};
13
14impl From<WordSize> for vals::ChTr1Dw {
15 fn from(raw: WordSize) -> Self {
16 match raw {
17 WordSize::OneByte => Self::BYTE,
18 WordSize::TwoBytes => Self::HALFWORD,
19 WordSize::FourBytes => Self::WORD,
20 }
21 }
22}
23
24struct ChannelState {
25 waker: AtomicWaker,
26}
27
28impl ChannelState {
29 const fn new() -> Self {
30 Self {
31 waker: AtomicWaker::new(),
32 }
33 }
34}
35
36struct State {
37 channels: [ChannelState; GPDMA_CHANNEL_COUNT],
38}
39
40impl State {
41 const fn new() -> Self {
42 const CH: ChannelState = ChannelState::new();
43 Self {
44 channels: [CH; GPDMA_CHANNEL_COUNT],
45 }
46 }
47}
48
49static STATE: State = State::new();
50
51/// safety: must be called only once
52pub(crate) unsafe fn init() {
53 foreach_interrupt! {
54 ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => {
55 interrupt::$irq::steal().enable();
56 };
57 }
58 crate::_generated::init_gpdma();
59}
60
61foreach_dma_channel! {
62 ($channel_peri:ident, $dma_peri:ident, gpdma, $channel_num:expr, $index:expr, $dmamux:tt) => {
63 impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri {
64 unsafe fn start_write<W: Word>(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) {
65 let (ptr, len) = super::slice_ptr_parts(buf);
66 low_level_api::start_transfer(
67 pac::$dma_peri,
68 $channel_num,
69 request,
70 low_level_api::Dir::MemoryToPeripheral,
71 reg_addr as *const u32,
72 ptr as *mut u32,
73 len,
74 true,
75 W::bits(),
76 options,
77 )
78 }
79
80 unsafe fn start_write_repeated<W: Word>(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) {
81 let buf = [repeated];
82 low_level_api::start_transfer(
83 pac::$dma_peri,
84 $channel_num,
85 request,
86 low_level_api::Dir::MemoryToPeripheral,
87 reg_addr as *const u32,
88 buf.as_ptr() as *mut u32,
89 count,
90 false,
91 W::bits(),
92 options,
93 )
94 }
95
96 unsafe fn start_read<W: Word>(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) {
97 let (ptr, len) = super::slice_ptr_parts_mut(buf);
98 low_level_api::start_transfer(
99 pac::$dma_peri,
100 $channel_num,
101 request,
102 low_level_api::Dir::PeripheralToMemory,
103 reg_addr as *const u32,
104 ptr as *mut u32,
105 len,
106 true,
107 W::bits(),
108 options,
109 );
110 }
111
112 unsafe fn start_double_buffered_read<W: Word>(
113 &mut self,
114 _request: Request,
115 _reg_addr: *const W,
116 _buffer0: *mut W,
117 _buffer1: *mut W,
118 _buffer_len: usize,
119 _options: TransferOptions,
120 ) {
121 panic!("Unsafe double buffered mode is unavailable on GPBDMA");
122 }
123
124 unsafe fn set_buffer0<W: Word>(&mut self, _buffer: *mut W) {
125 panic!("Unsafe double buffered mode is unavailable on GPBDMA");
126 }
127
128 unsafe fn set_buffer1<W: Word>(&mut self, _buffer: *mut W) {
129 panic!("Unsafe double buffered mode is unavailable on GPBDMA");
130 }
131
132 unsafe fn is_buffer0_accessible(&mut self) -> bool {
133 panic!("Unsafe double buffered mode is unavailable on GPBDMA");
134 }
135
136 fn request_stop(&mut self) {
137 unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);}
138 }
139
140 fn is_running(&self) -> bool {
141 unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)}
142 }
143
144 fn remaining_transfers(&mut self) -> u16 {
145 unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)}
146 }
147
148 fn set_waker(&mut self, waker: &Waker) {
149 unsafe {low_level_api::set_waker($index, waker )}
150 }
151
152 fn on_irq() {
153 unsafe {
154 low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index);
155 }
156 }
157 }
158 impl crate::dma::Channel for crate::peripherals::$channel_peri { }
159 };
160}
161
162mod low_level_api {
163 use super::*;
164
165 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
166 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
167 pub enum Dir {
168 MemoryToPeripheral,
169 PeripheralToMemory,
170 }
171
172 pub unsafe fn start_transfer(
173 dma: Gpdma,
174 channel_number: u8,
175 request: Request,
176 dir: Dir,
177 peri_addr: *const u32,
178 mem_addr: *mut u32,
179 mem_len: usize,
180 incr_mem: bool,
181 data_size: WordSize,
182 _options: TransferOptions,
183 ) {
184 // "Preceding reads and writes cannot be moved past subsequent writes."
185 fence(Ordering::SeqCst);
186
187 let ch = dma.ch(channel_number as _);
188 ch.llr().write(|_| {}); // no linked list
189 ch.tr1().write(|w| {
190 w.set_sdw(data_size.into());
191 w.set_ddw(data_size.into());
192 w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);
193 w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);
194 });
195 ch.tr2().write(|w| {
196 w.set_dreq(match dir {
197 Dir::MemoryToPeripheral => vals::ChTr2Dreq::DESTINATIONPERIPHERAL,
198 Dir::PeripheralToMemory => vals::ChTr2Dreq::SOURCEPERIPHERAL,
199 });
200 w.set_reqsel(request);
201 });
202 ch.br1().write(|w| {
203 // BNDT is specified as bytes, not as number of transfers.
204 w.set_bndt((mem_len * data_size.bytes()) as u16)
205 });
206
207 match dir {
208 Dir::MemoryToPeripheral => {
209 ch.sar().write_value(mem_addr as _);
210 ch.dar().write_value(peri_addr as _);
211 }
212 Dir::PeripheralToMemory => {
213 ch.sar().write_value(peri_addr as _);
214 ch.dar().write_value(mem_addr as _);
215 }
216 }
217
218 ch.cr().write(|w| {
219 // Enable interrupts
220 w.set_tcie(true);
221 w.set_useie(true);
222 w.set_dteie(true);
223 w.set_suspie(true);
224
225 // Start it
226 w.set_en(true);
227 });
228 }
229
230 /// Stops the DMA channel.
231 pub unsafe fn request_stop(dma: Gpdma, channel_number: u8) {
232 // get a handle on the channel itself
233 let ch = dma.ch(channel_number as _);
234
235 // Disable the channel. Keep the IEs enabled so the irqs still fire.
236 ch.cr().write(|w| {
237 w.set_tcie(true);
238 w.set_useie(true);
239 w.set_dteie(true);
240 w.set_suspie(true);
241 });
242
243 // "Subsequent reads and writes cannot be moved ahead of preceding reads."
244 fence(Ordering::SeqCst);
245 }
246
247 /// Gets the running status of the channel
248 pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool {
249 let ch = dma.ch(ch as _);
250 !ch.sr().read().idlef()
251 }
252
253 /// Gets the total remaining transfers for the channel
254 /// Note: this will be zero for transfers that completed without cancellation.
255 pub unsafe fn get_remaining_transfers(dma: Gpdma, ch: u8) -> u16 {
256 // get a handle on the channel itself
257 let ch = dma.ch(ch as _);
258 // read the remaining transfer count. If this is zero, the transfer completed fully.
259 ch.br1().read().bndt()
260 }
261
262 /// Sets the waker for the specified DMA channel
263 pub unsafe fn set_waker(state_number: usize, waker: &Waker) {
264 STATE.channels[state_number].waker.register(waker);
265 }
266
267 /// Safety: Must be called with a matching set of parameters for a valid dma channel
268 pub unsafe fn on_irq_inner(dma: Gpdma, channel_num: u8, state_index: u8) {
269 let channel_num = channel_num as usize;
270 let state_index = state_index as usize;
271
272 let ch = dma.ch(channel_num);
273 let sr = ch.sr().read();
274
275 if sr.dtef() {
276 panic!(
277 "DMA: data transfer error on DMA@{:08x} channel {}",
278 dma.0 as u32, channel_num
279 );
280 }
281 if sr.usef() {
282 panic!(
283 "DMA: user settings error on DMA@{:08x} channel {}",
284 dma.0 as u32, channel_num
285 );
286 }
287
288 if sr.suspf() || sr.tcf() {
289 ch.cr().write(|w| w.set_reset(true));
290 STATE.channels[state_index].waker.wake();
291 }
292 }
293}
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs
index f96ccbf6e..c19f7b3c7 100644
--- a/embassy-stm32/src/dma/mod.rs
+++ b/embassy-stm32/src/dma/mod.rs
@@ -4,6 +4,8 @@ pub(crate) mod bdma;
4pub(crate) mod dma; 4pub(crate) mod dma;
5#[cfg(dmamux)] 5#[cfg(dmamux)]
6mod dmamux; 6mod dmamux;
7#[cfg(gpdma)]
8mod gpdma;
7 9
8#[cfg(dmamux)] 10#[cfg(dmamux)]
9pub use dmamux::*; 11pub use dmamux::*;
@@ -24,9 +26,9 @@ pub mod low_level {
24 26
25pub(crate) use transfers::*; 27pub(crate) use transfers::*;
26 28
27#[cfg(any(bdma_v2, dma_v2, dmamux))] 29#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))]
28pub type Request = u8; 30pub type Request = u8;
29#[cfg(not(any(bdma_v2, dma_v2, dmamux)))] 31#[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))]
30pub type Request = (); 32pub type Request = ();
31 33
32pub(crate) mod sealed { 34pub(crate) mod sealed {
@@ -118,11 +120,24 @@ pub(crate) mod sealed {
118 } 120 }
119} 121}
120 122
123#[derive(Debug, Copy, Clone, PartialEq, Eq)]
124#[cfg_attr(feature = "defmt", derive(defmt::Format))]
121pub enum WordSize { 125pub enum WordSize {
122 OneByte, 126 OneByte,
123 TwoBytes, 127 TwoBytes,
124 FourBytes, 128 FourBytes,
125} 129}
130
131impl WordSize {
132 pub fn bytes(&self) -> usize {
133 match self {
134 Self::OneByte => 1,
135 Self::TwoBytes => 2,
136 Self::FourBytes => 4,
137 }
138 }
139}
140
126pub trait Word: sealed::Word { 141pub trait Word: sealed::Word {
127 fn bits() -> WordSize; 142 fn bits() -> WordSize;
128} 143}
@@ -148,7 +163,8 @@ impl Word for u32 {
148 } 163 }
149} 164}
150 165
151#[derive(Debug, PartialEq)] 166#[derive(Debug, Copy, Clone, PartialEq, Eq)]
167#[cfg_attr(feature = "defmt", derive(defmt::Format))]
152pub enum Burst { 168pub enum Burst {
153 /// Single transfer 169 /// Single transfer
154 Single, 170 Single,
@@ -160,7 +176,8 @@ pub enum Burst {
160 Incr16, 176 Incr16,
161} 177}
162 178
163#[derive(Debug, PartialEq)] 179#[derive(Debug, Copy, Clone, PartialEq, Eq)]
180#[cfg_attr(feature = "defmt", derive(defmt::Format))]
164pub enum FlowControl { 181pub enum FlowControl {
165 /// Flow control by DMA 182 /// Flow control by DMA
166 Dma, 183 Dma,
@@ -168,6 +185,8 @@ pub enum FlowControl {
168 Peripheral, 185 Peripheral,
169} 186}
170 187
188#[derive(Debug, Copy, Clone, PartialEq, Eq)]
189#[cfg_attr(feature = "defmt", derive(defmt::Format))]
171pub struct TransferOptions { 190pub struct TransferOptions {
172 /// Peripheral burst transfer configuration 191 /// Peripheral burst transfer configuration
173 pub pburst: Burst, 192 pub pburst: Burst,
@@ -299,6 +318,8 @@ pub(crate) unsafe fn init() {
299 dma::init(); 318 dma::init();
300 #[cfg(dmamux)] 319 #[cfg(dmamux)]
301 dmamux::init(); 320 dmamux::init();
321 #[cfg(gpdma)]
322 gpdma::init();
302} 323}
303 324
304// TODO: replace transmutes with core::ptr::metadata once it's stable 325// TODO: replace transmutes with core::ptr::metadata once it's stable