Fastest Way to Communicate between Container and MCU

Hi

What is the best and fastest way to communicate between a Container and the MCU on the " Verdin iMX8M Plus"?

When i plan to run 2 large fifos on a shared ram section, one for Container->MCU with a pull/push pointer and one for MCU->Container with a push/pull pointer, how can i configure both the container and the mcu to have access to the same region in RAM?

The idea is to run most peripheral stuff (SPI; ADC; I2C,…) on the MCU and the mcu-firmware would essentially have no application logic, just a “cli-style” API via shared-ram-fifo so the Container can command the MCU for tasks to execute.

Hi @chalu ,

Sorry for the late reaction.

We’re discussing internally what would be the best way to perform what you are requesting.

We’ll reach out to you as soon as possible.

Best Regards
Kevin

1 Like

hi @kevin.tx

no worries, my project is currently on hold, so no hurry. just doing some prep testing.

also, please let me know if my idea of fifo+shared ram is nonsense :slight_smile: better alternatives welcome.

EDIT:

For the IMX7 there exists a documentation for “RPMsg” and a repository

Open Questions:

  • Is RPMsg the best option?
  • Is such a github repo also planed for the IMX8MP with “out of the box” working examples or do i have to port it myself?

Hi @chalu !

Usually, the “best” option is highly dependent on your use case. RPMsg is indeed a common strategy. One of the advantages is that you will not need to invest a lot of time developing your own RPMsg replacement (not to mention the code maintenance).

If you need the best strategy for your application, you will need to deep dive into it and understand how each possible implementation will impact your application.

Maybe you don’t need the best or the fastest way, but you need something that fits your specific needs.

To help you to figure out what is suitable, we can recommend you some of Toradex partners that are experts in this kind of implementation.

I know that the answer for this is probably “not planned”. As you can see, only Colibri iMX7 has it. Newer modules don’t have it.

Also, to better answer your questions, I am going to summon @hfranco.tx. He can maybe also give you some hints :slight_smile:

Best regards,

1 Like

Hi @chalu,

@henrique.tx Thanks for the answers, I agree with your points here.

@chalu the whole question depends, at the end, on what you need to do and what are your timings requirements.

The RPMSG framework implements an easier way to communicate between the processors, it’s thread-safe, you can write and read, it’s ready to use and etc.

However, the whole framework that will be slower if you just need to share some memory area between the two processors. For example, if you need to write data with the MCU and just read with Linux, a simple code for the MCU could be:

#define SHARED_MEM_ADDRESS 0x8FF00000

void SharedMemTask(void *pvParameters)
{
    uint8_t receiveBuff;
    uint32_t value = 0;
    uint32_t *ddr_shared_mem = (uint32_t*)SHARED_MEM_ADDRESS;

    // Print the initial banner
    PRINTF("\r\nShared Memory Demo!\n\n\r");
    PRINTF("Shared address: 0x%X\n\r",SHARED_MEM_ADDRESS);
    PRINTF("Type r to show the contend of shared memory address (0x%X)\n\r",SHARED_MEM_ADDRESS);
    PRINTF("Type w to write new value in the shared memory address (0x%X)\n\r",SHARED_MEM_ADDRESS);
    PRINTF("\n\rYou can also write and read the shared memory from linux with devmem2 utility:\n\r");
    PRINTF("READ:  $devmem2 0x%X\n\r",SHARED_MEM_ADDRESS);
    PRINTF("WRITE the value of 0xCC: $devmem2 0x%X w 0xCC\n\r",SHARED_MEM_ADDRESS);
    PRINTF("-------------------------------------------------------------------------------\n\r");


    // write 0xBB in the shared memory address
    *ddr_shared_mem=0xBB;
    while(1)
    {

        // First, get character
        receiveBuff = GETCHAR();

        switch(receiveBuff) {
            case 'r' :
                PRINTF("\r\n0x%X: 0x%X\n\r",SHARED_MEM_ADDRESS, *ddr_shared_mem);
                break;
            case 'w' :
                PRINTF("\r\nWrite a value for this register (in HEX): \n\r");
                SCANF("%X", &value);
                PRINTF("\r\nValue: 0x%X \n\r",value);
                *ddr_shared_mem = value;
                break;
        }
        
    }
}

