Skip to main content

Basic AMP on the i.MX8M Mini with Rpmsg

giant-gd1d82756c_640.jpg

In a recent post, I described the basics to execute code on the Cortex M4 micro controller inside the i.MX8M Mini SoC. However there was no communication going on between Linux on the Cortex A cores and the application running on the M4. In this post, I will look at an example using the remoteproc and rpmsg (Remote Processor Messaging) frameworks for communication between the processors. The remoteproc subsystem implements basic house keeping for co-processors in Linux and the rpmsg framework implements the actual data channels.

The M4 application

Before running the application, let's take a look at the source code. We can find it in the MCUXpresso SDK checked out via west in the previous post. The sample is below examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote and the main function is contained in main_remote.c . For completeness, this is the whole source file, but we can concentrate on the function app_task and keep in mind that the pre-processor symbol RPMSG_LITE_MASTER_IS_LINUX is defined but MCUMGR_USED isn't. We also need to remember that this application is using the FreeRTOS implementation from the MCUXpresso SDK:

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rpmsg_lite.h"
#include "rpmsg_queue.h"
#include "rpmsg_ns.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_debug_console.h"
#include "FreeRTOS.h"
#include "task.h"

#include "fsl_uart.h"
#include "rsc_table.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define RPMSG_LITE_LINK_ID            (RL_PLATFORM_IMX8MM_M4_USER_LINK_ID)
#define RPMSG_LITE_SHMEM_BASE         (VDEV0_VRING_BASE)
#define RPMSG_LITE_NS_ANNOUNCE_STRING "rpmsg-openamp-demo-channel"
#define RPMSG_LITE_MASTER_IS_LINUX

#define APP_DEBUG_UART_BAUDRATE (115200U) /* Debug console baud rate. */
#define APP_TASK_STACK_SIZE (256U)
#ifndef LOCAL_EPT_ADDR
#define LOCAL_EPT_ADDR (30U)
#endif
#define APP_RPMSG_READY_EVENT_DATA (1U)

typedef struct the_message
{
    uint32_t DATA;
} THE_MESSAGE, *THE_MESSAGE_PTR;

static volatile THE_MESSAGE msg = {0};
#ifdef RPMSG_LITE_MASTER_IS_LINUX
static char helloMsg[13];
#endif /* RPMSG_LITE_MASTER_IS_LINUX */

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

/*******************************************************************************
 * Code
 ******************************************************************************/
static TaskHandle_t app_task_handle = NULL;

static void app_nameservice_isr_cb(uint32_t new_ept, const char *new_ept_name, uint32_t flags, void *user_data)
{
}

#ifdef MCMGR_USED
/*!
 * @brief Application-specific implementation of the SystemInitHook() weak function.
 */
void SystemInitHook(void)
{
    /* Initialize MCMGR - low level multicore management library. Call this
       function as close to the reset entry as possible to allow CoreUp event
       triggering. The SystemInitHook() weak function overloading is used in this
       application. */
    (void)MCMGR_EarlyInit();
}
#endif /* MCMGR_USED */

