diff options
Diffstat (limited to 'examples/src/bin/dma_scatter_gather_builder.rs')
| -rw-r--r-- | examples/src/bin/dma_scatter_gather_builder.rs | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/examples/src/bin/dma_scatter_gather_builder.rs b/examples/src/bin/dma_scatter_gather_builder.rs new file mode 100644 index 000000000..078e26c60 --- /dev/null +++ b/examples/src/bin/dma_scatter_gather_builder.rs | |||
| @@ -0,0 +1,244 @@ | |||
| 1 | //! DMA Scatter-Gather Builder example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! This example demonstrates using the new `ScatterGatherBuilder` API for | ||
| 4 | //! chaining multiple DMA transfers with a type-safe builder pattern. | ||
| 5 | //! | ||
| 6 | //! # Features demonstrated: | ||
| 7 | //! - `ScatterGatherBuilder::new()` for creating a builder | ||
| 8 | //! - `add_transfer()` for adding memory-to-memory segments | ||
| 9 | //! - `build()` to start the chained transfer | ||
| 10 | //! - Automatic TCD linking and ESG bit management | ||
| 11 | //! | ||
| 12 | //! # Comparison with manual scatter-gather: | ||
| 13 | //! The manual approach (see `dma_scatter_gather.rs`) requires: | ||
| 14 | //! - Manual TCD pool allocation and alignment | ||
| 15 | //! - Manual CSR/ESG/INTMAJOR bit manipulation | ||
| 16 | //! - Manual dlast_sga address calculations | ||
| 17 | //! | ||
| 18 | //! The builder approach handles all of this automatically! | ||
| 19 | |||
| 20 | #![no_std] | ||
| 21 | #![no_main] | ||
| 22 | |||
| 23 | use embassy_executor::Spawner; | ||
| 24 | use embassy_mcxa::clocks::config::Div8; | ||
| 25 | use embassy_mcxa::clocks::Gate; | ||
| 26 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, ScatterGatherBuilder}; | ||
| 27 | use embassy_mcxa::{bind_interrupts, dma}; | ||
| 28 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | ||
| 29 | use embassy_mcxa::pac; | ||
| 30 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 31 | |||
| 32 | // Bind DMA channel 0 interrupt | ||
| 33 | bind_interrupts!(struct Irqs { | ||
| 34 | DMA_CH0 => DmaCh0InterruptHandler; | ||
| 35 | }); | ||
| 36 | |||
| 37 | // Source buffers (multiple segments) | ||
| 38 | static mut SRC1: [u32; 4] = [0x11111111, 0x22222222, 0x33333333, 0x44444444]; | ||
| 39 | static mut SRC2: [u32; 4] = [0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]; | ||
| 40 | static mut SRC3: [u32; 4] = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]; | ||
| 41 | |||
| 42 | // Destination buffers (one per segment) | ||
| 43 | static mut DST1: [u32; 4] = [0; 4]; | ||
| 44 | static mut DST2: [u32; 4] = [0; 4]; | ||
| 45 | static mut DST3: [u32; 4] = [0; 4]; | ||
| 46 | |||
| 47 | /// Helper to write a u32 as hex to UART | ||
| 48 | fn write_hex(tx: &mut LpuartTx<'_, Blocking>, val: u32) { | ||
| 49 | const HEX: &[u8; 16] = b"0123456789ABCDEF"; | ||
| 50 | for i in (0..8).rev() { | ||
| 51 | let nibble = ((val >> (i * 4)) & 0xF) as usize; | ||
| 52 | tx.blocking_write(&[HEX[nibble]]).ok(); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Helper to print a buffer to UART | ||
| 57 | fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { | ||
| 58 | tx.blocking_write(b"[").ok(); | ||
| 59 | unsafe { | ||
| 60 | for i in 0..len { | ||
| 61 | write_hex(tx, *buf_ptr.add(i)); | ||
| 62 | if i < len - 1 { | ||
| 63 | tx.blocking_write(b", ").ok(); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | tx.blocking_write(b"]").ok(); | ||
| 68 | } | ||
| 69 | |||
| 70 | #[embassy_executor::main] | ||
| 71 | async fn main(_spawner: Spawner) { | ||
| 72 | // Small delay to allow probe-rs to attach after reset | ||
| 73 | for _ in 0..100_000 { | ||
| 74 | cortex_m::asm::nop(); | ||
| 75 | } | ||
| 76 | |||
| 77 | let mut cfg = hal::config::Config::default(); | ||
| 78 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 79 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 80 | let p = hal::init(cfg); | ||
| 81 | |||
| 82 | defmt::info!("DMA Scatter-Gather Builder example starting..."); | ||
| 83 | |||
| 84 | // Enable DMA0 clock and release reset | ||
| 85 | unsafe { | ||
| 86 | hal::peripherals::DMA0::enable_clock(); | ||
| 87 | hal::peripherals::DMA0::release_reset(); | ||
| 88 | } | ||
| 89 | |||
| 90 | let pac_periphs = unsafe { pac::Peripherals::steal() }; | ||
| 91 | |||
| 92 | // Initialize DMA | ||
| 93 | unsafe { | ||
| 94 | dma::init(&pac_periphs); | ||
| 95 | } | ||
| 96 | |||
| 97 | // Enable DMA interrupt | ||
| 98 | unsafe { | ||
| 99 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | ||
| 100 | } | ||
| 101 | |||
| 102 | // Create UART for debug output | ||
| 103 | let config = Config { | ||
| 104 | baudrate_bps: 115_200, | ||
| 105 | enable_tx: true, | ||
| 106 | enable_rx: false, | ||
| 107 | ..Default::default() | ||
| 108 | }; | ||
| 109 | |||
| 110 | let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); | ||
| 111 | let (mut tx, _rx) = lpuart.split(); | ||
| 112 | |||
| 113 | tx.blocking_write(b"DMA Scatter-Gather Builder Example\r\n").unwrap(); | ||
| 114 | tx.blocking_write(b"===================================\r\n\r\n").unwrap(); | ||
| 115 | |||
| 116 | // Show source buffers | ||
| 117 | tx.blocking_write(b"Source buffers:\r\n").unwrap(); | ||
| 118 | tx.blocking_write(b" SRC1: ").unwrap(); | ||
| 119 | print_buffer(&mut tx, core::ptr::addr_of!(SRC1) as *const u32, 4); | ||
| 120 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 121 | tx.blocking_write(b" SRC2: ").unwrap(); | ||
| 122 | print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 4); | ||
| 123 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 124 | tx.blocking_write(b" SRC3: ").unwrap(); | ||
| 125 | print_buffer(&mut tx, core::ptr::addr_of!(SRC3) as *const u32, 4); | ||
| 126 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 127 | |||
| 128 | tx.blocking_write(b"Destination buffers (before):\r\n").unwrap(); | ||
| 129 | tx.blocking_write(b" DST1: ").unwrap(); | ||
| 130 | print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); | ||
| 131 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 132 | tx.blocking_write(b" DST2: ").unwrap(); | ||
| 133 | print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); | ||
| 134 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 135 | tx.blocking_write(b" DST3: ").unwrap(); | ||
| 136 | print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); | ||
| 137 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 138 | |||
| 139 | // Create DMA channel | ||
| 140 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 141 | |||
| 142 | tx.blocking_write(b"Building scatter-gather chain with builder API...\r\n").unwrap(); | ||
| 143 | |||
| 144 | // ========================================================================= | ||
| 145 | // ScatterGatherBuilder API demonstration | ||
| 146 | // ========================================================================= | ||
| 147 | // | ||
| 148 | // The builder pattern makes scatter-gather transfers much easier: | ||
| 149 | // 1. Create a builder | ||
| 150 | // 2. Add transfer segments with add_transfer() | ||
| 151 | // 3. Call build() to start the entire chain | ||
| 152 | // No manual TCD manipulation required! | ||
| 153 | |||
| 154 | let mut builder = ScatterGatherBuilder::<u32>::new(); | ||
| 155 | |||
| 156 | // Add three transfer segments - the builder handles TCD linking automatically | ||
| 157 | unsafe { | ||
| 158 | let src1 = &*core::ptr::addr_of!(SRC1); | ||
| 159 | let dst1 = &mut *core::ptr::addr_of_mut!(DST1); | ||
| 160 | builder.add_transfer(src1, dst1); | ||
| 161 | } | ||
| 162 | |||
| 163 | unsafe { | ||
| 164 | let src2 = &*core::ptr::addr_of!(SRC2); | ||
| 165 | let dst2 = &mut *core::ptr::addr_of_mut!(DST2); | ||
| 166 | builder.add_transfer(src2, dst2); | ||
| 167 | } | ||
| 168 | |||
| 169 | unsafe { | ||
| 170 | let src3 = &*core::ptr::addr_of!(SRC3); | ||
| 171 | let dst3 = &mut *core::ptr::addr_of_mut!(DST3); | ||
| 172 | builder.add_transfer(src3, dst3); | ||
| 173 | } | ||
| 174 | |||
| 175 | tx.blocking_write(b"Added 3 transfer segments to chain.\r\n").unwrap(); | ||
| 176 | tx.blocking_write(b"Starting scatter-gather transfer with .await...\r\n\r\n").unwrap(); | ||
| 177 | |||
| 178 | // Build and execute the scatter-gather chain | ||
| 179 | // The build() method: | ||
| 180 | // - Links all TCDs together with ESG bit | ||
| 181 | // - Sets INTMAJOR on all TCDs | ||
| 182 | // - Loads the first TCD into hardware | ||
| 183 | // - Returns a Transfer future | ||
| 184 | unsafe { | ||
| 185 | let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather"); | ||
| 186 | transfer.blocking_wait(); | ||
| 187 | } | ||
| 188 | |||
| 189 | tx.blocking_write(b"Scatter-gather transfer complete!\r\n\r\n").unwrap(); | ||
| 190 | |||
| 191 | // Show results | ||
| 192 | tx.blocking_write(b"Destination buffers (after):\r\n").unwrap(); | ||
| 193 | tx.blocking_write(b" DST1: ").unwrap(); | ||
| 194 | print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); | ||
| 195 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 196 | tx.blocking_write(b" DST2: ").unwrap(); | ||
| 197 | print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); | ||
| 198 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 199 | tx.blocking_write(b" DST3: ").unwrap(); | ||
| 200 | print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); | ||
| 201 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 202 | |||
| 203 | // Verify all three segments | ||
| 204 | let mut all_ok = true; | ||
| 205 | unsafe { | ||
| 206 | let src1 = core::ptr::addr_of!(SRC1) as *const u32; | ||
| 207 | let dst1 = core::ptr::addr_of!(DST1) as *const u32; | ||
| 208 | for i in 0..4 { | ||
| 209 | if *src1.add(i) != *dst1.add(i) { | ||
| 210 | all_ok = false; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | let src2 = core::ptr::addr_of!(SRC2) as *const u32; | ||
| 215 | let dst2 = core::ptr::addr_of!(DST2) as *const u32; | ||
| 216 | for i in 0..4 { | ||
| 217 | if *src2.add(i) != *dst2.add(i) { | ||
| 218 | all_ok = false; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | let src3 = core::ptr::addr_of!(SRC3) as *const u32; | ||
| 223 | let dst3 = core::ptr::addr_of!(DST3) as *const u32; | ||
| 224 | for i in 0..4 { | ||
| 225 | if *src3.add(i) != *dst3.add(i) { | ||
| 226 | all_ok = false; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | if all_ok { | ||
| 232 | tx.blocking_write(b"PASS: All segments verified!\r\n").unwrap(); | ||
| 233 | defmt::info!("PASS: All segments verified!"); | ||
| 234 | } else { | ||
| 235 | tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); | ||
| 236 | defmt::error!("FAIL: Mismatch detected!"); | ||
| 237 | } | ||
| 238 | |||
| 239 | tx.blocking_write(b"\r\n=== Scatter-Gather Builder example complete ===\r\n").unwrap(); | ||
| 240 | |||
| 241 | loop { | ||
| 242 | cortex_m::asm::wfe(); | ||
| 243 | } | ||
| 244 | } | ||