int main(void)
{
    // Initialize demo application pins setting and clock setting.
    hardware_init();

    // Create a demo task which will print the shared memory content.
    xTaskCreate(SharedMemTask, "Print Task", configMINIMAL_STACK_SIZE,
                NULL, tskIDLE_PRIORITY+1, NULL);

    // Start FreeRTOS scheduler.
    vTaskStartScheduler();

    // Should never reach this point.
    while (true);
}

On linux, you reserve the memory address and read from it, as an example:

reserved-memory {
	cmddr0@80000000 {
		reg = <0x8FF00000 0x100000>;
		no-map;
	};
};

So, for example, this removes all the overhead from the RPMSG framework but also introduces a lot of issues that you will need to deal with (threads, don’t write at the same time…). This is just a very simple example, not suitable for more complex scenarios.

Best Regards,
Hiago.

1 Like

Hi @hfranco.tx

reserved-memory {
	cmddr0@80000000 {
		reg = <0x8FF00000 0x100000>;
		no-map;
	};
};

When i build my custom image, how do i need to specify this?

I tried:

  device-tree:
    overlays:
      add:
        - device-tree/shared-ram-mcu.dts

shared-ram-mcu.dts:

reserved-memory {
	cmddr0@80000000 {
		reg = <0x8FF00000 0x100000>;
		no-map;
	};
};

Results in:

=> Adding device-tree overlay 'device-tree/shared-ram-mcu.dts'
Error: device-tree/shared-ram-mcu.dts:1.1-2 syntax error
FATAL ERROR: Unable to parse input tree
error: cannot apply device-tree/shared-ram-mcu.dts.

Q1: Is this meant as DT-overlay or standalone?
Q2: Is there some documentation to learn how to write device-trees (for newbies)?

Hi @chalu,

My reply was just an example of the device tree overlay, copying and pasting will not work. Let me put here what the entire code should look like:

/dts-v1/;
/plugin/;

/ {
	compatible = "toradex,verdin-imx8mp";
};

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

         cmddr0@80000000 {
                reg = <0 0x8FF00000 0 0x100000>;
                no-map;
         };
};

This should be the correct overlay. Please note that the memory region that I’m using it’s just an example and it wasn’t tested for the Verdin iMX8M Plus, if something fails, please check the ram address and make sure nothing is overlapping.

As you’re using the Verdin iMX8M Plus, please also check our overlay to enable the RPMSG and Remote proc features: verdin-imx8mp_hmp_overlay.dts « overlays - device-tree-overlays.git - Sources for Device Tree Overlays

Let me know if you have any questions.

Best Regards,
Hiago.

hi @hfranco.tx

ok here we go:

firmware:

#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"

#define SHARED_MEM_ADDRESS 0x8FF00000

