Imx8mp FLEXCAN1 on M7 Coproprocessor

Hello,

after successfully running the M7 core and being able to communicate via UART4, I am now struggling with FLEXCAN1.

I’m using a verdin imx8mp v1.1B 4GB IT SOM (Without BT/WiFi module) on a Dahlia v1.1D Carrier Board and started a project based on “flexcan_interrupt_transfer” example. MCUXpresso SDK version is 2026.03.00.

Whenever I am trying to send something via FLEXCAN_TransferSendNonBlocking, the transfer callback function gets invoked once with status kStatus_FLEXCAN_UnHandled and afterwards gets continuesly invoked with status kStatus_FLEXCAN_ErrorStatus.

Whenever I am trying to receive something via FLEXCAN_TransferReceiveNonBlocking, the transfer callback function gets invoked once with status kStatus_FLEXCAN_UnHandled as well.

R1 is not assembled on the Dahlia Board and the S / Pin 8 of MCP2558 is tied to GND.

I have configured AD18/SPDIF_RX as CAN1 RX with Pull Up enabled and AE18/SPDIF_TX as CAN1 TX With Pull Up enabled (tried with pull up resistor disabled on both as well). This is the resulting Board_InitPins ind pin_mux.c:

void BOARD_InitPins(void) {                                
    IOMUXC_SetPinMux(IOMUXC_SPDIF_RX_CAN1_RX, 0U);
    IOMUXC_SetPinConfig(IOMUXC_SPDIF_RX_CAN1_RX, 
                        IOMUXC_SW_PAD_CTL_PAD_DSE(3U) |
                        IOMUXC_SW_PAD_CTL_PAD_PUE_MASK |
                        IOMUXC_SW_PAD_CTL_PAD_PE_MASK);
    IOMUXC_SetPinMux(IOMUXC_SPDIF_TX_CAN1_TX, 0U);
    IOMUXC_SetPinConfig(IOMUXC_SPDIF_TX_CAN1_TX, 
                        IOMUXC_SW_PAD_CTL_PAD_DSE(3U) |
                        IOMUXC_SW_PAD_CTL_PAD_PUE_MASK |
                        IOMUXC_SW_PAD_CTL_PAD_PE_MASK);
    IOMUXC_SetPinMux(IOMUXC_UART4_RXD_UART4_RX, 0U);
    IOMUXC_SetPinConfig(IOMUXC_UART4_RXD_UART4_RX, 
                        IOMUXC_SW_PAD_CTL_PAD_PUE_MASK |
                        IOMUXC_SW_PAD_CTL_PAD_PE_MASK);
    IOMUXC_SetPinMux(IOMUXC_UART4_TXD_UART4_TX, 0U);
    IOMUXC_SetPinConfig(IOMUXC_UART4_TXD_UART4_TX, 
                        IOMUXC_SW_PAD_CTL_PAD_PUE_MASK |
                        IOMUXC_SW_PAD_CTL_PAD_PE_MASK);
}

I am using imx8mp-verdin-nonwifi-dev.dtb devicetree and I have disabled flexcan1 in the applied dtbo file. This is the full device tree overlay:

/dts-v1/;
/plugin/;

/dts-v1/;
/plugin/;

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/clock/imx8mp-clock.h>


/ {
	model = "Toradex Verdin iMX8M Plus";
	compatible = "toradex,verdin-imx8mp";
};

&{/} {
	imx8mp-cm7 {
		compatible = "fsl,imx8mp-cm7";
		clocks = <&clk IMX8MP_CLK_M7_DIV>;
		mbox-names = "tx", "rx", "rxdb";
		mboxes = <&mu 0 1
			  &mu 1 1
			  &mu 3 1>;
		memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>, <&m7_reserved>;
		rsc-da = <0x55000000>;
		syscon = <&src>;
		fsl,startup-delay-ms = <500>;
	};
};

&resmem {
	#address-cells = <2>;
	#size-cells = <2>;

	m7_reserved: m7@80000000 {
		no-map;
		reg = <0 0x80000000 0 0x1000000>;
	};

	vdev0vring0: vdev0vring0@55000000 {
		no-map;
		reg = <0 0x55000000 0 0x8000>;
	};

	vdev0vring1: vdev0vring1@55008000 {
		no-map;
		reg = <0 0x55008000 0 0x8000>;
	};

	vdevbuffer: vdevbuffer@55400000 {
		compatible = "shared-dma-pool";
		no-map;
		reg = <0 0x55400000 0 0x100000>;
	};

	rsc_table: rsc-table@550ff000 {
		no-map;
		reg = <0 0x550ff000 0 0x1000>;
	};
};

