MCUXpresso SDK Documentation

MCUXpresso SDK : mcuxsdk-middleware-mcmgr (Multicore Manager)#

Overview#

This repository is for MCUXpresso SDK Multicore Manager middleware delivery and it contains Multicore Manager component officially provided in NXP MCUXpresso SDK. This repository is part of the MCUXpresso SDK overall delivery which is composed of several sub-repositories/projects. Navigate to the top/parent repository mcuxsdk for the complete delivery of MCUXpresso SDK to be able to build and run Multicore Manager examples that are based on mcux-sdk-middleware-mcmgr component.

Documentation#

Overall details can be reviewed here: MCUXpresso SDK Online Documentation

Visit Multicore Manager - Documentation to review details on the contents in this sub-repo.

For Further API documentation, please look at doxygen documentation

Setup#

Instructions on how to install the MCUXpresso SDK provided from GitHub via west manifest Getting Started with SDK - Detailed Installation Instructions

Contribution#

We welcome and encourage the community to submit patches directly to the mcmgr project placed on github. Contributing can be managed via pull-requests. Before a pull-request is created the code should be tested and properly formatted.


Multicore Manager (MCMGR)#

The Multicore Manager (MCMGR) software library provides a number of services for multicore systems. This library is distributed as a part of the Multicore SDK (MCSDK). Together, the MCSDK and the MCUXpresso SDK (SDK) form a framework for development of software for NXP multicore devices.

The MCMGR component is located in the <MCUXpressoSDK_install_dir>/middleware/multicore/mcmgr directory.

The Multicore Manager provides the following major functions:

  • Maintains information about all cores in system.

  • Secondary/auxiliary core(s) startup and shutdown.

  • Remote core monitoring and event handling.

Usage of the MCMGR software component#

The main use case of MCMGR is the secondary/auxiliary core start. This functionality is performed by the public API function.

Example of MCMGR usage to start secondary core:

#include "mcmgr.h"

void main()
{
    /* Initialize MCMGR, low level multicore management library */
    MCMGR_Init();

    /* Boot secondary core application from the CORE1_BOOT_ADDRESS, pass "1" as startup data, starting synchronously. */
    MCMGR_StartCore(kMCMGR_Core1, CORE1_BOOT_ADDRESS, 1, kMCMGR_Start_Synchronous);
.
.
.
    /* Stop secondary core execution. */
    MCMGR_StopCore(kMCMGR_Core1);
}

Some platforms allow stopping and re-starting the secondary core application again, using the MCMGR_StopCore / MCMGR_StartCore API calls. It is necessary to ensure the initially loaded image is not corrupted before re-starting, especially if it deals with the RAM target. Cache coherence has to be considered/ensured as well.

It could also happen that the secondary core application stops running correctly and the primary core application does not know about that situation. Therefore, it is beneficial to implement a mechanism for core health monitoring. The test_heartbeat unit test can serve as an example how to ensure that: secondary core could periodically send heartbeat signals to the primary core using MCMGR_TriggerEvent() API to indicate that it is alive and functioning properly.

Another important MCMGR feature is the ability for remote core monitoring and handling of events such as reset, exception, and application events. Application-specific callback functions for events are registered by the MCMGR_RegisterEvent() API. Triggering these events is done using the MCMGR_TriggerEvent() API. mcmgr_event_type_t enums all possible event types.

An example of MCMGR usage for remote core monitoring and event handling. Code for the primary side:

#include "mcmgr.h"

#define APP_RPMSG_READY_EVENT_DATA  (1)
#define APP_NUMBER_OF_CORES (2)
#define APP_SECONDARY_CORE kMCMGR_Core1

/* Callback function registered via the MCMGR_RegisterEvent() and triggered by MCMGR_TriggerEvent() called on the secondary core side */
void RPMsgRemoteReadyEventHandler(mcmgr_core_t coreNum, uint16_t eventData, void *context)
{
    uint16_t *data = &((uint16_t *)context)[coreNum];

    *data = eventData;
}