int main(void)
{
  BOARD_InitMemory();
  BOARD_RdcInit();
  BOARD_InitBootPins();
  BOARD_BootClockRUN();
  BOARD_InitDebugConsole();

  uint8_t receiveBuff;
  uint32_t value = 0;
  uint32_t *ddr_shared_mem = (uint32_t*)SHARED_MEM_ADDRESS;

  PRINTF("\r\n\r\n\r\n\r\n\r\n\r\n\r\n");
  PRINTF("Shared Memory Demo!\r\n");
  PRINTF("Shared address: 0x%X\r\n", SHARED_MEM_ADDRESS);
  PRINTF("Type r to show the contend of shared memory address (0x%X)\r\n", SHARED_MEM_ADDRESS);
  PRINTF("Type w to write new value in the shared memory address (0x%X)\r\n", SHARED_MEM_ADDRESS);
  PRINTF("You can also write and read the shared memory from linux with devmem2 utility:\r\n");
  PRINTF("READ:  $devmem2 0x%X\r\n", SHARED_MEM_ADDRESS);
  PRINTF("WRITE the value of 0xCC: $devmem2 0x%X w 0xCC\r\n", SHARED_MEM_ADDRESS);

  // write 0xBB in the shared memory address
  *ddr_shared_mem=0xBB;
  
  while(1)
  {
    // First, get character
    receiveBuff = GETCHAR();

    switch(receiveBuff) 
    {
      case 'r' :
      {
        PRINTF("Read from SHARED_MEM_ADDRESS: addr=0x%X: value=0x%X\r\n",
          SHARED_MEM_ADDRESS, 
          *ddr_shared_mem);
      } break;

      case 'w' :
      {
        PRINTF("\r\nWrite a value for this register (in HEX): \r\n");
        SCANF("%X", &value);
        PRINTF("\r\nValue: 0x%X \r\n",value);
        *ddr_shared_mem = value;
      } break;
    }
  }

  while (1)
  {
    PRINTF("DO NOT GET HERE!\r\n");
  }

  return 0;
}

I ran the code on both the iMX8MP + Yavia and iMX8MP + Verdin. When starting the MCU from uBoot via “run m4boot” itself alone is happy: can read and write a byte. When i start the kernel as well, it crashes. THe firmware seems to initialize I2C3 and UART4. So i tried to exclude them from linux:

// comment

/dts-v1/;
/plugin/;

/ {
	compatible = "toradex,verdin-imx8mp";
};

&adma_uart4 {
	status = "disabled";
};

&uart4 {
	status = "disabled";
};

// &i2c0 {
// 	status = "disabled";
// };
// &i2c1 {
// 	status = "disabled";
// };
// &i2c2 {
// 	status = "disabled";
// };
&i2c3 {
	status = "disabled";
};

// &adma_i2c0 {
// 	status = "disabled";
// };
// &adma_i2c1 {
// 	status = "disabled";
// };
// &adma_i2c2 {
// 	status = "disabled";
// };
&adma_i2c3 {
	status = "disabled";
};

THis builds successfully but still crashes. Cannot get the linux side to boot in order to test the shared ram.
Where can i find the "default device tree so i can copy from? eg copy uart4 from the full device tree and overload it with status disabled, thats it!?

[    0.881159] Internal error: synchronous external abort: 96000210 [#1] PREEMPT SMP
[    0.888658] Modules linked in:
[    0.891721] CPU: 3 PID: 1 Comm: swapper/0 Not tainted 5.15.77-6.2.0+git.aa0ff7e3554e #1-TorizonCore
[    0.900775] Hardware name: Toradex Verdin iMX8M Plus WB on Verdin Development Board (DT)
[    0.908872] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[    0.915843] pc : imx_uart_probe+0x31c/0x7d0
[    0.920039] lr : imx_uart_probe+0x30c/0x7d0
[    0.924233] sp : ffff80000a28bb60
[    0.927549] x29: ffff80000a28bb60 x28: 0000000000000000 x27: ffff800009a204c0
[    0.934700] x26: ffff0000c4c7fc00 x25: 00000000fffffffa x24: 00000000fffffffa
[    0.941850] x23: ffff0000c015b410 x22: 000000000000002c x21: ffff0000c015b400
[    0.949000] x20: 0000000000000000 x19: ffff0000c4c1c080 x18: ffffffffffffffff
[    0.956150] x17: 647561625f657361 x16: 62202c3633203d20 x15: ffff0000c4c7f78a
[    0.963298] x14: ffffffffffffffff x13: 0000000000000038 x12: 0101010101010101
[    0.970445] x11: 0000000000000000 x10: 0101010101010101 x9 : 0000000000000004
[    0.977595] x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : fffefefefeff6f6c
[    0.984744] x5 : 8080808080800000 x4 : 0000000000000010 x3 : 0000000000000000
[    0.991892] x2 : 0000000000000000 x1 : ffff80000aac0080 x0 : 0000000000000000

Hi @chalu,

Before going into this problem, can you please check if the overlay that I shared with you works for exchanging data with RPMSG?

You can add the overlay to your overlays.txt file, then compile the multicore example from NXP, like evkmimx8mp/multicore_examples/rpmsg_lite_str_echo_rtos for example, and check if you can see both processors exchanging data on the /dev/rpmsg (you can use the “echo” command for that).

I would like to do that first because the error you shared looks like there is something wrong with your overlay. The Linux kernel is trying to access a peripheral that is owned by the Cortex-M, so a kernel panic occurs.

If it works with our overlay, then you can take a look at how our overlay is implemented and copy the necessary parts to fix your overlay.

Let me know if this helps somehow.

Best Regards,
Hiago.

Hi @hfranco.tx

Ok i can test rpmsg first.

Im having trouble compiling your overlay:

1. Include missing
1.1 Found it on Github but seems deprecated (V5 Only)
1.2 Even if i ignore it and clone it into my project dir, i cannot solve the include path (i dont know where to set include paths so the builder can find it)
1.3 alternatively i tried “torizoncore-builder dt checkout”, but thats also no longer supported in v6

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

2.0 this does not compile, says syntax issues (maybe follow up since the include is missing)

&{/} {
	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>;
	};
};