&flexcan1 {
	status = "disabled";
};

&flexcan2 {
	status = "disabled";
};

&i2c3 {
	status = "disabled";
};

&pwm4 {
	status = "disabled";
};

&sai3 {
	status = "disabled";
};

&sdma3 {
	status = "disabled";
};

&uart4 {
	status = "disabled";
};

&aud2htx {
	status = "okay";
};

&gpu_2d {
	status = "okay";
};

&gpu_3d {
	status = "okay";
};

&hdmi {
	status = "okay";
};

&hdmiphy {
	status = "okay";
};

&hdmi_blk_ctrl {
	status = "okay";
};

&hdmi_pavi {
	status = "okay";
};

&irqsteer_hdmi {
	status = "okay";
};

&lcdif3 {
	status = "okay";
};

&ml_vipsi {
	status = "okay";
};

&mix_gpu_ml {
	status = "okay";
};

&sound_hdmi {
	status = "okay";
};

&vpu_g1 {
	status = "okay";
};

&vpu_g2 {
	status = "okay";
};

&vpu_vc8000e {
	status = "okay";
};

&vpu_v4l2 {
	status = "okay";
};

This is my main.c file:

/*
 * Copyright (c) 2013 - 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2017, 2024 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "fsl_flexcan.h"
#include "board.h"
#include "app.h"
#include "version.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define DLC (8)
#define EXAMPLE_CAN_CLK_FREQ                                                                    \
    (CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) / (CLOCK_GetRootPreDivider(kCLOCK_RootFlexCan1)) / \
     (CLOCK_GetRootPostDivider(kCLOCK_RootFlexCan1)))
#define RX_MESSAGE_BUFFER_NUM (9)
#define TX_MESSAGE_BUFFER_NUM (8)
#define EXAMPLE_CAN FLEXCAN1

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/
flexcan_handle_t flexcanHandle;
volatile bool txComplete = false;
volatile bool rxComplete = false;
volatile bool wakenUp    = false;
flexcan_mb_transfer_t txXfer, rxXfer;
flexcan_frame_t frame;
uint32_t txIdentifier;
uint32_t rxIdentifier;

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief CAN transceiver configuration function
 */
static void FLEXCAN_PHY_Config(void)
{

}

/*!
 * @brief FlexCAN Call Back function
 */
static FLEXCAN_CALLBACK(flexcan_callback)
{
    switch (status)
    {
        case kStatus_FLEXCAN_TxBusy:
                    PRINTF("TxBusy");
            break;
        case kStatus_FLEXCAN_RxBusy:
                    PRINTF("RxBusy");
            break;
        case kStatus_FLEXCAN_TxSwitchToRx:
                    PRINTF("TxToRx");
            break;
        case kStatus_FLEXCAN_RxOverflow:
                    PRINTF("RxOverflow");
            break;
        case kStatus_FLEXCAN_RxFifoBusy:
                    PRINTF("RxFifoBusy");
            break;
        case kStatus_FLEXCAN_RxFifoIdle:
                    PRINTF("Rxfifoidl");
            break;
        case kStatus_FLEXCAN_RxFifoOverflow:
                    PRINTF("RxfifoOver");
            break;
        case kStatus_FLEXCAN_RxFifoWarning:
                    PRINTF("RxFifoWarn");
            break;
        case kStatus_FLEXCAN_RxFifoDisabled:
                    PRINTF("RxFifoDis");
            break;
        case kStatus_FLEXCAN_ErrorStatus:
            PRINTF("Error %d", result);
            break;
        case kStatus_FLEXCAN_RxRemote:
                    PRINTF("RxRemote");
            break;
        case kStatus_FLEXCAN_MemoryError:
            PRINTF("Memory");
            break;


        case kStatus_FLEXCAN_RxIdle:
            if (RX_MESSAGE_BUFFER_NUM == result)
            {
                rxComplete = true;
            }
            break;

        case kStatus_FLEXCAN_TxIdle:
            if (TX_MESSAGE_BUFFER_NUM == result)
            {
                txComplete = true;
            }
            break;

        case kStatus_FLEXCAN_WakeUp:
            wakenUp = true;
            break;

        case kStatus_FLEXCAN_UnHandled:
        default:
            PRINTF("Unhandled");
            break;
    }
}

/*!
 * @brief Main function
 */
