The i.MX93 SoC includes a high efficiency Cortex-M33 processor with security considerations. The Cortex-M33 implements the Armv8-M architecture with Main Extensions, including a single-precision floating-point unit (FPU), a nested vectored interrupt controller (NVIC), flash patch breakpoint (FPB), the data watchpoint and trace (DWT) unit, and instrumentation trace macrocell.

Communication between NPU and Cortex-A55 processor is done through the Cortex-M33 processor. As a consequence, you cannot run any custom firmware on the Cortex-M33 and make use of the NPU at the same time. To use the Cortex-M33 processor with a custom firmware, you first need to disable the NPU.

Features

The Cortex-M33 on the i.MX93 SoC includes the following features:

  • Arm Cortex-M33 CPU processor (up to 250 MHz)

    • Two-way set-associative 16 KB instruction cache

    • Two-way set-associative 16 KB data cache

    • 256 KB tightly coupled memory (TCM)

  • Supports FPU

  • Supports MPU

Download the SDK and toolchain

Before building the Cortex-M33 firmware, you must download and install the SDK, which includes example applications and the toolchain to build your firmware application.

Download the SDK

NXP offers an SDK that facilitates firmware development for the Cortex-M33 core. The SDK provides examples that demonstrate how to access the peripherals available on this subsystem.

You may need to register for an account on the NXP website.
  1. Go to https://mcuxpresso.nxp.com/en/builder.

  2. On the left sidebar, click Select Board.

  3. Select Boards > i.MX > EVK-MIMX93.

  4. Click Build MCUXpresso SDK.

  5. Select your Toolchain/IDE and your Host OS.

  6. Click Download SDK.

Download the toolchain

You must also download the toolchain to build your firmware application:

  1. Go to https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads.

  2. Select the latest available version for the Linux hosted "AArch32 bare-metal target" toolchain.

Install the SDK and toolchain

  1. Decompress the SDK in a directory of your choice:

    $ tar -xvf <SDK_VER>_EVK-MIMX93.tar.gz -C <sdk-install-path>
  2. Decompress the toolchain in a directory of your choice:

    $ tar -xvf arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi.tar.xz -C <toolchain_install_path>

Debug console UART

The Cortex-M33 uses UART2 as default debug console. By default the UART2 is in use on the wireless variant of the ConnectCore 93 by the Bluetooth UART. Therefore, for debug purposes, modify the application code to use a different console port. For instance, change the console port to UART4 (used for XBee, available on the J37 connector, pin2: RX, pin3: TX).

As reference, the changes needed to modify the debug port from UART2 to UART4 are:

<demo_apps>/hello_world/board.h
 /* The UART to use for debug messages. */
-#define BOARD_DEBUG_UART_INSTANCE   2U
+#define BOARD_DEBUG_UART_INSTANCE   4U
 #define BOARD_DEBUG_UART_BAUDRATE   115200U
 #define BOARD_DEBUG_UART_TYPE       kSerialPort_Uart
-#define BOARD_DEBUG_UART_CLOCK_ROOT kCLOCK_Root_Lpuart2
-#define BOARD_DEBUG_UART_CLOCK_GATE kCLOCK_Lpuart2
+#define BOARD_DEBUG_UART_CLOCK_ROOT kCLOCK_Root_Lpuart4
+#define BOARD_DEBUG_UART_CLOCK_GATE kCLOCK_Lpuart4
<demo_apps>/hello_world/pin_mux.c
-    IOMUXC_SetPinMux(IOMUXC_PAD_UART2_RXD__LPUART2_RX, 0U);
-    IOMUXC_SetPinMux(IOMUXC_PAD_UART2_TXD__LPUART2_TX, 0U);
+    IOMUXC_SetPinMux(IOMUXC_PAD_ENET2_RD0__LPUART4_RX, 0U);
+    IOMUXC_SetPinMux(IOMUXC_PAD_ENET2_TD0__LPUART4_TX, 0U);

