Verdin IMX8MM, hardfault when initializing UART on M4 core

We are having some issues getting a UART (more specifically the 2 that are not used for terminals of both processors) initialized on the M4 core of the verdin IMX8MM.


  • Verdin iMX8M Mini DualLite
  • Custom carrier board
  • Device tree for A53 with both UART’s (UART1 and UART2) disabled
  • J-link debugger connected

We have tried adding the UART driver to our application and the iuart_interrupt_transfer example. In both cases if we change the UART from uart4 to uart2 in the board, pinmux and main files we get a hardfault error that is thrown when trying to run the following line in the uart_init function of the driver:


What are we missing here? Is there maybe something in the device tree that still needs to be disabled or added to make this work? Or is this an issue with UART2 and UART3 not being in the correct RDC?

1 Like

Hi @Jorn,

Are you using BSP 6/ TorizonCore 6?

If so, can you please check adding the overlay verdin-imx8mm_hmp_overlay.dts?

Can you please share your device tree code with us? There might be something going on with the RDC that didn’t correctly the domain for your Cortex-M.

Best Regards,

Hi @hfranco.tx

We are using BSP 6 with Yocto. My colleague did a quick check and this overlay is already active (verdin-imx8mm_rpmsg.dts) in our project.

These are our device tree files on top of the standard ones for BSP 6 for the Verdin board. We started from the verdin non-wifi development board device tree files:

em-sdc_v2.0.0_23.10.dts (267 Bytes)
em-sdc.dtsi (14.0 KB)

We have also found some information regarding the “Modify the TF-A code to assign peripherals to the correct RDC domain” from another source and building the u-boot to change this ATF/TF-A part ( I added both links below). Could this be an issue or should this already be ok?

Best regards,

Hi @Jorn,

This should be “verdin-imx8mm_hmp_overlay.dts”, can you double-check, please?

The RDC for the mini shouldn’t be an issue. If you’re not using the verdin-imx8mm_hmp_overlay.dts overlay, can you share with us which overlay are you using? Also, the code that you’re using.

Best Regards,

Dear @Jorn,

Hope you are doing fine. Do you have any updates on this topic?

@hfranco.tx @rudhi.tx

Sorry for the long wait for the reply back. I had a week and a half vacation planned and couldn’t get back to you.

We checked the “verdin-imx8mm_hmp_overlay.dts”, this is not in our project but we do have the following file:
verdin-imx8mm_rpmsg.dts (1.3 KB)
This file looks exactly the same as the one mentioned.

The code we used is: (762.4 KB)
We made changes to the board.c, the main file and the pinmux.c file to use UART2 instead of UART4. This causes a hardfault error.

Hi @Jorn,

Don’t worry about that.

Looks like the same, yes, but I would recommend if possible you use our overlay because if we update anything on the overlay you’ll also get our updates automatically.

I’ll try to change the UART on my side as well. Can you please share with us the error you’re seeing? is it a kernel panic?

Best Regards,

Hi @hfranco.tx ,

Ok, we’ll add that overlay to our project to make it future proof.

The error we get is a hardfault error that goes straight into the assembly code for the hardfault. This happens immediately after the driver tries to write the first register during initialization.
I don’t have more information from the error, but since it happens after we try to write to the UART register for the first time it looks like we don’t have access to it.

Best regards,

Hi @Jorn,

I was able to reproduce the error on my side, I got the same hard fault in the assembly code. My assumptions are:

  • The UART is being used already, so we need to specify to the RDC (resource domain controller) that we want to use it on the Cortex-M side.
  • Or, the UART is not initialized yet, so we need to initialize it first.

I’m not sure right now what is happening, but I will update you if I manage to make progress.

Best Regards,

1 Like

Hi @hfranco.tx
I share some details from my side (on a iMX8MP. but it’s almost the same as for the iMX8MM).
the Application Processor Reference Manual from NXP has a chapter describing the Resource Domain Controller.
It lists a full set of Peripheral Domain Access Permissions registers (RDC_PDAPn) that are used to:

  • give Read and/or Write access to the peripheral to the various domains (bits DxR and DxW)
  • enforce the Semaphore Required (SREQ bit)
    The same section describes the Reset values of all the registers (i.e., the values of all the registers when the processor jumps out of reset).
    And, for the UART2 this is R/W access to all the domains, without any semaphore.

