Hello everyone!
In this document you'll find an example on how to build your Cortex-M33 code where some parts of the code runs from DDR, for this changes on ATF where the M33 core can visit DRAM in early stage.
For this we will take advantage of RPROC framework, RPROC (Remote Processor Framework) is a Linux kernel and U-Boot subsystem that manages secondary, embedded processors (like Cortex-M cores), where we will use the A55 to load the M33 firmware.
This will require changes on Linux device tree, ATF and M33 linker file.
Requirements: Ubuntu 20.04 or later host PC i.MX93 QSB UUU Tool ARM GNU Toolchain (arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu) SDK package (SDK_25_09_00_MCIMX93-QSB) Prebuild Linux Image (LF_v6.12.34-2.1.0_images_IMX93EVK)
### Clone imx-mkimage, it is better to download the same version of the sw we are working with ### $ git clone https://github.com/nxp-imx/imx-mkimage -b lf-6.12.34-2.1.0 ### Decompress the GNU toolchain into a path in local disk, in this test would be /opt/ ### $ sudo tar -xvJf arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt
### Clone and build Uboot ### $ git clone https://github.com/nxp-imx/uboot-imx -b lf-6.12.34-2.1.0 $ cd uboot-imx $ make -j $(nproc --all) clean $ make -j$(nproc --all) ARCH=arm CROSS_COMPILE=/opt/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- imx93_11x11_evk_defconfig $ make -j $(nproc --all) ARCH=arm CROSS_COMPILE=/opt/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- ### Download and extract ELE firmware ### $ cd .. $ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-ele-imx-2.0.3-286c884.bin $ chmod +x firmware-ele-imx-2.0.3-286c884.bin $ ./firmware-ele-imx-2.0.3-286c884.bin --auto-accept ### Optional if using i.MX FW ### $ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-8.29-8741a3b.bin $ chmod +x firmware-imx-8.29-8741a3b.bin $ ./firmware-imx-8.29-8741a3b.bin --auto-accept ### Clone ATF ### $ git clone https://github.com/nxp-imx/imx-atf -b lf-6.12.34-2.1.0 $ cd imx-atf ### Modify ATF for the M33 to be able to access DDR ###
Spoiler (Highlight to read)
--- a/plat/imx/imx93/trdc_config.h +++ b/plat/imx/imx93/trdc_config.h
struct trdc_mrc_config trdc_n_mrc[] = { { 0, 0, 0, 0x80000000, 0x80000000, 0, false }, /* MRC0 DRAM for S400 DID0 */ { 0, 1, 0, 0x80000000, 0x80000000, 0, false }, /* MRC0 DRAM for MTR DID1 */ - { 0, 2, 0, 0x80000000, 0x80000000, 0, true }, /* MRC0 DRAM for M33 DID2 */ + { 0, 2, 0, 0x80000000, 0x80000000, 1, true }, /* MRC0 DRAM for M33 DID2 */ { 0, 3, 0, 0x80000000, 0x80000000, 1, false }, /* MRC0 DRAM for A55 DID3 */ { 0, 5, 0, 0x80000000, 0x80000000, 0, false }, /* MRC0 DRAM for USDHC1 DID5 */ { 0, 6, 0, 0x80000000, 0x80000000, 0, false }, /* MRC0 DRAM for USDHC2 DID6 */
--- a/plat/imx/imx93/trdc_config.h+++ b/plat/imx/imx93/trdc_config.h
struct trdc_mrc_config trdc_n_mrc[] = {{ 0, 0, 0, 0x80000000, 0x80000000, 0, false }, /* MRC0 DRAM for S400 DID0 */{ 0, 1, 0, 0x80000000, 0x80000000, 0, false }, /* MRC0 DRAM for MTR DID1 */- { 0, 2, 0, 0x80000000, 0x80000000, 0, true }, /* MRC0 DRAM for M33 DID2 */+ { 0, 2, 0, 0x80000000, 0x80000000, 1, true }, /* MRC0 DRAM for M33 DID2 */{ 0, 3, 0, 0x80000000, 0x80000000, 1, false }, /* MRC0 DRAM for A55 DID3 */{ 0, 5, 0, 0x80000000, 0x80000000, 0, false }, /* MRC0 DRAM for USDHC1 DID5 */{ 0, 6, 0, 0x80000000, 0x80000000, 0, false }, /* MRC0 DRAM for USDHC2 DID6 */
### Build modified ATF ### $ make -j $(nproc --all) PLAT=imx93 bl31 CROSS_COMPILE=/opt/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
### Modify linker file and build M33 code, in this example we are using hello world SDK example ### $ cd .. $ tar -xvzf SDK_25_09_00_MCIMX93-QSB.tar.gz $ cd SDK_25_09_00_MCIMX93-QSB/boards/mcimx93qsb/demo_apps/hello_world/armgcc
Spoiler (Highlight to read)
--- a/boards/mcimx93autoevk/demo_apps/hello_world/armgcc/MIMX9352_cm33_ram.ld +++ b/boards/mcimx93autoevk/demo_apps/hello_world/armgcc/MIMX9352_cm33_ram.ld
m_a55_suspend_ram (RW) : ORIGIN = 0x20002000, LENGTH = 0x00001000 m_data (RW) : ORIGIN = 0x20003000, LENGTH = 0x0001B000 m_rsc_tbl (RW) : ORIGIN = 0x2001E000, LENGTH = 0x00001000 + m_text_dram (RW) : ORIGIN = 0x8F000000, LENGTH = 0x00001000 + m_data_dram (RW) : ORIGIN = 0x8F001000, LENGTH = 0x00001000 } /* Define output sections */
. = ALIGN(4); } > m_text + .dram_text : + { + . = ALIGN(32); + *(.myDRAM) + . = ALIGN(32); + } > m_text_dram + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*)
.ARM.attributes 0 : { *(.ARM.attributes) } ASSERT(__StackLimit >= __HeapLimit, "region m_data overflowed with stack and heap") + + .dram_data : + { + . = ALIGN(32); + *(.myDRAM_data) + . = ALIGN(32); + } > m_data_dram } a/boards/mcimx93autoevk/demo_apps/hello_world/hello_world.c +++ b/boards/mcimx93autoevk/demo_apps/hello_world/hello_world.c
* Definitions ******************************************************************************/ +#define _RET_IP_ (unsigned long)__builtin_return_address(0) +#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) /******************************************************************************* * Prototypes
/******************************************************************************* * Variables ******************************************************************************/ - +const char myString[] __attribute__((section(".myDRAM_data"))) = "Hello, World!"; /******************************************************************************* * Code ******************************************************************************/ +__attribute__ ((section(".myDRAM"))) +void Dram_test(void) { + PRINTF("Dram_test!!\r\n"); + + PRINTF("%s!!\r\n", myString); + + PRINTF("function %p\n",_THIS_IP_); +} + /*! * @brief Main function */
BOARD_BootClockRUN(); BOARD_InitDebugConsole(); - PRINTF("hello world.\r\n"); + PRINTF("hello world from DRAM.\r\n"); while (1) { ch = GETCHAR(); PUTCHAR(ch); + if(ch == 'a'){ + Dram_test(); + } } }
--- a/boards/mcimx93autoevk/demo_apps/hello_world/armgcc/MIMX9352_cm33_ram.ld+++ b/boards/mcimx93autoevk/demo_apps/hello_world/armgcc/MIMX9352_cm33_ram.ld
m_a55_suspend_ram (RW) : ORIGIN = 0x20002000, LENGTH = 0x00001000m_data (RW) : ORIGIN = 0x20003000, LENGTH = 0x0001B000m_rsc_tbl (RW) : ORIGIN = 0x2001E000, LENGTH = 0x00001000+ m_text_dram (RW) : ORIGIN = 0x8F000000, LENGTH = 0x00001000+ m_data_dram (RW) : ORIGIN = 0x8F001000, LENGTH = 0x00001000}/* Define output sections */
. = ALIGN(4);} > m_text+ .dram_text :+ {+ . = ALIGN(32);+ *(.myDRAM)+ . = ALIGN(32);+ } > m_text_dram+.ARM.extab :{*(.ARM.extab* .gnu.linkonce.armextab.*)
.ARM.attributes 0 : { *(.ARM.attributes) }ASSERT(__StackLimit >= __HeapLimit, "region m_data overflowed with stack and heap")++ .dram_data :+ {+ . = ALIGN(32);+ *(.myDRAM_data)+ . = ALIGN(32);+ } > m_data_dram}a/boards/mcimx93autoevk/demo_apps/hello_world/hello_world.c+++ b/boards/mcimx93autoevk/demo_apps/hello_world/hello_world.c
* Definitions******************************************************************************/+#define _RET_IP_ (unsigned long)__builtin_return_address(0)+#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; })/******************************************************************************** Prototypes
/******************************************************************************** Variables******************************************************************************/-+const char myString[] __attribute__((section(".myDRAM_data"))) = "Hello, World!";/******************************************************************************** Code******************************************************************************/+__attribute__ ((section(".myDRAM")))+void Dram_test(void) {+ PRINTF("Dram_test!!\r\n");++ PRINTF("%s!!\r\n", myString);++ PRINTF("function %p\n",_THIS_IP_);+}+/*!* @brief Main function*/
BOARD_BootClockRUN();BOARD_InitDebugConsole();- PRINTF("hello world.\r\n");+ PRINTF("hello world from DRAM.\r\n");while (1){ch = GETCHAR();PUTCHAR(ch);+ if(ch == 'a'){+ Dram_test();+ }}}
$ export ARMGCC_DIR=~/gcc-arm-none-eabi-10.3-2021.10 $ export PATH=$PATH:~/gcc-arm-none-eabi-10.3-2021.10 $ ./build_release.sh
### Copy the resulting binaries to imx-mkimage ### $ cp ~/imx-atf/build/imx93/release/bl31.bin ~/imx-mkimage/iMX93 $ cp ~/uboot-imx/u-boot.bin ~/imx-mkimage/iMX93 $ cp ~/uboot-imx/spl/u-boot-spl.bin ~/imx-mkimage/iMX93
### Copy i.MX firmware ### $ cd .. $ cp firmware-imx-8.29-8741a3b/firmware/ddr/synopsys/lpddr4_dmem_* ~/imx-mkimage/iMX93 $ cp firmware-imx-8.29-8741a3b/firmware/ddr/synopsys/lpddr4_imem_* ~/imx-mkimage/iMX93 $ cpfirmware-ele-imx-2.0.3-286c884/mx93a1-ahab-container.img ~/imx-mkimage/iMX93
### Build the flash.bin using mkimage $ cd imx-mkimage $ make SOC=iMX93 flash_singleboot
### Clone, modify and build device tree for Linux to be able to use RPROC to load M33 Firmware ### $ git clone https://github.com/nxp-imx/linux-imx -b lf-6.12.34-2.1.0
Spoiler (Highlight to read)
--- a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts
no-map; }; + dram: dram@20480000 { + reg = <0 0x8f000000 0 0x20000>; + no-map; + }; + rsc_table: rsc-table@2021e000 { reg = <0 0x2021e000 0 0x1000>; no-map;
<&mu1 1 1>, <&mu1 3 1>; - memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, + memory-region = <&dram>, <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&vdev1vring0>, <&vdev1vring1>, <&rsc_table>; fsl,startup-delay-ms = <500>; status = "okay";
--- a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts+++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts
no-map;};+ dram: dram@20480000 {+ reg = <0 0x8f000000 0 0x20000>;+ no-map;+ };+rsc_table: rsc-table@2021e000 {reg = <0 0x2021e000 0 0x1000>;no-map;
<&mu1 1 1>,<&mu1 3 1>;- memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>,+ memory-region = <&dram>, <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>,<&vdev1vring0>, <&vdev1vring1>, <&rsc_table>;fsl,startup-delay-ms = <500>;status = "okay";
$ export ARCH=arm64 $ export CROSS_COMPILE=/opt/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- $ make imx_v8_defconfig $ make freescale/imx93-9x9-qsb.dtb
Set SW for serial download on the QSB (0001), connect debug, download and power cables and turn on the QSB, for this test we will flash the demo image and just replace Linux device tree, flash.bin and M33 firmware into the QSB board.
$ uuu -b sd_all flash.bin imx-image-full-imx93evk.wic
Once it is done change SW to the respective bootmedia SD boot (0011) and boot the board and stop at uboot, to enter fastboot mode to load modified device tree and M33 firmware
> fastboot 1
After this just run UUU tool on the host computer and fatload the files
$ uuu -b fat_write imx93-9x9-qsb.dtb mmc 1:1 $ uuu -b fat_write hello_world.elf mmc 1:1
When the tool finish, stop fastboot mode by typing CTRL+C and then boot into Linux > boot
Login with default password "root" and run the following commands to load M33 firmware $ root $ modprobe imx_rpmsg_tty $ cp /run/media/boot-mmcblk1p1/hello_world.elf /lib/firmware/ $ echo hello_world.elf > /sys/class/remoteproc/remoteproc0/firmware $ echo start >/sys/class/remoteproc/remoteproc0/state
Once done we can verify that the M33 firmware is running on DDR
Hope everyone finds this useful!
For any question regarding this document, please create a community thread and tag me if needed.
Saludos/Regards, Aldo.