Skip to main content

<alp/mproc.h> — Inter-Core IPC primitives

Mailbox + shared-memory + hardware-semaphore primitives for the heterogeneous multi-core SoCs in the E1M catalogue. This is the low-level layer — use <alp/rpc.h> for framed cross-core RPC; reach for mproc only when you need raw mailbox / shared-memory / hwsem semantics (e.g. compose your own protocol).

Where it matters

SoC familyCoresalp_core_id_t peers
Alif EnsembleCortex-M55 HP @ 400 MHz + Cortex-M55 HE @ 160 MHz (+ Cortex-A32)ALP_CORE_M55_HP, ALP_CORE_M55_HE, ALP_CORE_A32_CLUSTER
Renesas RZ/V2NQuad Cortex-A55 @ 1.8 GHz + Cortex-M33-SMALP_CORE_A55_CLUSTER, ALP_CORE_M33_SM
NXP i.MX 9352Dual Cortex-A55 @ 1.7 GHz + Cortex-M33ALP_CORE_A55_CLUSTER, ALP_CORE_M33

The alp_core_id_t enum was generalised in v0.6 to carry concrete entries for every shipped SoM topology (AEN's HP/HE distinction is preserved; V2N adds A55_CLUSTER + M33_SM; NX9 adds M33).

#include <alp/mproc.h>

Peer-core lifecycle

[ABI-EXPERIMENTAL] · new in v0.9

On heterogeneous SoMs the firmware for a secondary core is often loaded at boot (by the boot ROM / secure firmware, from the boot package) but not released — the master core decides at runtime when the peer starts. alp_mproc_boot_core() asks the platform's boot authority (typically the SoC's secure / system-controller firmware) to start a peer core executing at a given entry address.

// entry_addr is the peer's entry point in the GLOBAL address map —
// the same load address the boot package declares for that core's image.
alp_status_t s = alp_mproc_boot_core(ALP_CORE_M55_HE, 0x60000000u);
if (s == ALP_OK) {
// Boot authority ACCEPTED the request. Confirming the peer actually
// runs is your job — heartbeat/beacon, or open a channel via <alp/rpc.h>.
}

entry_addr is in the global address map (e.g. the ITCM global alias the image was packaged for) and comes from the application's boot-package layout; the API performs no address validation beyond what the boot authority enforces. The call is bounded — backends riding a controller mailbox time-limit the round-trip, so it returns rather than hangs when the controller is unreachable.

CodeMeaning
ALP_OKBoot authority accepted the request (not a guarantee the peer runs).
ALP_ERR_INVALcore is ALP_CORE_SELF.
ALP_ERR_NOSUPPORTNo boot authority for core on this build (wrong SoM, native_sim, or a core the platform boots by other means).
ALP_ERR_NOT_READYBoot authority asleep/unreachable (retryable).
ALP_ERR_IOTransport fault or a rejected request.

Shared memory

The region pool is built at compile time from the DT aliases alp-shmem0 .. alp-shmemN; cfg->name must match an alias-derived label (e.g. "alp_shmem0"). On AEN dual-core builds the orchestrator emits the per-core overlay with matching aliases when board.yaml's ipc: block is declared; until that ships, customers can hand-author the alias in a board overlay.

const alp_shmem_config_t cfg = {
.name = "alp_shmem0", // must match an alp-shmemN DT alias
.size = 512, // bytes, rounded up to MMU/MPU page
.cacheable = false, // false → backend handles cache maintenance
};
alp_shmem_t *shm = alp_shmem_open(&cfg);
if (shm == NULL) {
// alp_last_error() reports why — ALP_ERR_NOT_READY on lookup miss
return -1;
}

void *base = NULL;
size_t size = 0;
alp_shmem_view(shm, &base, &size); // raw pointer + size; both cores see same bytes
memcpy(base, payload, payload_len);

alp_shmem_close(shm);

Mailbox (small messages, peer-signalled)

const alp_mbox_config_t cfg = {
.channel = 0, // 0..N per the SoC's mailbox controller
.peer = ALP_CORE_M55_HE, // counterpart core
};
alp_mbox_t *mbox = alp_mbox_open(&cfg);

// Send: max ~16-64 B per channel (MHU MTU); timeout in ms
uint8_t payload[12] = { /* ... */ };
alp_mbox_send(mbox, payload, sizeof(payload), 100 /* ms */);

// Receive: callback-driven, runs on the SDK's mbox thread (Zephyr)
static void on_mbox(uint32_t channel, const void *data, size_t len, void *user) {
// copy what you need — `data` is owned by the SDK
}
alp_mbox_set_callback(mbox, on_mbox, NULL);

The typical mproc flow stages a payload in shmem, signals the peer with an (offset, length) tuple over the mailbox, and lets the peer read the bytes via its own shmem view — see mproc-mailbox.

Hardware semaphore

alp_hwsem_t *sem = alp_hwsem_open(0); // SoC hwsem index 0 (must agree with peer)
if (sem == NULL) {
// ALP_ERR_OUT_OF_RANGE if id >= CONFIG_ALP_SDK_MPROC_HWSEM_COUNT (default 16)
return -1;
}

alp_hwsem_lock(sem, UINT32_MAX); // UINT32_MAX = wait indefinitely (K_FOREVER);
// 0 = non-blocking; otherwise milliseconds
// ... critical section over shared memory ...
alp_hwsem_unlock(sem); // ALP_ERR_INVAL if you didn't hold the lock

alp_hwsem_close(sem); // defensively releases if still held

alp_hwsem_try_lock(sem) is the non-blocking variant — returns ALP_ERR_BUSY if held by another core.

Implementation status

SurfaceZephyr backendYocto backendGD32 backend
alp_mbox_*✅ Functional since v0.4 (DT-anchored MBOX via alp-mboxN aliases; optional nanopb framing)Shim onlyNOSUPPORT
alp_shmem_*✅ Functional since v0.7 via DT aliases alp-shmem0..N (pre-HiL; intra-core fallback proven on native_sim)NOSUPPORTNOSUPPORT
alp_hwsem_*✅ Functional since v0.7 with k_sem-backed intra-core fallback (cap CONFIG_ALP_SDK_MPROC_HWSEM_COUNT, default 16); real per-SoC HWSEM blocks (AEN HWSEM, ST HSEM) wired up under HiL bring-upNOSUPPORTNOSUPPORT

The v0.7 Zephyr shmem + hwsem impl serialises within a single Zephyr image (intra-core); cross-core HWSEM-block wiring is the next step under the silicon-verification track. See the V2N+M1 bring-up for the V2N-side multi-proc story.

See also

Questions about this page? Discuss in Community Forum