aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBogdan Petru Chircu Mare <[email protected]>2025-12-01 19:14:22 -0800
committerBogdan Petru Chircu Mare <[email protected]>2025-12-01 19:14:22 -0800
commita18cf2fd0d8158f4b1f24f8982e187477b58267c (patch)
treee0567be32a6d25109eee73f607dd59eebfc84deb
parentbc21f9b35e09fe9ef2556adf53f1d600af909d03 (diff)
feat(dma): implement type-safe DMA request source API
Replace raw u8 DMA request sources with typed request system following the embassy-imxrt pattern. This provides compile-time verification that peripherals use correct DMA request sources. Changes: - Define DmaRequest trait for type-safe request sources - Add sealed SealedDmaRequest trait to prevent external implementation - Create Lpuart{0-5}{Tx,Rx}Request marker types for each LPUART DMA request - Update DmaChannel::set_request_source() to accept generic R: DmaRequest - Replace Instance::TX_DMA_REQ/RX_DMA_REQ u8 constants with associated types - Update all LPUART DMA call sites to use typed request API This prevents errors like using LPUART2_RX request for LPUART0 at compile time with zero runtime overhead. Addresses felipebalbi's review comment on PR #52 line 86.
-rw-r--r--src/dma.rs134
-rw-r--r--src/lpuart/mod.rs54
2 files changed, 138 insertions, 50 deletions
diff --git a/src/dma.rs b/src/dma.rs
index 8d82c254a..7d1588516 100644
--- a/src/dma.rs
+++ b/src/dma.rs
@@ -341,22 +341,97 @@ pub enum EnableInterrupt {
341pub const DMA_MAX_TRANSFER_SIZE: usize = 0x7FFF; 341pub const DMA_MAX_TRANSFER_SIZE: usize = 0x7FFF;
342 342
343// ============================================================================ 343// ============================================================================
344// DMA Request Source Constants 344// DMA Request Source Types (Type-Safe API)
345// ============================================================================ 345// ============================================================================
346 346
347/// DMA request source numbers for LPUART peripherals on DMA0. 347/// Trait for type-safe DMA request sources.
348pub const DMA_REQ_LPUART0_RX: u8 = 21; 348///
349pub const DMA_REQ_LPUART0_TX: u8 = 22; 349/// Each peripheral that can trigger DMA requests implements this trait
350pub const DMA_REQ_LPUART1_RX: u8 = 23; 350/// with marker types that encode the correct request source number at
351pub const DMA_REQ_LPUART1_TX: u8 = 24; 351/// compile time. This prevents using the wrong request source for a
352pub const DMA_REQ_LPUART2_RX: u8 = 25; 352/// peripheral.
353pub const DMA_REQ_LPUART2_TX: u8 = 26; 353///
354pub const DMA_REQ_LPUART3_RX: u8 = 27; 354/// # Example
355pub const DMA_REQ_LPUART3_TX: u8 = 28; 355///
356pub const DMA_REQ_LPUART4_RX: u8 = 29; 356/// ```ignore
357pub const DMA_REQ_LPUART4_TX: u8 = 30; 357/// // The LPUART2 RX request source is automatically derived from the type:
358pub const DMA_REQ_LPUART5_RX: u8 = 31; 358/// channel.set_request_source::<Lpuart2RxRequest>();
359pub const DMA_REQ_LPUART5_TX: u8 = 32; 359/// ```
360///
361/// This trait is sealed and cannot be implemented outside this crate.
362#[allow(private_bounds)]
363pub trait DmaRequest: sealed::SealedDmaRequest {
364 /// The hardware request source number for the DMA mux.
365 const REQUEST_NUMBER: u8;
366}
367
368/// Macro to define a DMA request type.
369///
370/// Creates a zero-sized marker type that implements `DmaRequest` with
371/// the specified request number.
372macro_rules! define_dma_request {
373 ($(#[$meta:meta])* $name:ident = $num:expr) => {
374 $(#[$meta])*
375 #[derive(Debug, Copy, Clone)]
376 pub struct $name;
377
378 impl sealed::SealedDmaRequest for $name {}
379
380 impl DmaRequest for $name {
381 const REQUEST_NUMBER: u8 = $num;
382 }
383 };
384}
385
386// LPUART DMA request sources (from MCXA276 reference manual Table 4-8)
387define_dma_request!(
388 /// DMA request source for LPUART0 RX.
389 Lpuart0RxRequest = 21
390);
391define_dma_request!(
392 /// DMA request source for LPUART0 TX.
393 Lpuart0TxRequest = 22
394);
395define_dma_request!(
396 /// DMA request source for LPUART1 RX.
397 Lpuart1RxRequest = 23
398);
399define_dma_request!(
400 /// DMA request source for LPUART1 TX.
401 Lpuart1TxRequest = 24
402);
403define_dma_request!(
404 /// DMA request source for LPUART2 RX.
405 Lpuart2RxRequest = 25
406);
407define_dma_request!(
408 /// DMA request source for LPUART2 TX.
409 Lpuart2TxRequest = 26
410);
411define_dma_request!(
412 /// DMA request source for LPUART3 RX.
413 Lpuart3RxRequest = 27
414);
415define_dma_request!(
416 /// DMA request source for LPUART3 TX.
417 Lpuart3TxRequest = 28
418);
419define_dma_request!(
420 /// DMA request source for LPUART4 RX.
421 Lpuart4RxRequest = 29
422);
423define_dma_request!(
424 /// DMA request source for LPUART4 TX.
425 Lpuart4TxRequest = 30
426);
427define_dma_request!(
428 /// DMA request source for LPUART5 RX.
429 Lpuart5RxRequest = 31
430);
431define_dma_request!(
432 /// DMA request source for LPUART5 TX.
433 Lpuart5TxRequest = 32
434);
360 435
361// ============================================================================ 436// ============================================================================
362// Channel Trait (Sealed Pattern) 437// Channel Trait (Sealed Pattern)
@@ -372,6 +447,9 @@ mod sealed {
372 /// Interrupt vector for this channel. 447 /// Interrupt vector for this channel.
373 fn interrupt(&self) -> Interrupt; 448 fn interrupt(&self) -> Interrupt;
374 } 449 }
450
451 /// Sealed trait for DMA request sources.
452 pub trait SealedDmaRequest {}
375} 453}
376 454
377/// Marker trait implemented by HAL peripheral tokens that map to a DMA0 455/// Marker trait implemented by HAL peripheral tokens that map to a DMA0
@@ -1452,26 +1530,40 @@ impl<C: Channel> DmaChannel<C> {
1452 cortex_m::asm::dsb(); 1530 cortex_m::asm::dsb();
1453 } 1531 }
1454 1532
1455 /// Configure the integrated channel MUX to use the given request 1533 /// Configure the integrated channel MUX to use the given typed
1456 /// source value (for example [`DMA_REQ_LPUART2_TX`] or 1534 /// DMA request source (e.g., [`Lpuart2TxRequest`] or [`Lpuart2RxRequest`]).
1457 /// [`DMA_REQ_LPUART2_RX`]). 1535 ///
1536 /// This is the type-safe version that uses marker types to ensure
1537 /// compile-time verification of request source validity.
1458 /// 1538 ///
1459 /// # Safety 1539 /// # Safety
1460 /// 1540 ///
1461 /// Caller must ensure the request source mapping matches the 1541 /// The channel must be properly configured before enabling requests.
1462 /// peripheral that will drive this channel. 1542 /// The caller must ensure the DMA request source matches the peripheral
1543 /// that will drive this channel.
1463 /// 1544 ///
1464 /// # Note 1545 /// # Note
1465 /// 1546 ///
1466 /// The NXP SDK requires a two-step write sequence: first clear 1547 /// The NXP SDK requires a two-step write sequence: first clear
1467 /// the mux to 0, then set the actual source. This is a hardware 1548 /// the mux to 0, then set the actual source. This is a hardware
1468 /// requirement on eDMA4 for the mux to properly latch. 1549 /// requirement on eDMA4 for the mux to properly latch.
1550 ///
1551 /// # Example
1552 ///
1553 /// ```ignore
1554 /// use embassy_mcxa::dma::{DmaChannel, Lpuart2RxRequest};
1555 ///
1556 /// // Type-safe: compiler verifies this is a valid DMA request type
1557 /// unsafe {
1558 /// channel.set_request_source::<Lpuart2RxRequest>();
1559 /// }
1560 /// ```
1469 #[inline] 1561 #[inline]
1470 pub unsafe fn set_request_source(&self, request: u8) { 1562 pub unsafe fn set_request_source<R: DmaRequest>(&self) {
1471 // Two-step write per NXP SDK: clear to 0, then set actual source. 1563 // Two-step write per NXP SDK: clear to 0, then set actual source.
1472 self.tcd().ch_mux().write(|w| w.src().bits(0)); 1564 self.tcd().ch_mux().write(|w| w.src().bits(0));
1473 cortex_m::asm::dsb(); // Ensure the clear completes before setting new source 1565 cortex_m::asm::dsb(); // Ensure the clear completes before setting new source
1474 self.tcd().ch_mux().write(|w| w.src().bits(request)); 1566 self.tcd().ch_mux().write(|w| w.src().bits(R::REQUEST_NUMBER));
1475 } 1567 }
1476 1568
1477 /// Enable hardware requests for this channel (ERQ=1). 1569 /// Enable hardware requests for this channel (ERQ=1).
diff --git a/src/lpuart/mod.rs b/src/lpuart/mod.rs
index bcac1fdbd..a2072ae2a 100644
--- a/src/lpuart/mod.rs
+++ b/src/lpuart/mod.rs
@@ -18,7 +18,9 @@ pub mod buffered;
18// DMA INTEGRATION 18// DMA INTEGRATION
19// ============================================================================ 19// ============================================================================
20 20
21use crate::dma::{Channel as DmaChannelTrait, DmaChannel, EnableInterrupt, RingBuffer, DMA_MAX_TRANSFER_SIZE}; 21use crate::dma::{
22 Channel as DmaChannelTrait, DmaChannel, DmaRequest, EnableInterrupt, RingBuffer, DMA_MAX_TRANSFER_SIZE,
23};
22 24
23// ============================================================================ 25// ============================================================================
24// MISC 26// MISC
@@ -50,14 +52,14 @@ pub struct Info {
50pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> { 52pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> {
51 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance; 53 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance;
52 type Interrupt: interrupt::typelevel::Interrupt; 54 type Interrupt: interrupt::typelevel::Interrupt;
53 /// DMA request source for TX 55 /// Type-safe DMA request source for TX
54 const TX_DMA_REQ: u8; 56 type TxDmaRequest: DmaRequest;
55 /// DMA request source for RX 57 /// Type-safe DMA request source for RX
56 const RX_DMA_REQ: u8; 58 type RxDmaRequest: DmaRequest;
57} 59}
58 60
59macro_rules! impl_instance { 61macro_rules! impl_instance {
60 ($($n:expr, $tx_req:expr, $rx_req:expr);* $(;)?) => { 62 ($($n:expr);* $(;)?) => {
61 $( 63 $(
62 paste!{ 64 paste!{
63 impl SealedInstance for crate::peripherals::[<LPUART $n>] { 65 impl SealedInstance for crate::peripherals::[<LPUART $n>] {
@@ -82,29 +84,23 @@ macro_rules! impl_instance {
82 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance 84 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance
83 = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>]; 85 = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>];
84 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>]; 86 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];
85 const TX_DMA_REQ: u8 = $tx_req; 87 type TxDmaRequest = crate::dma::[<Lpuart $n TxRequest>];
86 const RX_DMA_REQ: u8 = $rx_req; 88 type RxDmaRequest = crate::dma::[<Lpuart $n RxRequest>];
87 } 89 }
88 } 90 }
89 )* 91 )*
90 }; 92 };
91} 93}
92 94
93// DMA request sources from MCXA276 reference manual 95// DMA request sources are now type-safe via associated types.
94// LPUART0: RX=21, TX=22 96// The request source numbers are defined in src/dma.rs:
95// LPUART1: RX=23, TX=24 97// LPUART0: RX=21, TX=22 -> Lpuart0RxRequest, Lpuart0TxRequest
96// LPUART2: RX=25, TX=26 98// LPUART1: RX=23, TX=24 -> Lpuart1RxRequest, Lpuart1TxRequest
97// LPUART3: RX=27, TX=28 99// LPUART2: RX=25, TX=26 -> Lpuart2RxRequest, Lpuart2TxRequest
98// LPUART4: RX=29, TX=30 100// LPUART3: RX=27, TX=28 -> Lpuart3RxRequest, Lpuart3TxRequest
99// LPUART5: RX=31, TX=32 101// LPUART4: RX=29, TX=30 -> Lpuart4RxRequest, Lpuart4TxRequest
100impl_instance!( 102// LPUART5: RX=31, TX=32 -> Lpuart5RxRequest, Lpuart5TxRequest
101 0, 22, 21; 103impl_instance!(0; 1; 2; 3; 4; 5);
102 1, 24, 23;
103 2, 26, 25;
104 3, 28, 27;
105 4, 30, 29;
106 5, 32, 31;
107);
108 104
109// ============================================================================ 105// ============================================================================
110// INSTANCE HELPER FUNCTIONS 106// INSTANCE HELPER FUNCTIONS
@@ -1196,8 +1192,8 @@ impl<'a, T: Instance, C: DmaChannelTrait> LpuartTxDma<'a, T, C> {
1196 self.tx_dma.clear_done(); 1192 self.tx_dma.clear_done();
1197 self.tx_dma.clear_interrupt(); 1193 self.tx_dma.clear_interrupt();
1198 1194
1199 // Set DMA request source from instance type 1195 // Set DMA request source from instance type (type-safe)
1200 self.tx_dma.set_request_source(T::TX_DMA_REQ); 1196 self.tx_dma.set_request_source::<T::TxDmaRequest>();
1201 1197
1202 // Configure TCD for memory-to-peripheral transfer 1198 // Configure TCD for memory-to-peripheral transfer
1203 self.tx_dma 1199 self.tx_dma
@@ -1307,8 +1303,8 @@ impl<'a, T: Instance, C: DmaChannelTrait> LpuartRxDma<'a, T, C> {
1307 self.rx_dma.clear_done(); 1303 self.rx_dma.clear_done();
1308 self.rx_dma.clear_interrupt(); 1304 self.rx_dma.clear_interrupt();
1309 1305
1310 // Set DMA request source from instance type 1306 // Set DMA request source from instance type (type-safe)
1311 self.rx_dma.set_request_source(T::RX_DMA_REQ); 1307 self.rx_dma.set_request_source::<T::RxDmaRequest>();
1312 1308
1313 // Configure TCD for peripheral-to-memory transfer 1309 // Configure TCD for peripheral-to-memory transfer
1314 self.rx_dma 1310 self.rx_dma
@@ -1398,8 +1394,8 @@ impl<'a, T: Instance, C: DmaChannelTrait> LpuartRxDma<'a, T, C> {
1398 // Get the peripheral data register address 1394 // Get the peripheral data register address
1399 let peri_addr = self.info.regs.data().as_ptr() as *const u8; 1395 let peri_addr = self.info.regs.data().as_ptr() as *const u8;
1400 1396
1401 // Configure DMA request source for this LPUART instance 1397 // Configure DMA request source for this LPUART instance (type-safe)
1402 self.rx_dma.set_request_source(T::RX_DMA_REQ); 1398 self.rx_dma.set_request_source::<T::RxDmaRequest>();
1403 1399
1404 // Enable RX DMA request in the LPUART peripheral 1400 // Enable RX DMA request in the LPUART peripheral
1405 self.info.regs.baud().modify(|_, w| w.rdmae().enabled()); 1401 self.info.regs.baud().modify(|_, w| w.rdmae().enabled());