diff options
Diffstat (limited to 'examples/mcxa/src/bin/raw_dma_channel_link.rs')
| -rw-r--r-- | examples/mcxa/src/bin/raw_dma_channel_link.rs | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/examples/mcxa/src/bin/raw_dma_channel_link.rs b/examples/mcxa/src/bin/raw_dma_channel_link.rs new file mode 100644 index 000000000..74785e4f3 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_channel_link.rs | |||
| @@ -0,0 +1,278 @@ | |||
| 1 | //! DMA channel linking example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have | ||
| 4 | //! a high-level and safe API for. It should not be taken as typical, recommended, or | ||
| 5 | //! stable usage! | ||
| 6 | //! | ||
| 7 | //! This example demonstrates DMA channel linking (minor and major loop linking): | ||
| 8 | //! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: | ||
| 9 | //! - Minor Link to Channel 1 (triggers CH1 after each minor loop) | ||
| 10 | //! - Major Link to Channel 2 (triggers CH2 after major loop completes) | ||
| 11 | //! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link) | ||
| 12 | //! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) | ||
| 13 | //! | ||
| 14 | //! # Embassy-style features demonstrated: | ||
| 15 | //! - `DmaChannel::new()` for channel creation | ||
| 16 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods | ||
| 17 | //! - Channel linking with `set_minor_link()` and `set_major_link()` | ||
| 18 | |||
| 19 | #![no_std] | ||
| 20 | #![no_main] | ||
| 21 | |||
| 22 | use embassy_executor::Spawner; | ||
| 23 | use embassy_mcxa::clocks::config::Div8; | ||
| 24 | use embassy_mcxa::dma::DmaChannel; | ||
| 25 | use embassy_mcxa::pac; | ||
| 26 | use static_cell::ConstStaticCell; | ||
| 27 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 28 | |||
| 29 | // Buffers | ||
| 30 | static SRC_BUFFER: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([1, 2, 3, 4]); | ||
| 31 | static DEST_BUFFER0: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 32 | static DEST_BUFFER1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 33 | static DEST_BUFFER2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 34 | |||
| 35 | #[embassy_executor::main] | ||
| 36 | async fn main(_spawner: Spawner) { | ||
| 37 | // Small delay to allow probe-rs to attach after reset | ||
| 38 | for _ in 0..100_000 { | ||
| 39 | cortex_m::asm::nop(); | ||
| 40 | } | ||
| 41 | |||
| 42 | let mut cfg = hal::config::Config::default(); | ||
| 43 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 44 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 45 | let p = hal::init(cfg); | ||
| 46 | |||
| 47 | defmt::info!("DMA channel link example starting..."); | ||
| 48 | |||
| 49 | // DMA is initialized during hal::init() - no need to call ensure_init() | ||
| 50 | |||
| 51 | let pac_periphs = unsafe { pac::Peripherals::steal() }; | ||
| 52 | let dma0 = &pac_periphs.dma0; | ||
| 53 | let edma = unsafe { &*pac::Edma0Tcd0::ptr() }; | ||
| 54 | |||
| 55 | // Clear any residual state | ||
| 56 | for i in 0..3 { | ||
| 57 | let t = edma.tcd(i); | ||
| 58 | t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); | ||
| 59 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 60 | t.ch_es().write(|w| w.err().clear_bit_by_one()); | ||
| 61 | t.ch_mux().write(|w| unsafe { w.bits(0) }); | ||
| 62 | } | ||
| 63 | |||
| 64 | // Clear Global Halt/Error state | ||
| 65 | dma0.mp_csr().modify(|_, w| { | ||
| 66 | w.halt() | ||
| 67 | .normal_operation() | ||
| 68 | .hae() | ||
| 69 | .normal_operation() | ||
| 70 | .ecx() | ||
| 71 | .normal_operation() | ||
| 72 | .cx() | ||
| 73 | .normal_operation() | ||
| 74 | }); | ||
| 75 | |||
| 76 | defmt::info!("EDMA channel link example begin."); | ||
| 77 | |||
| 78 | // Initialize buffers | ||
| 79 | let src = SRC_BUFFER.take(); | ||
| 80 | let dst0 = DEST_BUFFER0.take(); | ||
| 81 | let dst1 = DEST_BUFFER1.take(); | ||
| 82 | let dst2 = DEST_BUFFER2.take(); | ||
| 83 | |||
| 84 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); | ||
| 85 | defmt::info!("DEST0 (before): {=[?]}", dst0.as_slice()); | ||
| 86 | defmt::info!("DEST1 (before): {=[?]}", dst1.as_slice()); | ||
| 87 | defmt::info!("DEST2 (before): {=[?]}", dst2.as_slice()); | ||
| 88 | |||
| 89 | defmt::info!("Configuring DMA channels with Embassy-style API..."); | ||
| 90 | |||
| 91 | let ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 92 | let ch1 = DmaChannel::new(p.DMA_CH1); | ||
| 93 | let ch2 = DmaChannel::new(p.DMA_CH2); | ||
| 94 | |||
| 95 | // Configure channels using direct TCD access (advanced feature demo) | ||
| 96 | // This example demonstrates channel linking which requires direct TCD manipulation | ||
| 97 | |||
| 98 | // Helper to configure TCD for memory-to-memory transfer | ||
| 99 | // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt | ||
| 100 | #[allow(clippy::too_many_arguments)] | ||
| 101 | unsafe fn configure_tcd( | ||
| 102 | edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock, | ||
| 103 | ch: usize, | ||
| 104 | src: u32, | ||
| 105 | dst: u32, | ||
| 106 | width: u8, | ||
| 107 | nbytes: u32, | ||
| 108 | count: u16, | ||
| 109 | enable_int: bool, | ||
| 110 | ) { | ||
| 111 | let t = edma.tcd(ch); | ||
| 112 | |||
| 113 | // Reset channel state | ||
| 114 | t.ch_csr().write(|w| { | ||
| 115 | w.erq() | ||
| 116 | .disable() | ||
| 117 | .earq() | ||
| 118 | .disable() | ||
| 119 | .eei() | ||
| 120 | .no_error() | ||
| 121 | .ebw() | ||
| 122 | .disable() | ||
| 123 | .done() | ||
| 124 | .clear_bit_by_one() | ||
| 125 | }); | ||
| 126 | t.ch_es().write(|w| w.bits(0)); | ||
| 127 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 128 | |||
| 129 | // Source/destination addresses | ||
| 130 | t.tcd_saddr().write(|w| w.saddr().bits(src)); | ||
| 131 | t.tcd_daddr().write(|w| w.daddr().bits(dst)); | ||
| 132 | |||
| 133 | // Offsets: increment by width | ||
| 134 | t.tcd_soff().write(|w| w.soff().bits(width as u16)); | ||
| 135 | t.tcd_doff().write(|w| w.doff().bits(width as u16)); | ||
| 136 | |||
| 137 | // Attributes: size = log2(width) | ||
| 138 | let size = match width { | ||
| 139 | 1 => 0, | ||
| 140 | 2 => 1, | ||
| 141 | 4 => 2, | ||
| 142 | _ => 0, | ||
| 143 | }; | ||
| 144 | t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size)); | ||
| 145 | |||
| 146 | // Number of bytes per minor loop | ||
| 147 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); | ||
| 148 | |||
| 149 | // Major loop: reset source address after major loop | ||
| 150 | let total_bytes = nbytes * count as u32; | ||
| 151 | t.tcd_slast_sda() | ||
| 152 | .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); | ||
| 153 | t.tcd_dlast_sga() | ||
| 154 | .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); | ||
| 155 | |||
| 156 | // Major loop count | ||
| 157 | t.tcd_biter_elinkno().write(|w| w.biter().bits(count)); | ||
| 158 | t.tcd_citer_elinkno().write(|w| w.citer().bits(count)); | ||
| 159 | |||
| 160 | // Control/status: enable interrupt if requested | ||
| 161 | if enable_int { | ||
| 162 | t.tcd_csr().write(|w| w.intmajor().set_bit()); | ||
| 163 | } else { | ||
| 164 | t.tcd_csr().write(|w| w.intmajor().clear_bit()); | ||
| 165 | } | ||
| 166 | |||
| 167 | cortex_m::asm::dsb(); | ||
| 168 | } | ||
| 169 | |||
| 170 | unsafe { | ||
| 171 | // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations) | ||
| 172 | // Minor Link -> Channel 1 | ||
| 173 | // Major Link -> Channel 2 | ||
| 174 | configure_tcd( | ||
| 175 | edma, | ||
| 176 | 0, | ||
| 177 | src.as_ptr() as u32, | ||
| 178 | dst0.as_mut_ptr() as u32, | ||
| 179 | 4, // src width | ||
| 180 | 8, // nbytes (minor loop = 2 words) | ||
| 181 | 2, // count (major loop = 2 iterations) | ||
| 182 | false, // no interrupt | ||
| 183 | ); | ||
| 184 | ch0.set_minor_link(1); // Link to CH1 after each minor loop | ||
| 185 | ch0.set_major_link(2); // Link to CH2 after major loop | ||
| 186 | |||
| 187 | // Channel 1: Transfer 16 bytes (triggered by CH0 minor link) | ||
| 188 | configure_tcd( | ||
| 189 | edma, | ||
| 190 | 1, | ||
| 191 | src.as_ptr() as u32, | ||
| 192 | dst1.as_mut_ptr() as u32, | ||
| 193 | 4, | ||
| 194 | 16, // full buffer in one minor loop | ||
| 195 | 1, // 1 major iteration | ||
| 196 | false, | ||
| 197 | ); | ||
| 198 | |||
| 199 | // Channel 2: Transfer 16 bytes (triggered by CH0 major link) | ||
| 200 | configure_tcd( | ||
| 201 | edma, | ||
| 202 | 2, | ||
| 203 | src.as_ptr() as u32, | ||
| 204 | dst2.as_mut_ptr() as u32, | ||
| 205 | 4, | ||
| 206 | 16, // full buffer in one minor loop | ||
| 207 | 1, // 1 major iteration | ||
| 208 | true, // enable interrupt | ||
| 209 | ); | ||
| 210 | } | ||
| 211 | |||
| 212 | defmt::info!("Triggering Channel 0 (1st minor loop)..."); | ||
| 213 | |||
| 214 | // Trigger first minor loop of CH0 | ||
| 215 | unsafe { | ||
| 216 | ch0.trigger_start(); | ||
| 217 | } | ||
| 218 | |||
| 219 | // Wait for CH1 to complete (triggered by CH0 minor link) | ||
| 220 | while !ch1.is_done() { | ||
| 221 | cortex_m::asm::nop(); | ||
| 222 | } | ||
| 223 | unsafe { | ||
| 224 | ch1.clear_done(); | ||
| 225 | } | ||
| 226 | |||
| 227 | defmt::info!("CH1 done (via minor link)."); | ||
| 228 | defmt::info!("Triggering Channel 0 (2nd minor loop)..."); | ||
| 229 | |||
| 230 | // Trigger second minor loop of CH0 | ||
| 231 | unsafe { | ||
| 232 | ch0.trigger_start(); | ||
| 233 | } | ||
| 234 | |||
| 235 | // Wait for CH0 major loop to complete | ||
| 236 | while !ch0.is_done() { | ||
| 237 | cortex_m::asm::nop(); | ||
| 238 | } | ||
| 239 | unsafe { | ||
| 240 | ch0.clear_done(); | ||
| 241 | } | ||
| 242 | |||
| 243 | defmt::info!("CH0 major loop done."); | ||
| 244 | |||
| 245 | // Wait for CH2 to complete (triggered by CH0 major link) | ||
| 246 | // Using is_done() instead of AtomicBool - the standard interrupt handler | ||
| 247 | // clears the interrupt flag and wakes wakers, but DONE bit remains set | ||
| 248 | while !ch2.is_done() { | ||
| 249 | cortex_m::asm::nop(); | ||
| 250 | } | ||
| 251 | unsafe { | ||
| 252 | ch2.clear_done(); | ||
| 253 | } | ||
| 254 | |||
| 255 | defmt::info!("CH2 done (via major link)."); | ||
| 256 | |||
| 257 | defmt::info!("EDMA channel link example finish."); | ||
| 258 | |||
| 259 | defmt::info!("DEST0 (after): {=[?]}", dst0.as_slice()); | ||
| 260 | defmt::info!("DEST1 (after): {=[?]}", dst1.as_slice()); | ||
| 261 | defmt::info!("DEST2 (after): {=[?]}", dst2.as_slice()); | ||
| 262 | |||
| 263 | // Verify all buffers match source | ||
| 264 | let mut success = true; | ||
| 265 | for sli in [dst0, dst1, dst2] { | ||
| 266 | success &= sli == src; | ||
| 267 | } | ||
| 268 | |||
| 269 | if success { | ||
| 270 | defmt::info!("PASS: Data verified."); | ||
| 271 | } else { | ||
| 272 | defmt::error!("FAIL: Mismatch detected!"); | ||
| 273 | } | ||
| 274 | |||
| 275 | loop { | ||
| 276 | cortex_m::asm::wfe(); | ||
| 277 | } | ||
| 278 | } | ||
