diff options
| author | Bogdan Petru Chircu Mare <[email protected]> | 2025-12-01 19:14:22 -0800 |
|---|---|---|
| committer | Bogdan Petru Chircu Mare <[email protected]> | 2025-12-01 19:14:22 -0800 |
| commit | a18cf2fd0d8158f4b1f24f8982e187477b58267c (patch) | |
| tree | e0567be32a6d25109eee73f607dd59eebfc84deb | |
| parent | bc21f9b35e09fe9ef2556adf53f1d600af909d03 (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.rs | 134 | ||||
| -rw-r--r-- | src/lpuart/mod.rs | 54 |
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 { | |||
| 341 | pub const DMA_MAX_TRANSFER_SIZE: usize = 0x7FFF; | 341 | pub 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. |
| 348 | pub const DMA_REQ_LPUART0_RX: u8 = 21; | 348 | /// |
| 349 | pub const DMA_REQ_LPUART0_TX: u8 = 22; | 349 | /// Each peripheral that can trigger DMA requests implements this trait |
| 350 | pub const DMA_REQ_LPUART1_RX: u8 = 23; | 350 | /// with marker types that encode the correct request source number at |
| 351 | pub const DMA_REQ_LPUART1_TX: u8 = 24; | 351 | /// compile time. This prevents using the wrong request source for a |
| 352 | pub const DMA_REQ_LPUART2_RX: u8 = 25; | 352 | /// peripheral. |
| 353 | pub const DMA_REQ_LPUART2_TX: u8 = 26; | 353 | /// |
| 354 | pub const DMA_REQ_LPUART3_RX: u8 = 27; | 354 | /// # Example |
| 355 | pub const DMA_REQ_LPUART3_TX: u8 = 28; | 355 | /// |
| 356 | pub const DMA_REQ_LPUART4_RX: u8 = 29; | 356 | /// ```ignore |
| 357 | pub const DMA_REQ_LPUART4_TX: u8 = 30; | 357 | /// // The LPUART2 RX request source is automatically derived from the type: |
| 358 | pub const DMA_REQ_LPUART5_RX: u8 = 31; | 358 | /// channel.set_request_source::<Lpuart2RxRequest>(); |
| 359 | pub 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)] | ||
| 363 | pub 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. | ||
| 372 | macro_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) | ||
| 387 | define_dma_request!( | ||
| 388 | /// DMA request source for LPUART0 RX. | ||
| 389 | Lpuart0RxRequest = 21 | ||
| 390 | ); | ||
| 391 | define_dma_request!( | ||
| 392 | /// DMA request source for LPUART0 TX. | ||
| 393 | Lpuart0TxRequest = 22 | ||
| 394 | ); | ||
| 395 | define_dma_request!( | ||
| 396 | /// DMA request source for LPUART1 RX. | ||
| 397 | Lpuart1RxRequest = 23 | ||
| 398 | ); | ||
| 399 | define_dma_request!( | ||
| 400 | /// DMA request source for LPUART1 TX. | ||
| 401 | Lpuart1TxRequest = 24 | ||
| 402 | ); | ||
| 403 | define_dma_request!( | ||
| 404 | /// DMA request source for LPUART2 RX. | ||
| 405 | Lpuart2RxRequest = 25 | ||
| 406 | ); | ||
| 407 | define_dma_request!( | ||
| 408 | /// DMA request source for LPUART2 TX. | ||
| 409 | Lpuart2TxRequest = 26 | ||
| 410 | ); | ||
| 411 | define_dma_request!( | ||
| 412 | /// DMA request source for LPUART3 RX. | ||
| 413 | Lpuart3RxRequest = 27 | ||
| 414 | ); | ||
| 415 | define_dma_request!( | ||
| 416 | /// DMA request source for LPUART3 TX. | ||
| 417 | Lpuart3TxRequest = 28 | ||
| 418 | ); | ||
| 419 | define_dma_request!( | ||
| 420 | /// DMA request source for LPUART4 RX. | ||
| 421 | Lpuart4RxRequest = 29 | ||
| 422 | ); | ||
| 423 | define_dma_request!( | ||
| 424 | /// DMA request source for LPUART4 TX. | ||
| 425 | Lpuart4TxRequest = 30 | ||
| 426 | ); | ||
| 427 | define_dma_request!( | ||
| 428 | /// DMA request source for LPUART5 RX. | ||
| 429 | Lpuart5RxRequest = 31 | ||
| 430 | ); | ||
| 431 | define_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 | ||
| 21 | use crate::dma::{Channel as DmaChannelTrait, DmaChannel, EnableInterrupt, RingBuffer, DMA_MAX_TRANSFER_SIZE}; | 21 | use 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 { | |||
| 50 | pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> { | 52 | pub 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 | ||
| 59 | macro_rules! impl_instance { | 61 | macro_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 |
| 100 | impl_instance!( | 102 | // LPUART5: RX=31, TX=32 -> Lpuart5RxRequest, Lpuart5TxRequest |
| 101 | 0, 22, 21; | 103 | impl_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()); |