Hi @chalu,

Instead of trying to compile it yourself, this overlay should be included with your image on our latest nightly build. I believe it’s easier to download one of these images and test it out of the box. Please check this thread for more information on which image to download: Support of rpmsg in TorizonCore on Verdin iMX8MP - #15 by vix. Also, check my last answer there, it will be necessary to configure u-boot properly.

The images (including the nightly builds) you can find them here: Toradex Download Links (Torizon, Yocto Project Reference Images, WinCE and Partner Demos) | Toradex Developer Center

Best Regards,
Hiago.

hi @hfranco.tx

Ok got it, tested as follows:

i.MX8MP on Yavia (Is yavia OK or does this just work on the verdin carrier?), as described in our link to the other thread in this forum:

Base image: https://artifacts.toradex.com/artifactory/torizoncore-oe-prerelease-frankfurt/kirkstone-6.x.y/nightly/255/verdin-imx8mp/torizon/torizon-core-docker/oedeploy/torizon-core-docker-verdin-imx8mp-Tezi_6.3.0-devel-20230427+build.255.tar

cat /proc/cmdline
# root=LABEL=otaroot rootfstype=ext4 quiet logo.nologo vt.global_cursor_default=0 plymouth.ignore-serial-consoles splash fbcon=map:3 ostree=/ostree/boot.0/torizon/ca91836f6f15baebacea0844370a92f612af6cf310188cc3df6f1494dfaba1ee/0 loglevel=3
¨

ls /sysroot/boot/ostree/torizon-ca91836f6f15baebacea0844370a92f612af6cf310188cc3df6f1494dfaba1ee/dtb/overlays
# custom-kargs_overlay.dtbo
# verdin-imx8mp_dsi-to-hdmi_overlay.dtbo
# verdin-imx8mp_dsi-to-lvds_panel-cap-touch-10inch-lvds_overlay.dtbo
# verdin-imx8mp_dsi-to-lvds_panel-lvds-dual-channel-1080p_overlay.dtbo
# verdin-imx8mp_hdmi_overlay.dtbo
# verdin-imx8mp_hmp_overlay.dtbo        <------------------------------------------
# verdin-imx8mp_mezzanine_ov5640-alt-jumpers_overlay.dtbo
# verdin-imx8mp_mezzanine_ov5640-default-jumpers_overlay.dtbo
# verdin-imx8mp_mezzanine_panel-cap-touch-10inch-lvds_overlay.dtbo
# verdin-imx8mp_mezzanine_panel-lvds-dual-channel-1080p_overlay.dtbo
# verdin-imx8mp_nau8822-btl_overlay.dtbo
# verdin-imx8mp_ov5640_overlay.dtbo
# verdin-imx8mp_spidev_overlay.dtbo

