aboutsummaryrefslogtreecommitdiff
path: root/examples/src/bin/dma_scatter_gather.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/src/bin/dma_scatter_gather.rs')
-rw-r--r--examples/src/bin/dma_scatter_gather.rs281
1 files changed, 281 insertions, 0 deletions
diff --git a/examples/src/bin/dma_scatter_gather.rs b/examples/src/bin/dma_scatter_gather.rs
new file mode 100644
index 000000000..86dd881cd
--- /dev/null
+++ b/examples/src/bin/dma_scatter_gather.rs
@@ -0,0 +1,281 @@
1//! DMA scatter-gather transfer example for MCXA276.
2//!
3//! This example demonstrates using DMA with scatter/gather to chain multiple
4//! transfer descriptors. The first TCD transfers the first half of the buffer,
5//! then automatically loads the second TCD to transfer the second half.
6//!
7//! # Embassy-style features demonstrated:
8//! - `dma::edma_tcd()` accessor for simplified register access
9//! - `DmaChannel::new()` for channel creation
10//! - Scatter/gather with chained TCDs
11
12#![no_std]
13#![no_main]
14
15use core::sync::atomic::{AtomicBool, Ordering};
16use embassy_executor::Spawner;
17use embassy_mcxa::clocks::config::Div8;
18use embassy_mcxa::clocks::Gate;
19use embassy_mcxa::dma::{edma_tcd, DmaChannel, Tcd};
20use embassy_mcxa::{bind_interrupts, dma};
21use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
22use embassy_mcxa::pac;
23use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
24
25// Source and destination buffers
26static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
27static mut DST: [u32; 8] = [0; 8];
28
29// TCD pool for scatter/gather - must be 32-byte aligned
30#[repr(C, align(32))]
31struct TcdPool([Tcd; 2]);
32
33static mut TCD_POOL: TcdPool = TcdPool([Tcd {
34 saddr: 0,
35 soff: 0,
36 attr: 0,
37 nbytes: 0,
38 slast: 0,
39 daddr: 0,
40 doff: 0,
41 citer: 0,
42 dlast_sga: 0,
43 csr: 0,
44 biter: 0,
45}; 2]);
46
47static TRANSFER_DONE: AtomicBool = AtomicBool::new(false);
48
49// Custom DMA interrupt handler for scatter-gather transfer
50// We need a custom handler because we signal completion via TRANSFER_DONE flag
51// and need to conditionally clear DONE bit based on ESG status
52pub struct ScatterGatherDmaHandler;
53
54impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for ScatterGatherDmaHandler {
55 unsafe fn on_interrupt() {
56 let edma = edma_tcd();
57
58 // Clear interrupt flag
59 edma.tcd(0).ch_int().write(|w| w.int().clear_bit_by_one());
60
61 // If ESG=1 (Scatter/Gather), the hardware loads the next TCD and clears DONE.
62 // If ESG=0 (Last TCD), DONE remains set and must be cleared.
63 if edma.tcd(0).ch_csr().read().done().bit_is_set() {
64 edma.tcd(0).ch_csr().write(|w| w.done().clear_bit_by_one());
65 }
66
67 TRANSFER_DONE.store(true, Ordering::Release);
68 }
69}
70
71bind_interrupts!(struct Irqs {
72 DMA_CH0 => ScatterGatherDmaHandler;
73});
74
75/// Helper to write a u32 as decimal ASCII to UART
76fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) {
77 let mut buf = [0u8; 10];
78 let mut n = val;
79 let mut i = buf.len();
80
81 if n == 0 {
82 tx.blocking_write(b"0").ok();
83 return;
84 }
85
86 while n > 0 {
87 i -= 1;
88 buf[i] = b'0' + (n % 10) as u8;
89 n /= 10;
90 }
91
92 tx.blocking_write(&buf[i..]).ok();
93}
94
95/// Helper to print a buffer to UART
96fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) {
97 tx.blocking_write(b"[").ok();
98 unsafe {
99 for i in 0..len {
100 write_u32(tx, *buf_ptr.add(i));
101 if i < len - 1 {
102 tx.blocking_write(b", ").ok();
103 }
104 }
105 }
106 tx.blocking_write(b"]").ok();
107}
108
109#[embassy_executor::main]
110async fn main(_spawner: Spawner) {
111 // Small delay to allow probe-rs to attach after reset
112 for _ in 0..100_000 {
113 cortex_m::asm::nop();
114 }
115
116 let mut cfg = hal::config::Config::default();
117 cfg.clock_cfg.sirc.fro_12m_enabled = true;
118 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
119 let p = hal::init(cfg);
120
121 defmt::info!("DMA scatter-gather transfer example starting...");
122
123 // Enable DMA0 clock and release reset
124 unsafe {
125 hal::peripherals::DMA0::enable_clock();
126 hal::peripherals::DMA0::release_reset();
127 }
128
129 let pac_periphs = unsafe { pac::Peripherals::steal() };
130
131 unsafe {
132 dma::init(&pac_periphs);
133 }
134
135 // Use edma_tcd() accessor instead of passing register block around
136 let edma = edma_tcd();
137
138 // Enable DMA interrupt
139 unsafe {
140 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
141 }
142
143 let config = Config {
144 baudrate_bps: 115_200,
145 enable_tx: true,
146 enable_rx: false,
147 ..Default::default()
148 };
149
150 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
151 let (mut tx, _rx) = lpuart.split();
152
153 tx.blocking_write(b"EDMA scatter-gather transfer example begin.\r\n\r\n")
154 .unwrap();
155
156 // Initialize buffers
157 unsafe {
158 SRC = [1, 2, 3, 4, 5, 6, 7, 8];
159 DST = [0; 8];
160 }
161
162 tx.blocking_write(b"Source Buffer: ").unwrap();
163 print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8);
164 tx.blocking_write(b"\r\n").unwrap();
165
166 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
167 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
168 tx.blocking_write(b"\r\n").unwrap();
169
170 tx.blocking_write(b"Configuring scatter-gather DMA with Embassy-style API...\r\n")
171 .unwrap();
172
173 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
174
175 // Configure scatter-gather transfer using direct TCD access:
176 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel.
177 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1.
178 // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD.
179 unsafe {
180 let tcds = core::slice::from_raw_parts_mut(
181 core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd,
182 2,
183 );
184 let src_ptr = core::ptr::addr_of!(SRC) as *const u32;
185 let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32;
186
187 let num_tcds = 2usize;
188 let chunk_len = 4usize; // 8 / 2
189 let chunk_bytes = (chunk_len * 4) as u32;
190
191 for i in 0..num_tcds {
192 let is_last = i == num_tcds - 1;
193 let next_tcd_addr = if is_last {
194 0 // No next TCD
195 } else {
196 &tcds[i + 1] as *const _ as u32
197 };
198
199 tcds[i] = Tcd {
200 saddr: src_ptr.add(i * chunk_len) as u32,
201 soff: 4,
202 attr: 0x0202, // 32-bit src/dst
203 nbytes: chunk_bytes,
204 slast: 0,
205 daddr: dst_ptr.add(i * chunk_len) as u32,
206 doff: 4,
207 citer: 1,
208 dlast_sga: next_tcd_addr as i32,
209 // ESG (scatter/gather) for non-last, INTMAJOR for all
210 csr: if is_last { 0x0002 } else { 0x0012 },
211 biter: 1,
212 };
213 }
214
215 // Load TCD0 into hardware registers
216 dma_ch0.load_tcd(edma, &tcds[0]);
217 }
218
219 tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap();
220
221 // Trigger first transfer (first half: SRC[0..4] -> DST[0..4])
222 // TCD0 is currently loaded.
223 unsafe {
224 dma_ch0.trigger_start(edma);
225 }
226
227 // Wait for first half
228 while !TRANSFER_DONE.load(Ordering::Acquire) {
229 cortex_m::asm::nop();
230 }
231 TRANSFER_DONE.store(false, Ordering::Release);
232
233 tx.blocking_write(b"First half transferred.\r\n").unwrap();
234 tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap();
235
236 // Trigger second transfer (second half: SRC[4..8] -> DST[4..8])
237 // TCD1 should have been loaded by the scatter/gather engine.
238 unsafe {
239 dma_ch0.trigger_start(edma);
240 }
241
242 // Wait for second half
243 while !TRANSFER_DONE.load(Ordering::Acquire) {
244 cortex_m::asm::nop();
245 }
246 TRANSFER_DONE.store(false, Ordering::Release);
247
248 tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap();
249
250 tx.blocking_write(b"EDMA scatter-gather transfer example finish.\r\n\r\n")
251 .unwrap();
252 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
253 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
254 tx.blocking_write(b"\r\n\r\n").unwrap();
255
256 // Verify: DST should match SRC
257 let mut mismatch = false;
258 unsafe {
259 let src_ptr = core::ptr::addr_of!(SRC) as *const u32;
260 let dst_ptr = core::ptr::addr_of!(DST) as *const u32;
261 for i in 0..8 {
262 if *src_ptr.add(i) != *dst_ptr.add(i) {
263 mismatch = true;
264 break;
265 }
266 }
267 }
268
269 if mismatch {
270 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
271 defmt::error!("FAIL: Mismatch detected!");
272 } else {
273 tx.blocking_write(b"PASS: Data verified.\r\n").unwrap();
274 defmt::info!("PASS: Data verified.");
275 }
276
277 loop {
278 cortex_m::asm::wfe();
279 }
280}
281