How Linux talks to a real-time core: remoteproc and RPMsg

· INTECH

A lot of Embedded (Real-Time) Linux products now run on a single SoC that carries two very different brains: a Cortex-A core running Linux for connectivity, UI and housekeeping, and a small Cortex-M or Cortex-R core handling the hard real-time work — motor control, sampling, deterministic I/O. TI’s Sitara AM62x, with its Cortex-M4F alongside the A53s, is a typical example.

The interesting question is how those two halves talk to each other without the non-deterministic Linux side ruining the real-time side’s timing. In mainline Linux, the answer is two cooperating frameworks: remoteproc and RPMsg.

remoteproc: lifecycle of the real-time core

remoteproc (remote processor) is the framework that lets Linux manage the life of the other core: load its firmware, start it, stop it. The remote firmware is typically an ELF image, and Linux exposes control through sysfs — you point it at a firmware file and write start or stop to the core’s state.

Crucially, the firmware image carries a resource table: a small structure that tells the host what the remote core needs — memory regions to reserve, the vrings for messaging, a trace buffer for its logs. The host reads this table and sets up shared resources accordingly, so the two sides agree on the memory map before any message is exchanged.

RPMsg: the message channel

Once the remote core is running, you need a way to exchange data. That’s RPMsg (Remote Processor Messaging). It’s a message-passing layer built on virtio, using shared-memory ring buffers (vrings) for the data and a mailbox — a hardware block — for the “kick” that tells the other side a message is waiting.

So the mechanics of one message are:

  1. Sender writes the message into a buffer in shared memory (a vring slot).
  2. Sender rings the mailbox, raising an interrupt on the other core.
  3. Receiver wakes on the interrupt and consumes the buffer.

On the Linux side, RPMsg channels surface as devices. A driver such as rpmsg_char exposes endpoints (for example under /dev/rpmsg*) so user-space applications can send and receive without writing kernel code.

Where the latency really is

This is the part teams get wrong. RPMsg is not a zero-latency link. Each message costs a shared-memory write plus a mailbox interrupt and the receiver’s scheduling delay. On the Linux side especially, that delay is variable — it’s a general-purpose OS, not an RTOS.

The design rule that follows:

  • Keep the hard real-time loop entirely on the real-time core. It should not depend on Linux responding within a deadline. Ever.
  • Use RPMsg for what it’s good at: configuration, commands, status, aggregated results — traffic where a few tens of microseconds of jitter don’t matter.
  • For high-rate streaming, consider a shared-memory ring the real-time core fills autonomously and Linux drains lazily, rather than one RPMsg message per sample.

Get that split right and you get the best of both: Linux’s ecosystem on one core, hard determinism on the other, with a clean, well-understood channel between them.

Why it’s worth doing properly

The frameworks are mainline and well-trodden, but the failure modes are subtle — memory-map mismatches in the resource table, mailbox configuration, cache coherency on shared buffers, and firmware that assumes timing Linux can’t guarantee. These are exactly the problems that look like “it works on the bench but glitches in the field.”


Building or debugging a heterogeneous multicore system? Embedded (Real-Time) Linux work is what we do — including the real-time and inter-core parts that are easy to get subtly wrong.