Apalis i.MX8QM UART3 receive issue on M4_0 (TX works, RX not working)

Hello Toradex Support Team,

I’m trying to configure UART3 (DMA_UART0) on the Apalis i.MX8QM with the Ixora carrier board, so that it can be used as the debug console for Cortex-M4 core 0.

What I have done so far

  • SCFW configuration: Edited board_system_config() to add the UART0 resource (SC_R_UART_0) and pads (SC_P_UART0_RX, SC_P_UART0_TX) to the M4_0 partition.
/*--------------------------------------------------------------------------*/
/* Configure the system (inc. additional resource partitions)               */
/*--------------------------------------------------------------------------*/
void board_system_config(sc_bool_t early, sc_rm_pt_t pt_boot)
{
    sc_err_t err = SC_ERR_NONE;

    /* This function configures the system. It usually partitions
       resources according to the system design. It must be modified by
       customers. Partitions should then be specified using the mkimage
       -p option. */

    /* Note the configuration here is for NXP test purposes */

    sc_bool_t alt_config = SC_FALSE;
    sc_bool_t no_ap = SC_FALSE;
    sc_bool_t ddrtest = SC_FALSE;

    /* Get boot parameters. See the Boot Flags section for definition
       of these flags.*/
    boot_get_data(NULL, NULL, NULL, NULL, NULL, NULL, &alt_config,
        NULL, &ddrtest, &no_ap, NULL);

    board_print(3, "board_system_config(%d, %d)\n", early, alt_config);

    /* Name default partitions */
    PARTITION_NAME(SC_PT, "SCU");
    PARTITION_NAME(SECO_PT, "SECO");
    PARTITION_NAME(pt_boot, "BOOT");

    /* Configure initial resource allocation (note additional allocation
       and assignments can be made by the SCFW clients at run-time */
    if (alt_config != SC_FALSE)
    {
        sc_rm_pt_t pt_m4_0 = SC_RM_NUM_PARTITION;
        sc_rm_pt_t pt_m4_1 = SC_RM_NUM_PARTITION;

        #ifdef BOARD_RM_DUMP
            rm_dump(pt_boot);
        #endif

        /* Name boot partition */
        PARTITION_NAME(pt_boot, "AP0");

        /* Create M4 0 partition */
        if (rm_is_resource_avail(SC_R_M4_0_PID0) != SC_FALSE)
        {
            sc_rm_mr_t mr;

            /* List of resources */
            static const sc_rsrc_t rsrc_list[9U] =
            {
                SC_R_SYSTEM,
                SC_R_IRQSTR_M4_0,
                SC_R_UART_0,
                SC_R_CAN_0,
                SC_R_MU_5B,
                SC_R_MU_7A,
                SC_R_MU_8B,
                SC_R_GPT_4,
                SC_R_SECO_MU_4
            };

            /* List of pads */
            static const sc_pad_t pad_list[6U] =
            {
                RM_RANGE(SC_P_M40_I2C0_SCL, SC_P_M40_GPIO0_01),
                RM_RANGE(SC_P_UART0_RX, SC_P_UART0_TX),
                RM_RANGE(SC_P_FLEXCAN0_RX, SC_P_FLEXCAN0_TX)
            };

            /* List of memory regions */
            static const sc_rm_mem_list_t mem_list[2U] =
            {
                {0x088000000ULL, 0x0887FFFFFULL},
                {0x008081000ULL, 0x008180FFFULL}
            };

            /* Create partition */
            BRD_ERR(rm_partition_create(pt_boot, &pt_m4_0, SC_FALSE,
                SC_TRUE, SC_FALSE, SC_TRUE, SC_FALSE, SC_R_M4_0_PID0,
                rsrc_list, ARRAY_SIZE(rsrc_list),
                pad_list, ARRAY_SIZE(pad_list),
                mem_list, ARRAY_SIZE(mem_list)));

            /* Name partition for debug */
            PARTITION_NAME(pt_m4_0, "MCU0");
            
            /* Allow AP to use SYSTEM (not production!) */
            BRD_ERR(rm_set_peripheral_permissions(SC_PT, SC_R_SYSTEM,
                pt_boot, SC_RM_PERM_SEC_RW));

            /* Move M4 0 TCM */
            BRD_ERR(rm_find_memreg(pt_boot, &mr, 0x034FE0000ULL,
                0x034FE0000ULL));
            BRD_ERR(rm_assign_memreg(pt_boot, pt_m4_0, mr));

            /* Move partition to be owned by SC */
            BRD_ERR(rm_set_parent(pt_boot, pt_m4_0, SC_PT));

            /* Check if booting with the no_ap flag set */
            if (no_ap != SC_FALSE)
            {
                /* Move boot to be owned by M4 0 for Android Automotive */
                BRD_ERR(rm_set_parent(SC_PT, pt_boot, pt_m4_0));
            }
        }

        /* Create M4 1 partition */
        if (rm_is_resource_avail(SC_R_M4_1_PID0) != SC_FALSE)
        {
            sc_rm_mr_t mr;

            /* List of resources */
            static const sc_rsrc_t rsrc_list[9U] =
            {
                SC_R_IRQSTR_M4_1,
                SC_R_UART_2,
                SC_R_MU_6B,
                SC_R_MU_7B,
                SC_R_MU_9B,
                SC_R_GPT_3,
                RM_RANGE(SC_R_CAN_1, SC_R_CAN_2),
                SC_R_FSPI_0
            };

            /* List of pads */
            static const sc_pad_t pad_list[8U] =
            {
                RM_RANGE(SC_P_M41_I2C0_SCL, SC_P_M41_GPIO0_01),
                RM_RANGE(SC_P_LVDS0_I2C1_SCL,SC_P_LVDS0_I2C1_SDA),
                RM_RANGE(SC_P_FLEXCAN1_RX, SC_P_FLEXCAN2_TX),
                RM_RANGE(SC_P_QSPI0A_DATA0, SC_P_COMP_CTL_GPIO_1V8_3V3_QSPI0)
            };

            /* List of memory regions */
            static const sc_rm_mem_list_t mem_list[2U] =
            {
                {0x088800000ULL, 0x08FFFFFFFULL},
                {0x008181000ULL, 0x008280FFFULL}
            };

            /* Create partition */
            BRD_ERR(rm_partition_create(pt_boot, &pt_m4_1, SC_FALSE,
                SC_TRUE, SC_FALSE, SC_TRUE, SC_FALSE, SC_R_M4_1_PID0,
                rsrc_list, ARRAY_SIZE(rsrc_list),
                pad_list, ARRAY_SIZE(pad_list),
                mem_list, ARRAY_SIZE(mem_list)));

            /* Name partition for debug */
            PARTITION_NAME(pt_m4_1, "MCU1");
            
            /* Allow M4 1 to use SYSTEM (not production!) */
            BRD_ERR(rm_set_peripheral_permissions(SC_PT, SC_R_SYSTEM,
                pt_m4_1, SC_RM_PERM_SEC_RW));

            /* Move M4 1 TCM */
            BRD_ERR(rm_find_memreg(pt_boot, &mr, 0x038FE0000ULL,
                0x038FE0000ULL));
            BRD_ERR(rm_assign_memreg(pt_boot, pt_m4_1, mr));

            /* Move partition to be owned by SC */
            BRD_ERR(rm_set_parent(pt_boot, pt_m4_1, SC_PT));
        }

        /* Create partition for shared/hidden resources */
        {
            sc_rm_pt_t pt;
            sc_rm_mr_t mr;

            /* List of resources */
            static const sc_rsrc_t rsrc_list[4U] =
            {
                RM_RANGE(SC_R_M4_0_PID1, SC_R_M4_0_PID4),
                RM_RANGE(SC_R_M4_1_PID1, SC_R_M4_1_PID4)
            };

            /* List of memory regions */
            static const sc_rm_mem_list_t mem_list[1U] =
            {
                {0x090000000ULL, 0x091FFFFFFULL}
            };

            /* Create shared partition */
            BRD_ERR(rm_partition_create(SC_PT, &pt, SC_FALSE, SC_TRUE,
                SC_FALSE, SC_FALSE, SC_FALSE, SC_NUM_RESOURCE,
                rsrc_list, ARRAY_SIZE(rsrc_list), NULL, 0U,
                mem_list, ARRAY_SIZE(mem_list)));

            /* Name partition for debug */
            PARTITION_NAME(pt, "Shared");
            
            /* Share memory space */
            BRD_ERR(rm_find_memreg(SC_PT, &mr,
                mem_list[0U].addr_start, mem_list[0U].addr_start));
            BRD_ERR(rm_set_memreg_permissions(pt, mr, pt_boot,
                SC_RM_PERM_FULL));
            if (pt_m4_0 != SC_RM_NUM_PARTITION)
            {
                BRD_ERR(rm_set_memreg_permissions(pt, mr, pt_m4_0,
                    SC_RM_PERM_FULL));
            }
            if (pt_m4_1 != SC_RM_NUM_PARTITION)
            {
                BRD_ERR(rm_set_memreg_permissions(pt, mr, pt_m4_1,
                    SC_RM_PERM_FULL));
            }
        }

        /* Allow all to access the SEMA42s */
        BRD_ERR(rm_set_peripheral_permissions(SC_PT, SC_R_M4_0_SEMA42,
            SC_RM_PT_ALL, SC_RM_PERM_FULL));
        BRD_ERR(rm_set_peripheral_permissions(SC_PT, SC_R_M4_1_SEMA42,
            SC_RM_PT_ALL, SC_RM_PERM_FULL));

        #ifdef BOARD_RM_DUMP
            rm_dump(pt_boot);
        #endif
    }
}
  • Rebuilt the boot image with:
make SOC=iMX8QM flash_regression_linux_m4
  • For the m4 firmware, I use the MCUXpresso SDK hello_world example. Modified board.h to use DMA__LPUART0.
#if defined(MIMX8QM_CM4_CORE0)
#define BOARD_DEBUG_UART_BASEADDR (uint32_t) DMA__LPUART0
#define BOARD_DEBUG_UART_INSTANCE 2U
#define BOARD_DEBUG_UART_SC_RSRC  SC_R_UART_0
#define BOARD_DEBUG_UART_CLKSRC   kCLOCK_DMA_Lpuart0
#define BOARD_UART_IRQ            DMA_UART0_INT_IRQn
#define BOARD_UART_IRQ_HANDLER    DMA_UART0_INT_IRQHandler
#define BOARD_M4_CPU_RSRC         SC_R_M4_0_PID0
  • Updated pin_mux.h/c to configure UART0 RX/TX pins (SC_P_UART0_RX, SC_P_UART0_TX). BOARD_InitPins() calls sc_pad_set_all() for RX and TX pads.
/* UART0_TX (number AV48), FTDI_UART0_TX */
#define BOARD_INITPINS_FTDI_UART0_TX_PERIPHERAL                   DMA__UART0   /*!< Peripheral   name */
#define BOARD_INITPINS_FTDI_UART0_TX_SIGNAL                          uart_tx   /*!< Signal name */
#define BOARD_INITPINS_FTDI_UART0_TX_PIN_NAME                       UART0_TX   /*!< Routed pin name */
#define BOARD_INITPINS_FTDI_UART0_TX_PIN_FUNCTION_ID           SC_P_UART0_TX   /*!< Pin function id */
#define BOARD_INITPINS_FTDI_UART0_TX_LABEL                   "FTDI_UART0_TX"   /*!< Label */
#define BOARD_INITPINS_FTDI_UART0_TX_NAME                    "FTDI_UART0_TX"   /*!< Identifier */