As far as I can see, the Cortex-M example doesn’t ovewrite these reset values, and so I think that the case

  • The UART is being used already, so we need to specify to the RDC (resource domain controller) that we want to use it on the Cortex-M side.

is only possible if Cortex-A has overwritten them, reserving to Cortex-A domain, or enforcing a semaphore requirement.
This means that either U-Boot or TorizonCore are responsible for this.
In my case, the crash happens when the Linux driver is loaded, and so I suppose that the responsible is TorizonCore.

Hi @vix and @hfranco.tx,

So it’s very likely that we will have to fix this by using the method I described in my first reply (3rd post) by rebuilding the U-Boot because of the change we need to do in the ARM trusted firmware file (ATF/TF-A)?

Best regards,

This is possible @Jorn
And the link you posted is valuable.
I’ve just found this topic Verdin-imx8mm - Torizon - Accessing UART on Cortex-M4 (UART2) on Verdin Developentboard V1.1->(RS485 UART_1) causes Hard Fault and it seems that a U-Boot rebuild is necessary.

But if an U-Boot rebuild is necessary in every case the list of peripherals assigned to Cortex-M is not those included in the default, the TorizonCore philosophy (stay focus on the application, and not on the OS) is a little bit decreased.
If this task could be handled by ApolloX + TCB, this would be great!
I’ve already written this somewhere here, but I cannot find the post anymore.

But the most important thing is finding a reliable way to reserve peripherals to Cortex-M (even if a little bit annoying or time-consuming)

Hi @vix and @Jorn,

Good news, I was able to change from UART4 to UART3 (which is the UART2 of the board), only changing the Cortex-M code. It turns out I was missing the new clock config.

Here is the patch:

diff --git a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/board.c b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/board.c
index 2228b55..832015d 100644
--- a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/board.c
+++ b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/board.c
@@ -24,7 +24,7 @@
 void BOARD_InitDebugConsole(void)
     uint32_t uartClkSrcFreq = BOARD_DEBUG_UART_CLK_FREQ;
-    CLOCK_EnableClock(kCLOCK_Uart4);
+    CLOCK_EnableClock(kCLOCK_Uart3);
 /* Initialize MPU, configure non-cacheable memory */
diff --git a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/board.h b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/board.h
index cee60e1..0b9ca6c 100644
--- a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/board.h
+++ b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/board.h
@@ -19,13 +19,13 @@
 /* The UART to use for debug messages. */
 #define BOARD_DEBUG_UART_TYPE     kSerialPort_Uart
 #define BOARD_DEBUG_UART_CLK_FREQ                                                           \
-    CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) / (CLOCK_GetRootPreDivider(kCLOCK_RootUart4)) / \
-        (CLOCK_GetRootPostDivider(kCLOCK_RootUart4)) / 10
-#define BOARD_UART_IRQ         UART4_IRQn
+    CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) / (CLOCK_GetRootPreDivider(kCLOCK_RootUart3)) / \
+        (CLOCK_GetRootPostDivider(kCLOCK_RootUart3)) / 10
+#define BOARD_UART_IRQ         UART3_IRQn
 #define GPV5_BASE_ADDR        (0x32500000)
 #define FORCE_INCR_OFFSET     (0x4044)
diff --git a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/clock_config.c b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/clock_config.c
index eb851c1..d4e5826 100644
--- a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/clock_config.c
+++ b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/clock_config.c
@@ -99,8 +99,8 @@ void BOARD_BootClockRUN(void)
     //    CLOCK_SetRootDivider(kCLOCK_RootAxi, 1U, 2);
     //    CLOCK_SetRootMux(kCLOCK_RootAxi, kCLOCK_AxiRootmuxSysPll1); /* switch AXI to SYSTEM PLL1 800MHZ */
