Create a multi-core app
The Dev Board Micro has a dual-core MCU with a Cortex M7 core and a Cortex M4 core, enabling a variety of multi-core applications. When the board boots up, the bootloader starts the M7 executable only. So if you want to use the M4 core, you need to start it from your M7 program. This page shows you how to do that and introduces some of the inter-process communication (IPC) features available on the board.
Define an M4 program with CMake
First thing you need to do is tell CMake which program you want to run on the
M4. You can do that with the following updates to your CMakeLists.txt
:
- Declare the M4 executable with
add_executable_m4()
. (This works exactly the same asadd_executable_m7()
because they're both wrappers foradd_executable()
.) - Link the M4 executable's libraries with
target_link_libraries()
. - Add the M4 executable target to the M7 executable by declaring
it with the
M4_EXECUTABLE
property. (The M4 executable will be bundled into the M7 binary.)
For example, the following CMakeLists.txt
declares
an M7 executable named multi_core_hello
, which includes the M4 executable
named multi_core_hello_m4
:
add_executable_m7(multi_core_hello
hello_world_cm7.cc
M4_EXECUTABLE
multi_core_hello_m4
)
target_link_libraries(multi_core_hello
libs_base-m7_freertos
)
add_executable_m4(multi_core_hello_m4
hello_world_cm4.cc
)
target_link_libraries(multi_core_hello_m4
libs_base-m4_freertos
)
Notice that the linked libraries for each executable are unique, because not all libraries are the same for the M7 and M4.
Execute the M4 program
Just like an M7 program, an M4 program must include an app_main()
function as
its entry point. So your code for the M4 might appear identical to code that
runs on the M7. However, only the M7 program's app_main()
starts automatically
upon boot.
When the board boots, the bootloader loads the M4 program into memory but it
does not run until you start it from your M7 program by calling
IpcM7::StartM4()
.
For example, the following M7 program (hello_world_cm7.cc
from the above
CMake file) starts the M4 program (hello_world_cm4.cc
)
and then suspends itself:
#include "libs/base/ipc_m7.h"
#include "libs/base/mutex.h"
extern "C" void app_main(void* param) {
(void)param;
coralmicro::IpcM7::GetSingleton()->StartM4();
CHECK(coralmicro::IpcM7::GetSingleton()->M4IsAlive(500));
vTaskSuspend(nullptr);
}
app_main()
, there's only one M4 executable, so you don't need
to specify its name. Just start the M4 and the system invokes
app_main()
in your M4 executable.Caveats
Although most code works the same on each core, there are some important exceptions:
-
M4 can't use the Edge TPU: Only the M7 can execute inference on the Edge TPU. This is because the M4 currently cannot access the USB stack. (You can still run TFLite Micro models on the M4.)
-
M4 must initialize resources: Many system resources are initialized by default on the M7, but not on the M4. For example, the camera is already initialized for M7 programs, but an M4 program must initialize the camera before you can use it. For example:
// Only M4 programs need to call camera's Init() CameraTask::GetSingleton()->Init(I2C5Handle()); CameraTask::GetSingleton()->SetPower(false);
Other resources that must be initialized for the M4 include timers, random number generators, and temperature sensors.
Also beware that there is no internal arbitration if both cores attempt to
access a shared resource such as the camera. If each core wants to
use the camera (or other shared resource), you must use a
MulticoreMutexLock
to avoid both cores
calling upon it at the same time, which can have undefined results.
Communicate between cores
There are essentially two ways to communicate between the M7 and M4: with a mutex lock to avoid simultaneous access to a shared resource, and with inter-process communication (IPC) to send custom app messages.
To protect a shared resource with a mutex lock that works across cores, simply
create a MulticoreMutexLock
on each
core, using the same gate number. This lock should be initialized right before
each program attempts to use a shared resource, so that code will be blocked in
the event that the other core still holds the lock. The lock is released by each
core once the code leaves the scope of the
MulticoreMutexLock
object.
To pass messages between cores, you just need to create a message with
IpcMessage
and, depending whether your sender is on the M7 or M4, send it
to the other process with either IpcM7::SendMessage()
or
IpcM4::SendMessage()
, respectively. For details, see the
IPC API reference.
Multi-core examples
Sometimes, the best way to learn is to read some working code. So check out these examples that use both MCU cores:
- multi_core_blink_led: Starts an M4 app to blink an LED and suspends the M7 app.
- multi_core_hello: Starts an M4 app and then continuously prints "Hello world" from each core (using a mutex lock to ensure alternating messages).
- multi_core_ipc: Performs IPC between cores, with each core sending and receiving messages
Is this content helpful?