void main()
{
    uint16_t RPMsgRemoteReadyEventData[NUMBER_OF_CORES] = {0};

    /* Initialize MCMGR, low level multicore management library */
    MCMGR_Init();

    /* Register the application event before starting the secondary core */
    MCMGR_RegisterEvent(kMCMGR_RemoteApplicationEvent, RPMsgRemoteReadyEventHandler, (void *)RPMsgRemoteReadyEventData);

    /* Boot secondary core application from the CORE1_BOOT_ADDRESS, pass rpmsg_lite_base address as startup data, starting synchronously. */
    MCMGR_StartCore(APP_SECONDARY_CORE, CORE1_BOOT_ADDRESS, (uint32_t)rpmsg_lite_base, kMCMGR_Start_Synchronous);

    /* Wait until the secondary core application signals the rpmsg remote has been initialized and is ready to communicate. */
    while(APP_RPMSG_READY_EVENT_DATA != RPMsgRemoteReadyEventData[APP_SECONDARY_CORE]) {};
.
.
.
}

Code for the secondary side:

#include "mcmgr.h"

#define APP_RPMSG_READY_EVENT_DATA  (1)

void main()
{
    /* Initialize MCMGR, low level multicore management library */
    MCMGR_Init();
.
.
.

    /* Signal the to other core that we are ready by triggering the event and passing the APP_RPMSG_READY_EVENT_DATA */
    MCMGR_TriggerEvent(kMCMGR_Core0, kMCMGR_RemoteApplicationEvent, APP_RPMSG_READY_EVENT_DATA);
.
.
.
}

Sending events to the remote core#

MCMGR events are sent with one of two functions:

  • MCMGR_TriggerEvent() waits until the remote core has read the previously sent data, then sends the event. Use it from task or thread context.

  • MCMGR_TriggerEventForce() never waits, so it can be called from interrupt context. RPMsg-Lite uses it for its notifications (doorbells) when RL_USE_MCMGR_IPC_ISR_HANDLER is enabled.

On MU-based platforms (i.MX RT1160, RT1170, RT1180, RT700, K32L3A6, KW43B43, MCXW70, MCXL20, MCXE32B), all events share one MU register. MCMGR guarantees that an event is never overwritten before the remote core has read it:

  • A forced event that finds the MU register occupied is queued and sent from the MU TX empty interrupt. Forced events are sent in the order of the calls.

  • An RPMsg-Lite doorbell (kMCMGR_RemoteRPMsgEvent) identical to one already waiting is merged into it, because one doorbell makes the remote core process the whole virtqueue.

  • Any other forced event is never merged. If MCMGR_MU_FORCED_QUEUE_SIZE events are already waiting, MCMGR_TriggerEventForce() returns kStatus_MCMGR_Error and does not send the event.

  • MCMGR_TriggerEvent() is sent after the forced events queued before the call, so it cannot overtake them.

  • Waiting events are dropped by MCMGR_Init() and when the remote core is reset.

A successful return means the event was sent or queued without overwriting anything. It does not confirm that the remote core received the event. Use an application-level acknowledgment when delivery must be confirmed.

Configuration#

The options below are set in Kconfig, for example in the application prj.conf. Each one can also be set with a compiler definition of the same name, for example -DMCMGR_MU_FORCED_QUEUE_SIZE=4, which takes precedence over Kconfig. Projects without Kconfig use the default from the platform mcmgr_platform.h.

Option

Default

Description

MCMGR_BUSY_POLL_COUNT

0

Maximum number of iterations of MCMGR waiting loops before they return an error. 0 waits indefinitely.

MCMGR_REMOTE_APP_EVENT_COUNT

1

Number of application events, kMCMGR_RemoteApplicationEvent to kMCMGR_RemoteApplicationEvent4 (1 to 5).

MCMGR_MU_FORCED_QUEUE_SIZE

2; 3 on KW43B43 and MCXW70

MU-based platforms only. Number of forced events, RPMsg-Lite doorbells excluded, that can wait per MU (0 to 32). KW43B43 and MCXW70 reserve one entry for the wireless framework NBU fault trigger.

MCMGR_MU_RPMSG_DOORBELL_SLOTS

2; 8 on i.MX RT700

MU-based platforms only. Number of distinct RPMsg-Lite doorbells that can wait per MU (1 to 64): 2 per RPMsg-Lite link (one per vring) times the links using the MU. i.MX RT700 allows up to 4 links per core pair.

Example prj.conf for an application that sends up to four forced events in a row:

CONFIG_MCMGR_MU_FORCED_QUEUE_SIZE=4

Each queue entry costs 4 bytes per MU instance.

MCMGR Data Exchange Diagram#

The following picture shows how the handshakes are supposed to work between the two cores in the MCMGR software.