-    CLOCK_SetRootMux(kCLOCK_RootUart4, kCLOCK_UartRootmuxSysPll1Div10); /* Set UART source to SysPLL1 Div10 80MHZ */
-    CLOCK_SetRootDivider(kCLOCK_RootUart4, 1U, 1U);                     /* Set root clock to 80MHZ/ 1= 80MHZ */
+    CLOCK_SetRootMux(kCLOCK_RootUart3, kCLOCK_UartRootmuxSysPll1Div10); /* Set UART source to SysPLL1 Div10 80MHZ */
+    CLOCK_SetRootDivider(kCLOCK_RootUart3, 1U, 1U);                     /* Set root clock to 80MHZ/ 1= 80MHZ */
     CLOCK_EnableClock(kCLOCK_Rdc); /* Enable RDC clock */
     /* The purpose to enable the following modules clock is to make sure the M4 core could work normally when A53 core
diff --git a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/hello_world.c b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/hello_world.c
index d1f35d9..3fb5d77 100644
--- a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/hello_world.c
+++ b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/hello_world.c
@@ -29,8 +29,6 @@
 int main(void)
-    char ch;
     /* Init board hardware. */
     /* Board specific RDC settings */
@@ -40,11 +38,14 @@ int main(void)
-    PRINTF("hello world.\r\n");
+    uint8_t counter = 0;
+    int i;
     while (1)
-        ch = GETCHAR();
-        PUTCHAR(ch);
+        if (counter == 0xff) counter = 0;
+        else counter++;
+        PRINTF("hello_world %d\r\n", counter);
+        for (i = 0; i < 10000000; i++);
diff --git a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/pin_mux.c b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/pin_mux.c
index b7f21c6..6572cd5 100644
--- a/boards/evkmimx8mm/demo_apps/hello_world_change_uart/pin_mux.c
+++ b/boards/evkmimx8mm/demo_apps/hello_world_change_uart/pin_mux.c
@@ -55,12 +55,12 @@ BOARD_InitPins:
  * END ****************************************************************************************************************/
 void BOARD_InitPins(void) {                                /*!< Function assigned for the core: Cortex-M4[m4] */
                         IOMUXC_SW_PAD_CTL_PAD_DSE(6U) |
                         IOMUXC_SW_PAD_CTL_PAD_DSE(6U) |

So a couple of things to notice: I created a new project called hello_world_change_uart, copied hello_world from NXP, and then I applied this patch. Also, I’ve changed the hello_word.c a little bit to keep printing “hello world” on my screen.

Please note the most important things: configuring the right clock to use the base UART3 and also changing the pin to the correct name. All pin names are available on the pin_mux files from the SDK (should be inside the hello_world project).

In my case, I wanted to use UART2 from the Verdin Development Board (SODIMM_139 or UART_2_TXD). Note that this pin routes to ECSPI1_MOSI UART3:


That’s why I changed to code to UART3 but I’m connected to UART2 of my board:

I loaded the code with u-boot and it just worked. I can see the messages being printed and I can also boot Linux without problems. Keep in mind that it’s recommended to disable this UART to prevent errors on the Linux side.

I’ve used the reference multimedia image 6.3 on my verdin Mini. I’ll now test it on TorizonCore, but I believe it will also work. @vix although this is for Verdin Mini, it should be the same procedure for the Verdin Plus.

Hope this helps, let me know if you have any questions.

Best Regards,

1 Like

Update form side: Tested with TorizonCore and it also works.

Best Regards,

Hi @hfranco.tx,

Sounds very promising, I’ll be testing this solution this week so I will keep you posted what the results are on my side.

Best regards,

1 Like

Hi guys! Signed up specifically to point out an inaccuracy in this excellent answer, hopefully this saves someone many hours. The RX pin assignment is wrong, must be like this (looked in linux DTS):


May be this applied only for NXP iMX8MM, but let’s keep it here for everyone.

The RX pin assignment is wrong, must be like this (looked in linux DTS):

Files pin_mux.c and pin_mux.h can be easily generated using software MCUXpresso Config Tools from NXP.
Based on my experience it’s the best way to have consistent pin setup and assignment.

1 Like