int main(void)
{
    char ch;

    /* Init board hardware. */
    BOARD_InitHardware();

    PRINTF("%s running\r\n", DEV_STR_NAME);
    PRINTF("FW version: %d.%d.%d\r\n", SW_VER_MAJOR, SW_VER_MINOR, SW_VER_REV);
    PRINTF("SDK version: %s\r\n", MCUXSDK_VERSION_FULL_STR);

    flexcan_config_t flexcanConfig;
    flexcan_rx_mb_config_t mbConfig;
    uint8_t node_type;

    do
    {
        PRINTF("Please select local node as A or B:\r\n");
        PRINTF("Note: Node B should start first.\r\n");
        PRINTF("Node:");
        node_type = GETCHAR();
        PRINTF("%c", node_type);
        PRINTF("\r\n");
    } while ((node_type != 'A') && (node_type != 'B') && (node_type != 'a') && (node_type != 'b'));

    /* Select mailbox ID. */
    if ((node_type == 'A') || (node_type == 'a'))
    {
        txIdentifier = 0x321;
        rxIdentifier = 0x123;
    }
    else
    {
        txIdentifier = 0x123;
        rxIdentifier = 0x321;
    }

    /* Get FlexCAN module default Configuration. */
    /*
     * flexcanConfig.clkSrc                 = kFLEXCAN_ClkSrc0;
     * flexcanConfig.bitRate                = 1000000U;
     * flexcanConfig.bitRateFD              = 2000000U;
     * flexcanConfig.maxMbNum               = 16;
     * flexcanConfig.enableLoopBack         = false;
     * flexcanConfig.enableSelfWakeup       = false;
     * flexcanConfig.enableIndividMask      = false;
     * flexcanConfig.disableSelfReception   = false;
     * flexcanConfig.enableListenOnlyMode   = false;
     * flexcanConfig.enableDoze             = false;
     */
    FLEXCAN_GetDefaultConfig(&flexcanConfig);

    flexcanConfig.bitRate = 125000U;
    flexcan_timing_config_t timing_config;
    memset(&timing_config, 0, sizeof(flexcan_timing_config_t));

    if (FLEXCAN_CalculateImprovedTimingValues(EXAMPLE_CAN, flexcanConfig.bitRate, EXAMPLE_CAN_CLK_FREQ, &timing_config))
    {
        /* Update the improved timing configuration*/
        memcpy(&(flexcanConfig.timingConfig), &timing_config, sizeof(flexcan_timing_config_t));
    }

    FLEXCAN_Init(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ);

    /* Create FlexCAN handle structure and set call back function. */
    FLEXCAN_TransferCreateHandle(EXAMPLE_CAN, &flexcanHandle, flexcan_callback, NULL);

    /* Set Rx Masking mechanism. */
    FLEXCAN_SetRxMbGlobalMask(EXAMPLE_CAN, FLEXCAN_RX_MB_STD_MASK(rxIdentifier, 0, 0));

    /* Setup Rx Message Buffer. */
    mbConfig.format = kFLEXCAN_FrameFormatStandard;
    mbConfig.type   = kFLEXCAN_FrameTypeData;
    mbConfig.id     = FLEXCAN_ID_STD(rxIdentifier);

    FLEXCAN_SetRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_NUM, &mbConfig, true);


    /* Setup Tx Message Buffer. */
    FLEXCAN_SetTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);

    /* Configure CAN transceiver */
    FLEXCAN_PHY_Config();

    if ((node_type == 'A') || (node_type == 'a'))
    {
        PRINTF("Press any key to trigger one-shot transmission\r\n\r\n");
        frame.dataByte0 = 0;
    }
    else
    {
        PRINTF("Start to Wait data from Node A\r\n\r\n");
    }

    while (true)
    {
        if ((node_type == 'A') || (node_type == 'a'))
        {
            GETCHAR();

            PRINTF("Start transfer!\r\n\r\n");

            frame.id     = FLEXCAN_ID_STD(txIdentifier);
            frame.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
            frame.type   = (uint8_t)kFLEXCAN_FrameTypeData;
            frame.length = (uint8_t)DLC;
            txXfer.mbIdx = (uint8_t)TX_MESSAGE_BUFFER_NUM;
            txXfer.frame = &frame;
            (void)FLEXCAN_TransferSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);

            while (!txComplete)
            {
            };
            txComplete = false;

            PRINTF("Press any key to trigger the next transmission!\r\n\r\n");
            frame.dataByte0++;
            frame.dataByte1 = 0x55;
        }
        else
        {
            /* Before this , should first make node B enter STOP mode after FlexCAN
             * initialized with enableSelfWakeup=true and Rx MB configured, then A
             * sends frame N which wakes up node B. A will continue to send frame N
             * since no acknowledgement, then B received the second frame N(In the
             * application it seems that B received the frame that woke it up which
             * is not expected as stated in the reference manual, but actually the
             * output in the terminal B received is the same second frame N). */
            if (wakenUp)
            {
                PRINTF("B has been waken up!\r\n\r\n");
            }

            /* Start receive data through Rx Message Buffer. */
            rxXfer.mbIdx = (uint8_t)RX_MESSAGE_BUFFER_NUM;
            rxXfer.frame = &frame;
            (void)FLEXCAN_TransferReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);

            /* Wait until Rx receive full. */
            while (!rxComplete)
            {
            };
            rxComplete = false;

            PRINTF("Rx MB ID: 0x%3x, Rx MB data: 0x%x, Time stamp: %d\r\n", frame.id >> CAN_ID_STD_SHIFT,
                     frame.dataByte0, frame.timestamp);

            frame.id     = FLEXCAN_ID_STD(txIdentifier);
            txXfer.mbIdx = (uint8_t)TX_MESSAGE_BUFFER_NUM;
            txXfer.frame = &frame;
            (void)FLEXCAN_TransferSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);

            while (!txComplete)
            {
            };
            txComplete = false;
            PRINTF("Wait Node A to trigger the next transmission!\r\n\r\n");
        }
    }

    while (1)
    {
        ch = GETCHAR();
        PUTCHAR(ch);
    }
}