/* UART0_RX (number AV50), FTDI_UART0_RX */
#define BOARD_INITPINS_FTDI_UART0_RX_PERIPHERAL                   DMA__UART0   /*!< Peripheral name */
#define BOARD_INITPINS_FTDI_UART0_RX_SIGNAL                          uart_rx   /*!< Signal name */
#define BOARD_INITPINS_FTDI_UART0_RX_PIN_NAME                       UART0_RX   /*!< Routed pin name */
#define BOARD_INITPINS_FTDI_UART0_RX_PIN_FUNCTION_ID           SC_P_UART0_RX   /*!< Pin function id */
#define BOARD_INITPINS_FTDI_UART0_RX_LABEL                   "FTDI_UART0_RX"   /*!< Label */
#define BOARD_INITPINS_FTDI_UART0_RX_NAME                    "FTDI_UART0_RX"   /*!< Identifier */
void BOARD_InitPins(sc_ipc_t ipc)                          /*!< Function assigned for the core: Cortex-M4F[cm4_core0] */
{
    sc_err_t err = SC_ERR_NONE;

    err = sc_pad_set_all(ipc, BOARD_INITPINS_FTDI_UART0_RX_PIN_FUNCTION_ID, 0U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);/* IOMUXD_UART0_RX register modification value */
    if (SC_ERR_NONE != err)
    {
        assert(false);
    }  
    err = sc_pad_set_all(ipc, BOARD_INITPINS_FTDI_UART0_TX_PIN_FUNCTION_ID, 0U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);/* IOMUXD_UART0_TX register modification value */
    if (SC_ERR_NONE != err)
    {
        assert(false);
    }
}
  • The main program remains unchanged. Main loop simply does:
ch = GETCHAR();
PUTCHAR(ch);

The issue

  • TX works: “hello world.” is printed on the serial terminal.
  • RX does not work: characters typed in the terminal are not echoed back.

