Cortex-M RPMsg Guide
Introduction
The goal of this article is to provide a comprehensive guide on how to use the RPMsg protocol within a Heterogeneous Multicore Processing (HMP) environment. There is a quick overview of how it works and how to leverage Toradex's out-of-the-box resources.
Why RPMsg
RPMsg (or Remote Processor Messaging) is a communication protocol for inter-processor communication on embedded Linux systems. It enables efficient data exchange between processors in a heterogeneous multi-core environment, facilitating seamless integration and improved system performance.
For more information, check the OpenAMP website or, for NXP downstream kernel, check the RPMsg-Lite repository.
Device Tree Modifications
For RPMsg to work properly, it is necessary to allocate the correct RAM buffers in the device tree file. Toradex provides some device tree overlays for an out-of-the-box experience with HMP. The overlays are compatible with all Downstream and upstream-based modules starting from BSP and Torizon OS 6.
Check the following table for information about the status of the overlays:
Overlay | Status | Device Tree Overlays Repository Branch |
---|---|---|
verdin-imx8mp_hmp_overlay.dts | Available | toradex_5.15-2.2.x-imx |
verdin-imx8mp_hmp_overlay.dts | Coming soon | master |
verdin-imx8mm_hmp_overlay.dts | Available | toradex_5.15-2.2.x-imx |
verdin-imx8mm_hmp_overlay.dts | Available | master |
verdin-am62_hmp_overlay.dts | Available | toradex_ti-linux-6.1.y |
apalis-imx8_hmp_overlay.dts | Coming soon | toradex_5.15-2.2.x-imx |
colibri-imx8x_hmp_overlay.dts(*1) | Available | toradex_5.15-2.2.x-imx |
colibri-imx7_hmp_overlay.dts | Coming soon | master |
(*1): Note that this overlay only works to enable RPMsg.
For overlays marked as available, they are pre-compiled (*.dtbo
) into Torizon OS and our BSP reference images. Their source code is also available on Toradex device-tree-overlays.git repository under the specific branch
.
If you are interested in the DTOs marked as "coming soon", get in touch with us on the Toradex Community.
For information on how to add and enable an overlay, refer to First Steps with Device Tree Overlays.
How the Buffer Reservation Works
Let's take the Verdin iMX8M Mini as example. After downloading the MCUXpresso SDK, go to the boards/evkmimx8mm/multicore_examples/rpmsg_lite_str_echo_rtos
folder.
On the main_remote.c
file , we can see some definitions related to RPMSG:
/*******************************************************************************
* Definitions
******************************************************************************/
#define RPMSG_LITE_SHMEM_BASE (VDEV0_VRING_BASE)
#define RPMSG_LITE_LINK_ID (RL_PLATFORM_IMX8MM_M4_USER_LINK_ID)
#define RPMSG_LITE_NS_ANNOUNCE_STRING "rpmsg-virtual-tty-channel-1"
#define APP_TASK_STACK_SIZE (256)
#ifndef LOCAL_EPT_ADDR
#define LOCAL_EPT_ADDR (30)
And searching for these definitions, we find the memory addresses inside the board.h
file:
/* Shared memory base for RPMsg communication. */
#define VDEV0_VRING_BASE (0xB8000000U)
#define RESOURCE_TABLE_OFFSET (0xFF000)
There is also more information in the rsc_table.c
file:
/* Vring rsc entry - part of vdev rsc entry */
{VDEV0_VRING_BASE, VRING_ALIGN, RL_BUFFER_COUNT, 0, 0},
{VDEV0_VRING_BASE + VRING_SIZE, VRING_ALIGN, RL_BUFFER_COUNT, 1, 0},
};
void copyResourceTable(void)
{
/*
* Resource table should be copied to VDEV0_VRING_BASE + RESOURCE_TABLE_OFFSET.
* VDEV0_VRING_BASE is temperorily kept for backward compatibility, will be
* removed in future release
*/
memcpy((void *)VDEV0_VRING_BASE, &resources, sizeof(resources));
memcpy((void *)(VDEV0_VRING_BASE + RESOURCE_TABLE_OFFSET), &resources, sizeof(resources));
}
Using these addresses and also checking the imx_rpmsg.c file, we can set the following memory addresses:
vdev0vring0: vdev0vring0@b8000000 {
reg = <0 0xb8000000 0 0x8000>;
no-map;
};
vdev0vring1: vdev0vring1@b8008000 {
reg = <0 0xb8008000 0 0x8000>;
no-map;
};
rsc_table: rsc_table@b80ff000 {
reg = <0 0xb80ff000 0 0x1000>;
no-map;
};
vdevbuffer: vdevbuffer@b8400000 {
compatible = "shared-dma-pool";
reg = <0 0xb8400000 0 0x100000>;
no-map;
};
Loading the RPMsg Linux Driver
After the device tree has been modified and the module has been rebooted, the driver should appear as "registered" in the dmesg log:
# dmesg | grep -i rpmsg
[ 0.045742] imx rpmsg driver is registered.
Then, you are able to load the rpmsg driver from MCUXpresso. Follow the How to Load Compiled Binaries into Cortex-M guide on how to compile a demo and how to load it to your module.
RPMsg TTY Demo
First, follow the instructions on How to Load Compiled Binaries into Cortex-M article and compile the rpmsg_lite_str_echo_rtos
demo binary. Then, send it to the module, and load on U-Boot.
Then, you should see the following message in the debug UART:
RPMSG String Echo FreeRTOS RTOS API Demo...
After booting Linux, if the RPMsg is correctly configured, the following message should appear on the Cortex-M side:
RPMSG String Echo FreeRTOS RTOS API Demo...
Nameservice sent, ready for incoming messages...
The dmesg
log will also show that RPMsg has been correctly configured:
# dmesg | grep -i rpmsg
[ 0.045793] imx rpmsg driver is registered.
[ 1.329068] virtio_rpmsg_bus virtio0: rpmsg host is online
[ 1.329111] virtio_rpmsg_bus virtio0: creating channel rpmsg-virtual-tty-channel-1 addr 0x1e
If RPMsg is correctly configured on the system, load the imx_rpmsg_tty
kernel module.
# sudo modprobe imx_rpmsg_tty
After the kernel module has been loaded, the Cortex-M should print a "Hello World" in the screen, showing that the channel has been created correctly:
RPMSG String Echo FreeRTOS RTOS API Demo...
Nameservice sent, ready for incoming messages...
Get Message From Master Side : "hello world!" [len : 12]
And a new tty device will be created:
# ls /dev/ | grep -i rpmsg
rpmsg_ctrl0
ttyRPMSG30
Then, you are able to exchange data by writing to the tty device:
# echo Toradex! > /dev/ttyRPMSG30
You can see the message being received on the Cortex-M side:
RPMSG String Echo FreeRTOS RTOS API Demo...
Nameservice sent, ready for incoming messages...
Get Message From Master Side : "hello world!" [len : 12]
Get Message From Master Side : "Toradex!" [len : 8]
Get New Line From Master Side
Ping Pong RPMsg Demo
For this demo, compile the evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote
example and load it on U-Boot as explained in the How to load compiled binaries into Cortex-M. The following message will be printed in the Cortex-M debug UART:
RPMSG Ping-Pong FreeRTOS RTOS API Demo...
RPMSG Share Base Addr is 0xb8000000
After Linux has been booted, the following messages should appear:
RPMSG Ping-Pong FreeRTOS RTOS API Demo...
RPMSG Share Base Addr is 0xb8000000
Link is up!
Nameservice announce sent.
Now load the kernel module:
# sudo modprobe imx_rpmsg_pingpong
The messages should appear on the Cortex-M side:
Sending pong...
Waiting for ping...
Sending pong...
Waiting for ping...
Sending pong...
Waiting for ping...
Sending pong...
Waiting for ping...
Sending pong...
Waiting for ping...
Sending pong...
Waiting for ping...
Sending pong...
Waiting for ping...
Sending pong...
Ping pong done, deinitializing...
Looping forever...
And also on the Linux side:
# dmesg | grep ping
[ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 516096
[ 0.920924] SMCCC: SOC_ID: ARCH_SOC_ID not implemented, skipping ....
[ 122.533450] imx_rpmsg_pingpong virtio0.rpmsg-openamp-demo-channel.-1.30: new channel: 0x400 -> 0x1e!
[ 122.704607] imx_rpmsg_pingpong virtio0.rpmsg-openamp-demo-channel.-1.30: goodbye!