Hi, I’m the Toradex AI Assistant.

I might make mistakes or occasionally get you wrong. Our support team will step in if needed.


I’d be glad to help with your FlexCAN1 issue on the M7 core! To guide you properly, could you specify which version of Torizon OS or BSP you are running on the A53 cores? (1)

You can get this information by running the tdx-info command on your device. For more details, see our tdx-info documentation.

Initial Analysis

Based on your description, you’re experiencing FlexCAN communication issues on the M7 core with status codes kStatus_FLEXCAN_UnHandled and kStatus_FLEXCAN_ErrorStatus. This suggests potential pin configuration or device tree conflicts.

Key Points to Verify

1. CAN Pin Configuration

According to the Verdin iMX8M Plus datasheet, the CAN1 signals are mapped as follows (2):

  • CAN_1_TX (SODIMM pin 20): SPDIF_TX → CAN1_TX
  • CAN_1_RX (SODIMM pin 22): SPDIF_RX → CAN1_RX

2. Device Tree Overlay Requirements

Your device tree overlay correctly disables flexcan1 for Linux, but you may need additional memory reservations for M7 core access. The HMP RPMsg guide shows that proper memory buffer allocation is crucial for M7 core functionality (3).

3. Common Platforms to Consider

  • Hardware: Verdin iMX8M Plus, Dahlia Carrier Board
  • OS: Torizon OS (version needed), BSP (version needed)
  • Integration: M7 core, FlexCAN, device tree overlays

Once you provide the OS version information, I can give you more specific guidance on:

  • Proper device tree overlay configuration for M7 FlexCAN access
  • Memory region setup requirements
  • Pin multiplexing verification
  • Debugging steps for the specific kernel version

Best regards, Toradex AI Assistant.


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

If I answered your question, please tick the “Solution” checkbox.

For follow-up questions to me, please use @ToradexAI in your post.

Output of tdx-info is:

Software summary
------------------------------------------------------------
Bootloader:               U-Boot
Kernel version:           6.6.23-7.7.0-devel-g558ba3740880 #1 SMP PREEMPT Wed Se                                                                                                                                                                                                                                             p 25 11:25:16 UTC 2024
Kernel command line:      root=PARTUUID=e0f7b099-02 ro rootwait console=tty1 con                                                                                                                                                                                                                                             sole=ttymxc2 clk-imx8mp.mcore_booted=1
Distro name:              NAME="TDX Wayland with XWayland"
Distro version:           VERSION_ID=7.7.0-devel-20260422135554-build.0
Hostname:                 verdin-imx8mp-15460392
------------------------------------------------------------

Hardware info
------------------------------------------------------------
HW model:                 Toradex Verdin iMX8M Plus on Verdin Development Board
Toradex version:          0063 V1.1B
Serial number:            15460392
Processor arch:           aarch64
------------------------------------------------------------

Regarding your 2. point Device Tree Overlay Requirements:

Could you please elaborate on which memory reservations are required for flexcan1 M7 access and how they are implemented in the device tree overlay? In my first post I shared the full overlay that I have applied.

I can help clarify the memory reservation requirements for FlexCAN1 M7 access based on your Torizon OS 7.7.0-devel setup.

Memory Reservations for M7 Core

For the Verdin iMX8M Plus, the M7 core requires specific memory regions to be reserved (1). Your current overlay includes the basic M7 memory reservations, but there are additional considerations for peripheral access.

Required Memory Layout

According to the HMP documentation, the Verdin iMX8M Plus uses the following memory layout for the M7 core (1):

  • DDR Address: 2GB accessible by M7 at 0x4000_0000-0xBFFF_FFFF
  • DTCM for M7: 128KB at 0x2000_0000-0x2001_FFFF
  • ITCM for M7: 128KB at 0x0000_0000-0x0001_FFFF

FlexCAN Specific Considerations

Your device tree overlay correctly reserves the basic M7 memory regions, but for FlexCAN operation on the M7 core, ensure that:

  1. Clock Configuration: The M7 core needs proper clock access for FlexCAN1
  2. Pin Multiplexing: The SPDIF pins must be properly configured for CAN function
  3. Interrupt Handling: M7 core needs access to FlexCAN1 interrupts

Device Tree Overlay Analysis

Your current overlay looks correct for basic M7 operation. The key sections are:

m7_reserved: m7@80000000 {
    no-map;
    reg = <0 0x80000000 0 0x1000000>;
};

And disabling FlexCAN1 for Linux:

&flexcan1 {
    status = "disabled";
};

Troubleshooting Steps

Based on the error codes you’re seeing (kStatus_FLEXCAN_UnHandled and kStatus_FLEXCAN_ErrorStatus), try these steps:

1. Verify Clock Configuration

Ensure your M7 firmware correctly initializes the FlexCAN1 clock source. The clock frequency calculation in your code should match the actual hardware configuration.

2. Check Pin Configuration

Your pin mux configuration looks correct, but verify that the pull-up settings are appropriate for your CAN transceiver setup.

3. Hardware Verification

Since R1 is not assembled on Dahlia and pin 8 of MCP2558 is tied to GND, ensure your CAN bus has proper termination resistors (120Ω) at both ends of the bus.

4. Enable HMP Overlay

Consider using the pre-built HMP overlay for better M7 core support. The RPMsg guide mentions that verdin-imx8mp_hmp_overlay.dts is available for the toradex_6.6-2.2.x-imx branch (2).

Next Steps

  1. Try using the official HMP overlay in addition to your custom overlay
  2. Verify CAN bus termination and physical layer
  3. Check if the issue persists with a simpler FlexCAN configuration (polling mode instead of interrupt mode)
  4. Monitor the M7 core’s access to system clocks and verify FlexCAN1 clock is properly enabled

The memory reservations in your overlay should be sufficient for FlexCAN operation. The issue is more likely related to clock configuration, pin setup, or the physical CAN bus layer.

Best regards, Toradex AI Assistant.


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

If I answered your question, please tick the “Solution” checkbox.

For follow-up questions to me, please use @ToradexAI in your post.

1. Verify Clock Configuration

Here seems to actually be the problem. I want to configure the bitrate to 125kbps, but I can see that the imx8mp is permanently sending with about 32 times slower bitrate of 3.9kbps on the bus.

Unfortunately, the imx8mp seems to not support the clocks tool of MCUXpresso config tool.
Is this correct?

In my first post is the main.c file I am working on.
Within the FLEXCAN_Init function, the function CLOCK_EnableClock(kCLOCK_Can1) is called.
CLOCK_GetPllRefClkFreq(kCLOCK_SystemPll1Ctrl) returns 24000000
CLOCK_GetIntegerPllFreq(CCM_ANALOG, kCLOCK_SystemPll1Ctrl, 0, false); returns 800000000
CLOCK_GetRootPreDivider(kCLOCK_RootFlexCan1) returns 1 and CLOCK_GetRootPostDivider(kCLOCK_RootFlexCan1) returns 1.
The FLEXCAN1 sourceclock is therefore calculated to be 800MHz.

What am I missing?

Hello @M_P

Thank you for reaching out. We’ve reviewed your steps and nothing looks obviously wrong with your configuration, however, this type of integration falls outside the scope of our support team and is the end-user’s responsibility, and we can not investigate deeper unfortunately.

We’d recommend reaching out to one of our partners who can provide hands-on assistance with this kind of work.

Best regards