-    IOMUXC_SetPinConfig(IOMUXC_PAD_UART2_RXD__LPUART2_RX,
+    IOMUXC_SetPinConfig(IOMUXC_PAD_ENET2_RD0__LPUART4_RX,
                         IOMUXC_PAD_PD_MASK);
-    IOMUXC_SetPinConfig(IOMUXC_PAD_UART2_TXD__LPUART2_TX,
+    IOMUXC_SetPinConfig(IOMUXC_PAD_ENET2_TD0__LPUART4_TX,
                         IOMUXC_PAD_DSE(15U));

Build the "Hello World" demo application

  1. Export the variable ARMGCC_DIR with the absolute path to the GCC ARM Embedded toolchain.

    $ export ARMGCC_DIR=<toolchain_install_path>/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi
  2. Build the hello_world application.

    $ cd <sdk-install-path>/boards/evkmimx93/demo_apps/hello_world/armgcc
    $ ./build_all.sh

    Find the artifacts in the debug/release directory.

For more information, see the MCUXpresso Software Development Kit

Run the firmware

You have two options to run the Cortex-M33 firmware:

  • run it from U-Boot, during the boot process

  • run it from Linux, once the operating system is running

Option 1: Run from U-Boot

Containerize the demo application

To run the Cortex-M33 firmware from U-Boot, you must embed it in an AHAB container.

  1. Source the Digi Embedded Yocto SDK environment.

    $ . /opt/dey/5.0-r4/ccimx93-dvk/environment-setup-armv8a-dey-linux
  2. Create the AHAB container from your firmware image.

    $ mkimage_imx8 -soc IMX9 -c -m33 hello_world.bin 0 0x1FFE0000 0x201E0000 -out hello_world_cntr.bin

Transfer and run the demo application

  1. Boot your device and stop in the U-Boot console.

  2. Add parameter clk-imx93.mcore_booted=1 to the Linux kernel command line to prevent the Cortex-M33 application from freezing after Linux starts:

    => setenv extra_bootargs clk-imx93.mcore_booted=1,${extra_bootargs}
    => saveenv
  3. Transfer the application to RAM, for example via TFTP:

    => tftp $loadaddr <m33_firmware.bin>
    TFTP from server 192.168.1.1; our IP address is 192.168.1.2
    Filename 'm33_firmware.bin'.
    Load address: 0x40480000
    Loading: #
              1.6 MiB/s
    done
    Bytes transferred = 6528 (1980 hex)

    where <m33_firmware.bin> is the binary filename.

  4. Use the bootaux_cntr command to run the application on the Cortex-M33:

    => bootaux_cntr $loadaddr 0
    Authenticate auxcore container at 0x80400000
    ## Starting auxiliary core addr = 0x1FFE0000...

Auto-run the firmware on every boot

The Cortex-M33 doesn’t have ROM memory. To instruct the Cortex-M33 to run an application every time the device boots, you must store the application on the eMMC and modify U-Boot’s default bootcmd to load the firmware to RAM and run it on the Cortex-M33 before booting the operating system:

  1. Transfer the Cortex-M33 firmware to the eMMC at the linux_a partition:

    => updatefile linux_a tftp <m33_firmware.bin>
  2. Create a U-Boot script to read and load the Cortex-M33 firmware, and prepend it to the default boot command:

    => setenv boot_cortexm "load mmc 0:1 ${loadaddr} <m33_firmware.bin>; bootaux_cntr ${loadaddr} 0"
    => setenv bootcmd "run boot_cortexm; ${bootcmd}"
    => saveenv

Option 2: Run from Linux remoteproc framework

Modern SoCs typically have remote processing devices on the same chip silicon. The remoteproc framework allows different platforms/architectures to control (power on, load firmware, power off) those remote processors.

The firmware file must be in ELF format to be used with the remoteproc framework.

Enable the remoteproc framework

remoteproc support is enabled as built-in on the ConnectCore 93 Development Kit kernel configuration file.

  • Linux remoteproc framework (CONFIG_REMOTEPROC)

  • IMX remoteproc driver (CONFIG_IMX_REMOTEPROC)

Control remote processors with remoteproc framework

You can use the remoteproc framework to load firmware onto an SoC and power it off and on. The location of the remoteproc device is available in the file system at:

# /sys/class/remoteproc/remoteprocX

Where X is the index of the device node, 0 by default.

To load the firmware:

# echo -n <firmware_name.elf> > /sys/class/remoteproc/remoteproc0/firmware

To start the firmware:

# echo start > /sys/class/remoteproc/remoteproc0/state

To stop the firmware:

# echo stop > /sys/class/remoteproc/remoteproc0/state

You can monitor the state of the remoteproc firmware with:

# cat /sys/class/remoteproc/remoteproc0/state

Auto-run the firmware on every boot

To run the firmware from Linux on every boot, create a systemd service that loads and runs the firmware using the remoteproc instructions described above.