Thanks in advance!

apalis-imx8_hmp_overlay.dts (2.4 KB)

Hi, I’m the Toradex AI Assistant.

I might make mistakes. Our support team will step in if needed. If I answered your question, please tick the “Solution” checkbox. For follow-up questions to me, please use @ToradexAI in your post.

Did you like my answer? :+1: :-1:


(1), (2), (3), (4), (5), (6)

UART3 (DMA_UART0) Configuration for Cortex-M4 Core 0 Debug Console

Based on your configuration, you’re on the right track with your SCFW and pin mux setup. However, there are a few areas that need attention for proper UART RX functionality.

Hardware Setup Verification

First, ensure your hardware connections are correct. For Apalis iMX8 with the Evaluation Board, you need to connect your USB-TTL converter to the appropriate pins (2):

USB-TTL Signal Apalis Eval Board (M4_0)
TX MXM3_110
RX MXM3_120
GND SoC GND

Device Tree Overlay Requirements

Since you’re using UART pins that may conflict with Linux, you need to apply a device tree overlay to prevent Linux from claiming these pins (2). For Apalis iMX8 Core 0, use this overlay:

/dts-v1/;
/plugin/;

/* Disable UART1 Control Pins to use M4 Core 0 and 1 UART */

/ {
	compatible = "toradex,apalis-imx8";
};

&iomuxc {
	pinctrl-0 = <&pinctrl_cam1_gpios>, <&pinctrl_dap1_gpios>,
		    <&pinctrl_esai0_gpios>, <&pinctrl_fec2_gpios>,
		    <&pinctrl_gpio3>, <&pinctrl_gpio4>, <&pinctrl_gpio_keys>,
		    <&pinctrl_gpio_usbh_oc_n>,
		    <&pinctrl_lvds0_i2c0_gpio>, <&pinctrl_lvds1_i2c0_gpios>,
		    <&pinctrl_mipi_dsi_0_1_en>, <&pinctrl_mipi_dsi1_gpios>,
		    <&pinctrl_mlb_gpios>, <&pinctrl_qspi1a_gpios>,
		    <&pinctrl_sata1_act>, <&pinctrl_sim0_gpios>,
		    <&pinctrl_usdhc1_gpios>;
};

(2)

SCFW Configuration Check

Your SCFW configuration looks correct, but ensure you’re building with the proper flags (3):

$ make clean; make SOC=MX8QM B=apalis DL=2 R=b0 U=0 V=0 qm

(3)

Pin Mux Configuration Review

Your pin mux configuration appears correct, but verify that the pin function IDs match exactly with the hardware documentation. The pins should be configured as function 0 for UART functionality (2).

Firmware Loading Process

Ensure you’re loading the firmware correctly (1):

> setenv load_cmd "ext4load mmc 0:2"
> setenv m4_0_image "/home/root/hello_world.bin"
> saveenv
> run m4boot_0

(1)

Debugging Steps

  1. Verify SCFW Build: Ensure your custom SCFW is properly loaded and the UART0 resources are correctly partitioned to the M4_0 core.

  2. Check Pin Mapping: Verify the physical pin connections match the Ixora carrier board pinout (5).

  3. Test with Loopback: Try connecting TX and RX pins together to test if the UART peripheral is functioning correctly.

  4. JTAG Debugging: Consider using JTAG debugging to verify the UART peripheral registers are properly configured (4).

The most likely issue is that the device tree overlay isn’t applied, causing Linux to interfere with the UART pins. Apply the overlay and ensure it’s loaded during boot.

Best regards, Toradex AI Assistant.

Hey @rvrse,

I’m currently investigating the issue. I’m doing some tests, and will come back to you soon.

Best regards,

Hey, @rvrse,

To verify the functionality of the Cortex-M, I suggest running the same hello world example as you did without the device tree overlay. It’s something similar to what the AI suggested, but with an additional step:

> setenv load_cmd "ext4load mmc 0:2"
> setenv m4_0_image "/home/root/hello_world_m40.bin"
> setenv loadm4image_0 '${load_cmd} ${loadaddr} ${m4_0_image}'
> saveenv
> run m4boot_0

I ran the example without the device tree overlay and using the default SCFW and U-Boot from our Reference Minimal Image, and both TX and RX work properly (I see both ‘hello world.’ printed and the typed characters are echoed back).

This suggests the cause for the issue is either in your custom SCFW build or in the overlay. We still do not have a ready-to-use HMP overlay for the Apalis iMX8, so we don’t have a standard to compare your custom implementation with.

