diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-04-26 23:57:26 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-27 01:16:14 +0200 |
| commit | 009bb8e4e1b7afbe9d9d7d89135f8d4dd3c4e808 (patch) | |
| tree | d734f3e82f9fb3c22b7517a70e1f47969624d248 /embassy-stm32/src/dma | |
| parent | a39d796c3de9c96ea4df6b9da525cb0d5ef60fc0 (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.rs | 293 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/mod.rs | 29 |
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 @@ | |||
| 1 | use core::sync::atomic::{fence, Ordering}; | ||
| 2 | use core::task::Waker; | ||
| 3 | |||
| 4 | use embassy::interrupt::{Interrupt, InterruptExt}; | ||
| 5 | use embassy::waitqueue::AtomicWaker; | ||
| 6 | |||
| 7 | use crate::_generated::GPDMA_CHANNEL_COUNT; | ||
| 8 | use crate::interrupt; | ||
| 9 | use crate::pac; | ||
| 10 | use crate::pac::gpdma::{vals, Gpdma}; | ||
| 11 | |||
| 12 | use super::{Request, TransferOptions, Word, WordSize}; | ||
| 13 | |||
| 14 | impl 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 | |||
| 24 | struct ChannelState { | ||
| 25 | waker: AtomicWaker, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl ChannelState { | ||
| 29 | const fn new() -> Self { | ||
| 30 | Self { | ||
| 31 | waker: AtomicWaker::new(), | ||
| 32 | } | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | struct State { | ||
| 37 | channels: [ChannelState; GPDMA_CHANNEL_COUNT], | ||
| 38 | } | ||
| 39 | |||
| 40 | impl State { | ||
| 41 | const fn new() -> Self { | ||
| 42 | const CH: ChannelState = ChannelState::new(); | ||
| 43 | Self { | ||
| 44 | channels: [CH; GPDMA_CHANNEL_COUNT], | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | static STATE: State = State::new(); | ||
| 50 | |||
| 51 | /// safety: must be called only once | ||
| 52 | pub(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 | |||
| 61 | foreach_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 | |||
| 162 | mod 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; | |||
| 4 | pub(crate) mod dma; | 4 | pub(crate) mod dma; |
| 5 | #[cfg(dmamux)] | 5 | #[cfg(dmamux)] |
| 6 | mod dmamux; | 6 | mod dmamux; |
| 7 | #[cfg(gpdma)] | ||
| 8 | mod gpdma; | ||
| 7 | 9 | ||
| 8 | #[cfg(dmamux)] | 10 | #[cfg(dmamux)] |
| 9 | pub use dmamux::*; | 11 | pub use dmamux::*; |
| @@ -24,9 +26,9 @@ pub mod low_level { | |||
| 24 | 26 | ||
| 25 | pub(crate) use transfers::*; | 27 | pub(crate) use transfers::*; |
| 26 | 28 | ||
| 27 | #[cfg(any(bdma_v2, dma_v2, dmamux))] | 29 | #[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))] |
| 28 | pub type Request = u8; | 30 | pub type Request = u8; |
| 29 | #[cfg(not(any(bdma_v2, dma_v2, dmamux)))] | 31 | #[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))] |
| 30 | pub type Request = (); | 32 | pub type Request = (); |
| 31 | 33 | ||
| 32 | pub(crate) mod sealed { | 34 | pub(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))] | ||
| 121 | pub enum WordSize { | 125 | pub enum WordSize { |
| 122 | OneByte, | 126 | OneByte, |
| 123 | TwoBytes, | 127 | TwoBytes, |
| 124 | FourBytes, | 128 | FourBytes, |
| 125 | } | 129 | } |
| 130 | |||
| 131 | impl 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 | |||
| 126 | pub trait Word: sealed::Word { | 141 | pub 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))] | ||
| 152 | pub enum Burst { | 168 | pub 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))] | ||
| 164 | pub enum FlowControl { | 181 | pub 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))] | ||
| 171 | pub struct TransferOptions { | 190 | pub 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 |