static void app_task(void *param)
{
    volatile uint32_t remote_addr;
    struct rpmsg_lite_endpoint *volatile my_ept;
    volatile rpmsg_queue_handle my_queue;
    struct rpmsg_lite_instance *volatile my_rpmsg;
    volatile rpmsg_ns_handle ns_handle;

    /* Print the initial banner */
    (void)PRINTF("\r\nRPMSG Ping-Pong FreeRTOS RTOS API Demo...\r\n");

#ifdef MCMGR_USED
    uint32_t startupData;
    mcmgr_status_t status;

    /* Get the startup data */
    do
    {
	status = MCMGR_GetStartupData(&startupData);
    } while (status != kStatus_MCMGR_Success);

    my_rpmsg = rpmsg_lite_remote_init((void *)(char *)startupData, RPMSG_LITE_LINK_ID, RL_NO_FLAGS);

    /* Signal the other core we are ready by triggering the event and passing the APP_RPMSG_READY_EVENT_DATA */
    (void)MCMGR_TriggerEvent(kMCMGR_RemoteApplicationEvent, APP_RPMSG_READY_EVENT_DATA);
#else
    (void)PRINTF("RPMSG Share Base Addr is 0x%x\r\n", RPMSG_LITE_SHMEM_BASE);
    my_rpmsg = rpmsg_lite_remote_init((void *)RPMSG_LITE_SHMEM_BASE, RPMSG_LITE_LINK_ID, RL_NO_FLAGS);
#endif /* MCMGR_USED */
    while (0 == rpmsg_lite_is_link_up(my_rpmsg))
    {
    }
    (void)PRINTF("Link is up!\r\n");

    my_queue  = rpmsg_queue_create(my_rpmsg);
    my_ept    = rpmsg_lite_create_ept(my_rpmsg, LOCAL_EPT_ADDR, rpmsg_queue_rx_cb, my_queue);
    ns_handle = rpmsg_ns_bind(my_rpmsg, app_nameservice_isr_cb, ((void *)0));
    /* Introduce some delay to avoid NS announce message not being captured by the master side.
       This could happen when the remote side execution is too fast and the NS announce message is triggered
       before the nameservice_isr_cb is registered on the master side. */
    SDK_DelayAtLeastUs(1000000U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
    (void)rpmsg_ns_announce(my_rpmsg, my_ept, RPMSG_LITE_NS_ANNOUNCE_STRING, (uint32_t)RL_NS_CREATE);
    (void)PRINTF("Nameservice announce sent.\r\n");

#ifdef RPMSG_LITE_MASTER_IS_LINUX
    /* Wait Hello handshake message from Remote Core. */
    (void)rpmsg_queue_recv(my_rpmsg, my_queue, (uint32_t *)&remote_addr, helloMsg, sizeof(helloMsg), ((void *)0),
			   RL_BLOCK);
#endif /* RPMSG_LITE_MASTER_IS_LINUX */

    while (msg.DATA <= 100U)
    {
	(void)PRINTF("Waiting for ping...\r\n");
	(void)rpmsg_queue_recv(my_rpmsg, my_queue, (uint32_t *)&remote_addr, (char *)&msg, sizeof(THE_MESSAGE),
			       ((void *)0), RL_BLOCK);
	msg.DATA++;
	(void)PRINTF("Sending pong...\r\n");
	(void)rpmsg_lite_send(my_rpmsg, my_ept, remote_addr, (char *)&msg, sizeof(THE_MESSAGE), RL_BLOCK);
    }

    (void)PRINTF("Ping pong done, deinitializing...\r\n");

    (void)rpmsg_lite_destroy_ept(my_rpmsg, my_ept);
    my_ept = ((void *)0);
    (void)rpmsg_queue_destroy(my_rpmsg, my_queue);
    my_queue = ((void *)0);
    (void)rpmsg_ns_unbind(my_rpmsg, ns_handle);
    (void)rpmsg_lite_deinit(my_rpmsg);
    msg.DATA = 0U;

    (void)PRINTF("Looping forever...\r\n");

    /* End of the example */
    for (;;)
    {
    }
}

/*!
 * @brief Main function
 */
int main(void)
{
    /* Initialize standard SDK demo application pins */
    /* Board specific RDC settings */
    BOARD_RdcInit();

    BOARD_InitBootPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    BOARD_InitMemory();

    copyResourceTable();

#ifdef MCMGR_USED
    /* Initialize MCMGR before calling its API */
    (void)MCMGR_Init();
#endif /* MCMGR_USED */

    if (xTaskCreate(app_task, "APP_TASK", APP_TASK_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1U, &app_task_handle) != pdPASS)
    {
	(void)PRINTF("\r\nFailed to create application task\r\n");
	for (;;)
	{
	}
    }

    vTaskStartScheduler();

    (void)PRINTF("Failed to start FreeRTOS on core0.\r\n");
    for (;;)
    {
    }
}

Cross compilation

Continuing with the setup from the last blog post, cross compilation is easy:

dzu@krikkit:/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote ((MCUX_2.10.0))$ cd armgcc/
dzu@krikkit:/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/armgcc ((MCUX_2.10.0))$ ./build_debug.sh 
-- TOOLCHAIN_DIR: /usr
-- BUILD_TYPE: debug
-- TOOLCHAIN_DIR: /usr
-- BUILD_TYPE: debug
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/arm-none-eabi-gcc
-- The C compiler identification is GNU 10.3.1
-- The CXX compiler identification is GNU 10.3.1
middleware_multicore_rpmsg_lite_imx8mm_m4_freertos component is included.
middleware_multicore_rpmsg_lite component is included.
middleware_freertos-kernel_heap_4 component is included.
middleware_freertos-kernel component is included.
middleware_freertos-kernel_extension component is included.
driver_clock component is included.
driver_common component is included.
driver_reset component is included.
device_CMSIS component is included.
CMSIS_Include_core_cm component is included.
driver_mu component is included.
driver_rdc component is included.
utility_debug_console component is included.
component_serial_manager component is included.
component_serial_manager_uart component is included.
component_iuart_adapter component is included.
driver_iuart component is included.
component_lists component is included.
device_startup component is included.
device_system component is included.
utility_assert component is included.
utilities_misc_utilities component is included.
-- Configuring done
-- Generating done
-- Build files have been written to: /opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/armgcc
Scanning dependencies of target rpmsg_lite_pingpong_rtos_linux_remote.elf
[  7%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/boards/evkmimx8mm/board.c.obj
[  7%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/middleware/multicore/rpmsg_lite/lib/common/llist.c.obj
[  7%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/middleware/multicore/rpmsg_lite/lib/rpmsg_lite/porting/platform/imx8mm_m4/rpmsg_platform.c.obj
[ 13%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/pin_mux.c.obj
[ 13%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/boards/evkmimx8mm/clock_config.c.obj
[ 18%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/main_remote.c.obj
[ 18%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/rsc_table.c.obj
[ 21%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/middleware/multicore/rpmsg_lite/lib/rpmsg_lite/porting/environment/rpmsg_env_freertos.c.obj
[ 23%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/middleware/multicore/rpmsg_lite/lib/virtio/virtqueue.c.obj
[ 28%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/middleware/multicore/rpmsg_lite/lib/rpmsg_lite/rpmsg_lite.c.obj
[ 31%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/middleware/multicore/rpmsg_lite/lib/rpmsg_lite/rpmsg_queue.c.obj
[ 28%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/middleware/multicore/rpmsg_lite/lib/rpmsg_lite/rpmsg_ns.c.obj
[ 34%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/portable/MemMang/heap_4.c.obj
[ 36%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/tasks.c.obj
[ 39%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/event_groups.c.obj
[ 42%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/devices/MIMX8MM6/drivers/fsl_clock.c.obj
[ 44%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/drivers/common/fsl_common_arm.c.obj
[ 47%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/stream_buffer.c.obj
[ 50%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/croutine.c.obj
[ 52%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/drivers/common/fsl_common.c.obj
[ 55%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/timers.c.obj
[ 57%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/list.c.obj
[ 60%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/drivers/mu/fsl_mu.c.obj
[ 63%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/portable/GCC/ARM_CM4F/port.c.obj
[ 65%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/rtos/freertos/freertos_kernel/queue.c.obj
[ 68%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/drivers/rdc/fsl_rdc.c.obj
[ 71%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/utilities/debug_console/debug_console/fsl_debug_console.c.obj
[ 73%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/utilities/debug_console/str/fsl_str.c.obj
[ 76%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/components/serial_manager/fsl_component_serial_manager.c.obj
[ 78%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/components/serial_manager/fsl_component_serial_port_uart.c.obj
[ 81%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/components/uart/fsl_adapter_iuart.c.obj
[ 84%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/drivers/iuart/fsl_uart.c.obj
[ 86%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/components/lists/fsl_component_generic_list.c.obj
[ 89%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/devices/MIMX8MM6/system_MIMX8MM6_cm4.c.obj
[ 92%] Building ASM object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/devices/MIMX8MM6/gcc/startup_MIMX8MM6_cm4.S.obj
[ 94%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/utilities/assert/fsl_assert.c.obj
[ 97%] Building C object CMakeFiles/rpmsg_lite_pingpong_rtos_linux_remote.elf.dir/opt/src/git/mcuxsdk/core/utilities/misc_utilities/fsl_sbrk.c.obj
[100%] Linking C executable debug/rpmsg_lite_pingpong_rtos_linux_remote.elf
Memory region         Used Size  Region Size  %age Used
    m_interrupts:         576 B        576 B    100.00%
	  m_text:       35644 B     130496 B     27.31%
	  m_data:       43944 B       128 KB     33.53%
	 m_data2:          0 GB        16 MB      0.00%
[100%] Built target rpmsg_lite_pingpong_rtos_linux_remote.elf
dzu@krikkit:/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/armgcc ((MCUX_2.10.0))$ 

Let's copy the binary to the tftp area and setup a symbolic link to a more generic file name. In the next step we will use this file name in a script for U-Boot to load and start the M4 co-processor. Switching applications will then be a matter of adjusting the symlink rather than fiddling with the U-Boot environment on the target board:

dzu@krikkit:/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/armgcc ((MCUX_2.10.0))$ cp debug/rpmsg_lite_pingpong_rtos_linux_remote.bin /srv/tftp/
dzu@krikkit:/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/armgcc ((MCUX_2.10.0))$ pushd /srv/tftp/
/srv/tftp /opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/armgcc
dzu@krikkit:/srv/tftp$ ln -s rpmsg_lite_pingpong_rtos_linux_remote.bin m4_application.bin
dzu@krikkit:/srv/tftp$ popd
/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/armgcc
dzu@krikkit:/opt/src/git/mcuxsdk/examples/evkmimx8mm/multicore_examples/rpmsg_lite_pingpong_rtos/linux_remote/armgcc ((MCUX_2.10.0))$ 

With these preparations, let's write the script for U-Boot. Power up the EVK, connect to its serial console and stop U-Boot at the command line. Note that we do not do anything if the variable m4_application is empty. In this way we can disable the loading simply by removing this variable:

u-boot=> setenv m4_application m4_application.bin
u-boot=> setenv load_and_start_m4 'if test -n "$m4_application"; then tftp $loadaddr $m4_application; cp.b $loadaddr 0x7e0000 $filesize; bootaux 0x7e0000; fi'
u-boot=> saveenv
Saving Environment to MMC... Writing to MMC(1)... OK
u-boot=> 

Let's test the new script:

u-boot=> run load_and_start_m4 
Using ethernet@30be0000 device
TFTP from server 192.168.44.35; our IP address is 192.168.44.122
Filename 'm4_application.bin'.
Load address: 0x40480000
Loading: ###
	 4.3 MiB/s
done
Bytes transferred = 36332 (8dec hex)
## Starting auxiliary core stack = 0x20020000, pc = 0x1FFE03B9...
u-boot=> 

Cool - it works just like intended. Once this ran, you should see output on the serial port connected to the M4:

RPMSG Ping-Pong FreeRTOS RTOS API Demo...
RPMSG Share Base Addr is 0xb8000000

Booting Linux needs also a minor tweak as we need an extended device tree blob containing the required information for the rpmsg channels. With the U-Boot environment setup in a similar fashion to what I did above, all we need to do is to change the contents of the fdtfile variable from imx8mm-evk.dtb to imx8mm-evk-rpmsg.dtb:

u-boot=> setenv fdtfile imx8mm-evk-rpmsg.dtb        
u-boot=> saveenv
Saving Environment to MMC... Writing to MMC(1)... OK
u-boot=>

Now we can simply boot the kernel. Here is a truncated log of the boot process. Note that we do get some information from the rpmsg framework. The error about the txdb channel is not nice, but after checking the cause, it is actually to be expected. Looking at the whole boot log, this is not the only place where NXP should do some further cleanup to prevent drowning important messages from messages that are to be expected.

u-boot=> boot
starting USB...
Bus usb@32e40000: USB EHCI 1.00
scanning bus usb@32e40000 for devices... 2 USB Device(s) found
       scanning usb for storage devices... 0 Storage Device(s) found

Device 0: unknown device
switch to partitions #0, OK
mmc1 is current device
Scanning mmc 1:1...
48170 bytes read in 5 ms (9.2 MiB/s)
Scanning disk mmc@30b50000.blk...
Scanning disk mmc@30b60000.blk...
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
 ** Unrecognized filesystem type **
Found 21 disks
No EFI system partition
adv7535_mipi2hdmi adv7535@3d: Cant find cec device id=0x3c
fail to probe panel device adv7535@3d
mxs_video lcdif@32e00000: failed to get any video link display timings
BootOrder not defined
EFI boot manager: Cannot load any image
switch to partitions #0, OK
mmc2(part 0) is current device
 ** Unrecognized filesystem type **
Running BSP bootcmd ...
switch to partitions #0, OK
mmc1 is current device
Failed to load 'boot.scr'
30118400 bytes read in 1268 ms (22.7 MiB/s)
Booting from mmc ...
48170 bytes read in 5 ms (9.2 MiB/s)
Moving Image from 0x40480000 to 0x40600000, end=42350000
## Flattened Device Tree blob at 43000000
   Booting using the fdt blob at 0x43000000
   Using Device Tree in place at 0000000043000000, end 000000004300ec29
adv7535_mipi2hdmi adv7535@3d: Cant find cec device id=0x3c
fail to probe panel device adv7535@3d
mxs_video lcdif@32e00000: failed to get any video link display timings
probe video device failed, ret -22

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
[    0.000000] Linux version 5.10.72-lts-5.10.y+ga68e31b63f86 (oe-user@oe-host) (aarch64-poky-linux-gcc (GCC) 10.2.0, GNU ld (GNU Binutils) 2.36.1.20210209) #1 SMP PREEMPT Tue Nov 23 06:02:20 UTC 2021

[...]

[    2.381807] [drm] Initialized imx-drm 1.0.0 20120507 for 32c00000.bus:display-subsystem on minor 1
[    2.388489] imx6q-pcie 33800000.pcie:       IO 0x001ff80000..0x001ff8ffff -> 0x0000000000
[    2.405676] imx6q-pcie 33800000.pcie:      MEM 0x0018000000..0x001fefffff -> 0x0018000000
[    2.408430] sdhci-esdhc-imx 30b40000.mmc: voltage-ranges unspecified
[    2.408812] debugfs: Directory '30390000.reset-controller-src' with parent 'regmap' already present!
[    2.408949] imx-rproc imx8mm-cm4: mbox_request_channel_byname() could not locate channel named "txdb"
[    2.408954] imx-rproc imx8mm-cm4: No txdb, ret -22
[    2.409164] remoteproc remoteproc0: imx-rproc is available
[    2.409166] sdhci-esdhc-imx 30b50000.mmc: voltage-ranges unspecified
[    2.409207] sdhci-esdhc-imx 30b50000.mmc: Got CD GPIO
[    2.409241] remoteproc remoteproc0: attaching to imx-rproc
[    2.409333]  remoteproc0#vdev0buffer: assigned reserved memory node vdevbuffer@b8400000
[    2.413895] virtio_rpmsg_bus virtio0: rpmsg host is online
[    2.413923]  remoteproc0#vdev0buffer: registered virtio0 (type 7)
[    2.413949] imx6q-pcie 33800000.pcie: invalid resource
[    2.420329] sdhci-esdhc-imx 30b40000.mmc: allocated mmc-pwrseq
[    2.429421] remoteproc remoteproc0: remote processor imx-rproc is now attached
[    2.445628] mmc1: SDHCI controller on 30b50000.mmc [30b50000.mmc] using ADMA
[    2.449478] imx-audio-rpmsg imx-audio-rpmsg.1.auto: no reserved DMA memory
[    2.474348] mmc0: SDHCI controller on 30b40000.mmc [30b40000.mmc] using ADMA
[    2.505075] OF: graph: no port node found in /soc@0/bus@30800000/i2c@30a30000/tcpc@50/connector
[    2.533405] OF: graph: no port node found in /soc@0/bus@30800000/i2c@30a30000/tcpc@50/connector
[    2.547627] mmc0: queuing unknown CIS tuple 0x01 (3 bytes)
[    2.557657] imx-audio-rpmsg imx-audio-rpmsg.1.auto: no reserved DMA memory
[    2.558666] random: fast init done
[    2.565718] imx-audio-rpmsg imx-audio-rpmsg.1.auto: no reserved DMA memory

[...]

[  OK  ] Finished Update UTMP about System Runlevel Changes.

NXP i.MX Release Distro 5.10-hardknott imx8mmevk ttymxc1

imx8mmevk login: 

During the initialization of the rpmsg framework, we got further output on the M4 console:

RPMSG Ping-Pong FreeRTOS RTOS API Demo...
RPMSG Share Base Addr is 0xb8000000
Link is up!
Nameservice announce sent.

This shows us that Linux successfully found the shared resources and that we can proceed to use the rpmsg framework. The counterpart to the M4 application is the Linux kernel module imx-rpmsg-pingpong. Simply loading this module, starts the example ping pong between Linux and the M4:

root@imx8mmevk:~# modprobe imx_rpmsg_pingpong
[ 1333.546064] imx_rpmsg_pingpong virtio0.rpmsg-openamp-demo-channel.-1.30: new channel: 0x400 -> 0x1e!
[ 1333.556966] imx-audio-rpmsg imx-audio-rpmsg.1.auto: no reserved DMA memory
[ 1333.559012] get 1 (src: 0x1e)
root@imx8mmevk:~# [ 1333.568512] get 3 (src: 0x1e)
[ 1333.573536] get 5 (src: 0x1e)
[ 1333.578352] get 7 (src: 0x1e)
[ 1333.582874] get 9 (src: 0x1e)
[ 1333.587540] get 11 (src: 0x1e)
[ 1333.592232] get 13 (src: 0x1e)
[ 1333.596918] get 15 (src: 0x1e)
[ 1333.601913] get 17 (src: 0x1e)
[ 1333.606795] get 19 (src: 0x1e)
[ 1333.611400] get 21 (src: 0x1e)
[ 1333.616136] get 23 (src: 0x1e)
[ 1333.620826] get 25 (src: 0x1e)
[ 1333.625806] get 27 (src: 0x1e)
[ 1333.630749] get 29 (src: 0x1e)
[ 1333.635348] get 31 (src: 0x1e)
[ 1333.640089] get 33 (src: 0x1e)
[ 1333.644777] get 35 (src: 0x1e)
[ 1333.649463] get 37 (src: 0x1e)
[ 1333.654467] get 39 (src: 0x1e)
[ 1333.659060] get 41 (src: 0x1e)
[ 1333.663801] get 43 (src: 0x1e)
[ 1333.668487] get 45 (src: 0x1e)
[ 1333.673477] get 47 (src: 0x1e)
[ 1333.678356] get 49 (src: 0x1e)
[ 1333.682954] get 51 (src: 0x1e)
[ 1333.687691] get 53 (src: 0x1e)
[ 1333.692373] get 55 (src: 0x1e)
[ 1333.697066] get 57 (src: 0x1e)
[ 1333.701950] get 59 (src: 0x1e)
[ 1333.706535] get 61 (src: 0x1e)
[ 1333.711131] get 63 (src: 0x1e)
[ 1333.715908] get 65 (src: 0x1e)
[ 1333.720610] get 67 (src: 0x1e)
[ 1333.725320] get 69 (src: 0x1e)
[ 1333.729904] get 71 (src: 0x1e)
[ 1333.734489] get 73 (src: 0x1e)
[ 1333.739082] get 75 (src: 0x1e)
[ 1333.743852] get 77 (src: 0x1e)
[ 1333.748578] get 79 (src: 0x1e)
[ 1333.753297] get 81 (src: 0x1e)
[ 1333.757881] get 83 (src: 0x1e)
[ 1333.762466] get 85 (src: 0x1e)
[ 1333.767067] get 87 (src: 0x1e)
[ 1333.771842] get 89 (src: 0x1e)
[ 1333.776552] get 91 (src: 0x1e)
[ 1333.781259] get 93 (src: 0x1e)
[ 1333.785842] get 95 (src: 0x1e)
[ 1333.790427] get 97 (src: 0x1e)
[ 1333.795023] get 99 (src: 0x1e)
[ 1333.799816] get 101 (src: 0x1e)
[ 1333.803089] imx_rpmsg_pingpong virtio0.rpmsg-openamp-demo-channel.-1.30: goodbye!
[ 1333.913420] imx-rproc imx8mm-cm4: imx_rproc_kick: failed (0, err:-62)

On the M4 console, we see the other end of the communication:

RPMSG Ping-Pong FreeRTOS RTOS API Demo...
RPMSG Share Base Addr is 0xb8000000
Link is up!
Nameservice announce sent.
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...

Integrating the M4 boot

So our manual testing showed that the communication works if we load and start the M4 binary before booting into Linux. To integrate this into the regular boot flow, we will modify the bsp_bootcmd script in U-Boot itself. Note that we insert the run load_and_start_m4 command just before loadimage is executed. In our setup, this will load the device tree and Linux kernel from mmc.

This time we explicitly specify the file name of the binary. This way, we will load the binaries distributed through the Yocto build instead of our own version.

u-boot=> prin bsp_bootcmd
bsp_bootcmd=echo Running BSP bootcmd ...; mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; fi;
u-boot=> setenv bsp_bootcmd 'Running BSP bootcmd ...; mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run load_and_start_m4 loadimage; then run mmcboot; else run netboot; fi; fi; fi;'
u-boot=> prin m4_application 
m4_application=m4_application.bin
u-boot=> setenv m4_application rpmsg_lite_pingpong_rtos_linux_remote.bin
u-boot=> saveenv
Saving Environment to MMC... Writing to MMC(1)... OK
u-boot=> 

Once the environment has been saved, the board will always load and start the M4 processor before starting Linux. In Linux you will just need to modprobe imx_rpmsg_pingpong to execute the sample ping pong.

More details

There is one more demo provided in the Yocto images. In a future blog post, we will also look at that one and look more closely into the required details like address maps and device tree properties.

Update 2022-08-10

Change the load_and_start_m4 U-Boot script to check the variable m4_application. If it is not set, do not do anything. In this way, we can enable or disable the loading simply by setting or removing the latter variable.

Comments

Comments powered by Disqus