I suggest firstly following the same procedure as I did in a Reference Minimal image and see if you get the same results, more details are provided in our article on MCUXpresso and on How to Load Compiled Binaries into Cortex-M. If you get both TX and RX working, add your custom SCFW, test it, and then use the overlay (or vice-versa). This will help identify where the root cause of the issue is. Let me know if you get any further insights or encounter other issues.

Best regards,

Hi @leonardo.costa.tx

Thanks for your response.
I want to clarify that I am using custom SCFW as suggested by NXP community because of the issue where we face bootloop after load m4 binary from u-boot using BSP 7. Actually, I have also tried disabling the HMP overlay but it still doesn’t work.

root@apalis-imx8-14904109:~# tdx-info 

Software summary
------------------------------------------------------------
Bootloader:               U-Boot
Kernel version:           6.6.94-rt56-7.4.0-devel #1 SMP PREEMPT_RT Tue Jul 29 12:48:48 UTC 2025
Kernel command line:      root=PARTUUID=2122ce7c-02 ro rootwait console=tty1 console=ttyLP1,115200
Distro name:              NAME="TDX Wayland with XWayland RT"
Distro version:           VERSION_ID=7.4.0-devel-20250903110344-build.0
Distro variant:           -
Hostname:                 apalis-imx8-14904109
------------------------------------------------------------

Hardware info
------------------------------------------------------------
HW model:                 Toradex Apalis iMX8QM V1.1 on Apalis Evaluation Board
Toradex version:          0067 V1.1A
Serial number:            14904109
Processor arch:           aarch64
------------------------------------------------------------
root@apalis-imx8-14904109:~# tdx-info -dt

Device tree
------------------------------------------------------------
Device tree enabled:      imx8qm-apalis-v1.1-eval.dtb
Compatible string:        toradex,apalis-imx8-v1.1-evaltoradex,apalis-imx8-v1.1fsl,imx8qm
Device trees available:
                          imx8qm-apalis-eval-v1.2.dtb
                          imx8qm-apalis-eval.dtb
                          imx8qm-apalis-ixora-v1.1.dtb
                          imx8qm-apalis-v1.1-eval-v1.2.dtb
                          imx8qm-apalis-v1.1-eval.dtb
                          imx8qm-apalis-v1.1-ixora-v1.1.dtb
                          imx8qm-apalis-v1.1-ixora-v1.2.dtb
                          imx8qp-apalis-v1.1-eval-v1.2.dtb
                          imx8qp-apalis-v1.1-eval.dtb
                          imx8qp-apalis-v1.1-ixora-v1.1.dtb
                          imx8qp-apalis-v1.1-ixora-v1.2.dtb
------------------------------------------------------------

Device tree overlays
------------------------------------------------------------
Overlays enabled:         fdt_overlays=apalis-imx8_hdmi_overlay.dtbo apalis-imx8_spi1_spidev_overlay.dtbo apalis-imx8_spi2_spidev_overlay.dtbo
Overlays available:
                          apalis-imx8_ar0521_overlay.dtbo
                          apalis-imx8_hdmi_overlay.dtbo
                          apalis-imx8_hmp_overlay.dtbo
                          apalis-imx8_mezzanine_can_overlay.dtbo
                          apalis-imx8_mezzanine_ov5640-27mhz_overlay.dtbo
                          apalis-imx8_mezzanine_ov5640_overlay.dtbo
                          apalis-imx8_mezzanine_panel-cap-touch-10inch-lvds_overlay.dtbo
                          apalis-imx8_nau8822-btl_overlay.dtbo
                          apalis-imx8_ov5640-27mhz_overlay.dtbo
                          apalis-imx8_ov5640_overlay.dtbo
                          apalis-imx8_panel-cap-touch-10inch-lvds_overlay.dtbo
                          apalis-imx8_panel-lvds-dual-channel-1080p_overlay.dtbo
                          apalis-imx8_resistive-touch_overlay.dtbo
                          apalis-imx8_spi1_spidev_overlay.dtbo
                          apalis-imx8_spi2_spidev_overlay.dtbo
------------------------------------------------------------

Hey, @rvrse,

Since you tested it without the overlay (I suppose that means you tried via the U-Boot interface), then there may be some incompatibility in the SCFW configuration. Even if the custom SCFW comes from the NXP Community, I suggest trying to run the example in one of our standard, non-customized images to narrow down the problem for sure. Since you are using Torizon, try running the binary on the Cortex-M with one of our Torizon Releases and let me know if the RX works properly.

Best regards,