cat /sysroot/boot/ostree/torizon-ca91836f6f15baebacea0844370a92f612af6cf310188cc3df6f1494dfaba1ee/dtb/overlays.txt
# fdt_overlays=verdin-imx8mp_hdmi_overlay.dtbo verdin-imx8mp_dsi-to-hdmi_overlay.dtbo verdin-imx8mp_spidev_overlay.dtbo custom-kargs_overlay.dtbo

# Note: verdin-imx8mp_hmp_overlay.dtbo is missing, add it 
sudo nano /sysroot/boot/ostree/torizon-ca91836f6f15baebacea0844370a92f612af6cf310188cc3df6f1494dfaba1ee/dtb/overlays.txt

sudo reboot

Wont boot:

Verdin iMX8MP # reset
resetting ...

U-Boot SPL 2022.04-6.2.0+git.0e1f11392251 (Jan 01 1970 - 00:00:00 +0000)
DDRINFO: start DRAM init
DDRINFO: DRAM rate 4000MTS
Training FAILED
DDRINFO: start DRAM init
DDRINFO: DRAM rate 4000MTS
DDRINFO:ddrphy calibration done
DDRINFO: ddrmix config done
DDR configured as single rank
SEC0:  RNG instantiated
Normal Boot
WDT:   Started watchdog@30280000 with servicing (60s timeout)
Trying to boot from BOOTROM
Boot Stage: Primary boot
Find img info 0x&4802d800, size 888
Download 916480, Total size 917024
NOTICE:  BL31: v2.6(release):lf_v2.6-g3c1583ba0a
NOTICE:  BL31: Built : 00:00:00, Jan  1 1970


U-Boot 2022.04-6.2.0+git.0e1f11392251 (Jan 01 1970 - 00:00:00 +0000)

CPU:   i.MX8MP[8] rev1.1 1600 MHz (running at 1200 MHz)
CPU:   Industrial temperature grade (-40C to 105C) at 44C
Reset cause: POR
DRAM:  4 GiB
Core:  89 devices, 23 uclasses, devicetree: separate
WDT:   Started watchdog@30280000 with servicing (60s timeout)
MMC:   FSL_SDHC: 1, FSL_SDHC: 2
Loading Environment from MMC... OK
In:    serial@30880000
Out:   serial@30880000
Err:   serial@30880000
Model: Toradex 0058 Verdin iMX8M Plus Quad 4GB WB IT V1.0D
Serial#: 07174558
wait_for_sr_state: Arbitration lost sr=93 cr=80 state=2020
i2c_init_transfer: failed for chip 0x57 retry=0
i2c_idle_bus: failed to clear bus, sda=0 scl=0
i2c_init_transfer: give up i2c_regs=0x30a50000
get_tdx_eeprom: cannot find EEPROM by node
MISSING TORADEX CARRIER CONFIG BLOCKS
wait_for_sr_state: Arbitration lost sr=93 cr=80 state=2020
i2c_init_transfer: failed for chip 0x57 retry=0
i2c_idle_bus: failed to clear bus, sda=0 scl=0
i2c_init_transfer: give up i2c_regs=0x30a50000
get_tdx_eeprom: cannot find EEPROM by node
Net:   eth1: ethernet@30be0000, eth0: ethernet@30bf0000 [PRIME]
Normal Boot
Hit any key to stop autoboot:  0

@hfranco.tx

Could you have a look at my boot log if you see the mistake?

Hi @chalu,

Sorry for the delay. This issue is caused by FDT relocation.

Please, run this command on your u-boot terminal to fix the issue:

Verdin iMX8MP # setenv fdt_high 0xffffffffffffffff 
Verdin iMX8MP # saveenv 
Saving Environment to MMC... Writing to MMC(2)... OK
Verdin iMX8MP # reset 

These three commands will fix your issue.

For more information m, please refer to this thread: Support of rpmsg in TorizonCore on Verdin iMX8MP - #16 by hfranco.tx

Let me know if you have any questions.

Best Regards,
Hiago.

Ok that fixes the linux boot issue and the board now starts up.

I compiled the

SDK_2_13_0_MIMX8ML8xxxKZ/boards/evkmimx8mp/multicore_examples/rpmsg_lite_str_echo_rtos

example and flashed it onto the board, which outputs the following:

RPMSG String Echo FreeRTOS RTOS API Demo...

Then i went ahead and searched for the RPMSG device which i need to write to (for the echo/pingpong).

I cannot find /dev/rpmsg

 # TODO: does not exist?
ls -lah /dev/rpmsg
#ls: cannot access '/dev/rpmsg': No such file or directory

What i can see is:

root@verdin-imx8mp-07174558:/dev# ls -lah /dev/ | grep "RPMSG"
crw-rw----  1 root    dialout 507,   0 May 12 16:03 ttyRPMSG30
root@verdin-imx8mp-07174558:/dev# ls -lah /dev/ | grep "rpmsg"
crw-------  1 root    root    509,   0 Apr 28  2022 rpmsg_ctrl0

Neither one seem to write/read to/from the MCU.

Q1: Should i see /dev/rpmsg or is it possible that it is mapped differently?

Hi @chalu,

After running the modprobe for the rpmsg echo demo (for example)

# modprobe imx_rpmsg_tty

You will see that /dev/ttyRPMSG30 was created.

Now you can use echo to write something and the message will be received by Cortex-M:

# echo test > /dev/ttyRPMSG30

Could you please check the steps above?

Best Regards,
Hiago.

Dear @chalu,

Do you have any updates on this topic?

Hello @rudhi.tx

Unfortunatelly no, i was not able to get it running together with the “rpmsg_lite_str_echo_rtos” example.

I see the “ttyRPMSG30” and “rpmsg_ctrl0” devices but if i write to them via bash-echo/printf, it does either not send characters over the bus or the firmware is not capable of picking it up.

Do you have a tested environment on your side that works? then we could compare what is different.

Hello @chalu,

You are probably not seeing the messages on ttyRPMSG30 because the UART 4 is by default being used by the Bluetooth/WiFi module on the Verdin imx8m-plus module. Also, UART 4 is the default cortex-m debug port used by NXP. To fix that you can change from UART4 to UART 2 or 1. Here is an example we made for Hello-World demo but it should also work for RPMsg demo:

diff --git a/boards/evkmimx8mp/demo_apps/hello_world_change_uart/board.h b/boards/evkmimx8mp/demo_apps/hello_world_change_uart/board.h
index 4f02852..b25ea8b 100644
--- a/boards/evkmimx8mp/demo_apps/hello_world_change_uart/board.h
+++ b/boards/evkmimx8mp/demo_apps/hello_world_change_uart/board.h
@@ -20,13 +20,13 @@
 /* The UART to use for debug messages. */
 #define BOARD_DEBUG_UART_TYPE     kSerialPort_Uart
 #define BOARD_DEBUG_UART_BAUDRATE (115200U)
-#define BOARD_DEBUG_UART_BASEADDR UART4_BASE
-#define BOARD_DEBUG_UART_INSTANCE (4U)
+#define BOARD_DEBUG_UART_BASEADDR UART2_BASE
+#define BOARD_DEBUG_UART_INSTANCE (2U)
 #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
-#define BOARD_UART_IRQ_HANDLER UART4_IRQHandler
+    CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) / (CLOCK_GetRootPreDivider(kCLOCK_RootUart2)) / \
+        (CLOCK_GetRootPostDivider(kCLOCK_RootUart2)) / 10
+#define BOARD_UART_IRQ         UART2_IRQn
+#define BOARD_UART_IRQ_HANDLER UART2_IRQHandler
 
 #define BOARD_GPC_BASEADDR GPC
 #define BOARD_MU_IRQ       MU1_M7_IRQn
diff --git a/boards/evkmimx8mp/demo_apps/hello_world_change_uart/clock_config.c b/boards/evkmimx8mp/demo_apps/hello_world_change_uart/clock_config.c
index 82a7869..d2d611c 100644
--- a/boards/evkmimx8mp/demo_apps/hello_world_change_uart/clock_config.c
+++ b/boards/evkmimx8mp/demo_apps/hello_world_change_uart/clock_config.c
@@ -102,8 +102,8 @@ void BOARD_BootClockRUN(void)
     CLOCK_SetRootDivider(kCLOCK_RootAudioAhb, 1U, 2U);                    /* Set root clock freq to 800MHZ/ 2= 400MHZ*/
     CLOCK_SetRootMux(kCLOCK_RootAudioAhb, kCLOCK_AudioAhbRootmuxSysPll1); /* switch AUDIO AHB to SYSTEM PLL1 */
 
-    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_RootUart2, kCLOCK_UartRootmuxSysPll1Div10); /* Set UART source to SysPLL1 Div10 80MHZ */
+    CLOCK_SetRootDivider(kCLOCK_RootUart2, 1U, 1U);                     /* Set root clock to 80MHZ/ 1= 80MHZ */
 
     CLOCK_EnableClock(kCLOCK_Rdc);   /* Enable RDC clock */
     CLOCK_EnableClock(kCLOCK_Ocram); /* Enable Ocram clock */
diff --git a/boards/evkmimx8mp/demo_apps/hello_world_change_uart/hello_world.c b/boards/evkmimx8mp/demo_apps/hello_world_change_uart/hello_world.c
index 10c0a4b..a3f06e9 100644
--- a/boards/evkmimx8mp/demo_apps/hello_world_change_uart/hello_world.c
+++ b/boards/evkmimx8mp/demo_apps/hello_world_change_uart/hello_world.c
@@ -29,8 +29,6 @@
  */
 int main(void)
 {
-    char ch;
-
     /* Init board hardware. */
     /* M7 has its local cache and enabled by default,
      * need to set smart subsystems (0x28000000 ~ 0x3FFFFFFF)
@@ -44,11 +42,16 @@ int main(void)
     BOARD_BootClockRUN();
     BOARD_InitDebugConsole();
 
-    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/evkmimx8mp/demo_apps/hello_world_change_uart/pin_mux.c b/boards/evkmimx8mp/demo_apps/hello_world_change_uart/pin_mux.c
index 95c21cb..1120c9c 100644
--- a/boards/evkmimx8mp/demo_apps/hello_world_change_uart/pin_mux.c
+++ b/boards/evkmimx8mp/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-M7F[m7] */
-    IOMUXC_SetPinMux(IOMUXC_UART4_RXD_UART4_RX, 0U);
-    IOMUXC_SetPinConfig(IOMUXC_UART4_RXD_UART4_RX, 
+    IOMUXC_SetPinMux(IOMUXC_UART2_RXD_UART2_RX, 0U);
+    IOMUXC_SetPinConfig(IOMUXC_UART2_RXD_UART2_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_SetPinMux(IOMUXC_UART2_TXD_UART2_TX, 0U);
+    IOMUXC_SetPinConfig(IOMUXC_UART2_TXD_UART2_TX, 
                         IOMUXC_SW_PAD_CTL_PAD_PUE_MASK |
                         IOMUXC_SW_PAD_CTL_PAD_PE_MASK);
 }

For UART 1 it should work by default but for UART 2 you will have to run the following command in U-Boot terminal:
Verdin iMX8MP # mw.l 0x303d05a4 0xff
Please see this thread for more info: Embed M7 firmware in TorizonCore and load it automatically on Verdin iMX8M-Mini - #24 by vix

Please let us know if that solves the issue.