Skip to content

[MCXA] Add DMA1 support#6525

Merged
diondokter merged 6 commits into
embassy-rs:mainfrom
diondokter:mcxa_dma1
Jul 17, 2026
Merged

[MCXA] Add DMA1 support#6525
diondokter merged 6 commits into
embassy-rs:mainfrom
diondokter:mcxa_dma1

Conversation

@diondokter

@diondokter diondokter commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Adds DMA0_8..=11 and DMA1_0..=3 support. This is now generated from the build.rs.

@bramsdell-ms I've tried adding DMA1 to the HAL. Implementation should work, but it doesn't... But I don't really have time to debug.

To reproduce, go to the mcxa5xx examples, dma_mem_to_mem and then change dma0_ch0 to dma1_ch0. Run the example and you'll see a hardfault because of some pointer write.

@diondokter diondokter added the e-mcxa Issues for the NXP MCX-A family of chips label Jul 16, 2026
Comment thread embassy-mcxa/build.rs
Comment on lines +167 to +170
if peripheral.name.to_ascii_lowercase().starts_with("can") {
continue;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping CAN because something in nxp-pac broke. I'm guessing/hoping #6435 fixes that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I patched this in #6524 -- there seems to be a missing config as well as a naming inconsistency

@diondokter diondokter Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! I saw.

Didn't notice your PR. I was guessing you'd be asleep.
Your PR works, but my PR is a more proper durable implementation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree completely that your solution is better -- if we can both extend DMA0 and add DMA1 I'll take it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am intending to fix this directly in nxp-pac (see this PR) as part of #6435. The naming convention used in build.rs where gate.bit == peripheral.name.to_lowercase() is valid for most, but not all, peripherals. Among the latter are CAN0 and CAN1, which is what caused the build errors. The nxp-pac PR adds a bit member to the Gate struct and JSON schema that defaults to peripheral.name.to_lowercase() when unspecified (i.e., the vast majority of cases), but can be manually overwritten in the pac JSONs for outliers (e.g., CAN0/CAN1)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, then let's fix it in the hal when that PR lands

…ent page

dma::init() only enabled the DMA0 clock gate and configured DMA0's management page (MP_CSR). DMA1 was left unclocked and held in reset, so the first write to any DMA1 TCD register faulted. This is why switching dma_mem_to_mem from DMA0_CH0 to DMA1_CH0 hardfaulted.

Enable and reset DMA1 and configure its MP_CSR for mcxa5xx, mirroring the existing DMA0 setup. Verified on FRDM-MCXA577: dma_mem_to_mem now passes mem_to_mem and memset on DMA1_CH0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b465a11b-8384-4457-83e6-314125573eb3
@bramsdell-ms

Copy link
Copy Markdown
Contributor

@diondokter Found it — the hardfault happens because dma::init() never brings up the DMA1 instance.

init() only enables the DMA0 clock gate + releases DMA0 reset (enable_and_reset::<DMA0>) and configures DMA0's management page (DMA0.mp_csr()). DMA1 is left unclocked and held in reset, so the first write to any EDMA_1_TCD register (e.g. the TCD reset in setup_typical) triggers a bus fault → hardfault. That's exactly the "pointer write" fault you saw when switching dma0_ch0dma1_ch0.

The DMA1 gate is already generated by the new build.rs (impl_cc_gate!(DMA1, mrcc_glb_acc0, mrcc_glb_rst0, dma1, NoConfig)), so enable_and_reset::<DMA1> just works. We only need to enable/reset it and configure its MP_CSR, mirroring the DMA0 setup (mcxa5xx only):

diff --git a/embassy-mcxa/src/dma.rs b/embassy-mcxa/src/dma.rs
--- a/embassy-mcxa/src/dma.rs
+++ b/embassy-mcxa/src/dma.rs
@@ use crate::pac::{self, Interrupt};
+#[cfg(feature = "mcxa5xx")]
+use crate::peripherals::DMA1;
 use crate::peripherals::DMA0;
@@ pub(crate) fn init() {
         w.set_gmrc(true);
     });
 
+    // Enable DMA1 clock, release reset, and configure its management page.
+    // Without this, any access to the DMA1 TCD registers faults because the
+    // peripheral is unclocked and held in reset.
+    #[cfg(feature = "mcxa5xx")]
+    {
+        let _ = unsafe { enable_and_reset::<DMA1>(&NoConfig) };
+
+        pac::DMA1.mp_csr().modify(|w| {
+            w.set_edbg(true);
+            w.set_erca(true);
+            w.set_halt(Halt::NormalOperation);
+            w.set_gclc(true);
+            w.set_gmrc(true);
+        });
+    }
+
     // Enable all DMA request lines for non-secure access.
     #[cfg(all(feature = "mcxa5xx", feature = "dma-ipd-req"))]
     for sec_gp_i in 0..=9 {

Verified on an FRDM-MCXA577: with this patch, dma_mem_to_mem using DMA1_CH0 passes both mem-to-mem and memset — no hardfault:

[INFO ] Destination Buffer (after): [1, 2, 3, 4]
[INFO ] PASS: mem_to_mem verified.
[INFO ] Memset Buffer (after): [3735928559, 3735928559, 3735928559, 3735928559]
[INFO ] PASS: memset verified.
[INFO ] === All DMA tests complete ===

Both mcxa5xx and mcxa2xx still build (the DMA1 block is #[cfg(feature = "mcxa5xx")]-gated). Happy to open a PR against your branch if that's easier.

@diondokter

Copy link
Copy Markdown
Contributor Author

Ah well found! Yes please open a PR. If you do that within half an hour I can merge it all just before I go to bed 😇

@bramsdell-ms

Copy link
Copy Markdown
Contributor

Done — opened diondokter#3 against your mcxa_dma1 branch with the fix. 🚀

mcxa: fix DMA1 hardfault by initializing its clock, reset and management page
@diondokter
diondokter marked this pull request as ready for review July 16, 2026 22:09
@diondokter

Copy link
Copy Markdown
Contributor Author

Hmm fmt failed. Sorry, but I need to go to bed now. I'll fix it tomorrow. Or you need @felipebalbi to fix and merge it

Sort the peripherals imports so DMA0 precedes the cfg-gated DMA1, fixing the rustfmt CI failure on the merged DMA1 fix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b465a11b-8384-4457-83e6-314125573eb3
@bramsdell-ms

Copy link
Copy Markdown
Contributor

Formatting slipped through — the imports weren't run through nightly rustfmt, which is why CI is red. One-line fix in diondokter#4 (reorders \DMA0/\DMA1\ imports). \cargo +nightly fmt --check\ is clean with it. No rush if you're already off — it'll be there in the morning. 🌙

mcxa: rustfmt DMA1 import ordering
@diondokter
diondokter added this pull request to the merge queue Jul 17, 2026
Merged via the queue into embassy-rs:main with commit 461258b Jul 17, 2026
7 checks passed
@diondokter
diondokter deleted the mcxa_dma1 branch July 17, 2026 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e-mcxa Issues for the NXP MCX-A family of chips

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants