Skip to content

DRAFT: transport: rpmsg (OpenAMP) transport for MPU+MCU systems - #304

Draft
beriberikix wants to merge 11 commits into
transport/serialfrom
transport/rpmsg
Draft

DRAFT: transport: rpmsg (OpenAMP) transport for MPU+MCU systems#304
beriberikix wants to merge 11 commits into
transport/serialfrom
transport/rpmsg

Conversation

@beriberikix

@beriberikix beriberikix commented Jul 23, 2026

Copy link
Copy Markdown

Warning

Draft / do not merge. Stacked on #232 (serial transport core) — base is transport/serial, not main. Rebase onto main once #232 lands. Tracks the RFC in #274.

What this is

An rpmsg (OpenAMP) transport for Pouch on heterogeneous MPU+MCU SoCs: the MCU runs Zephyr + the Pouch device stack and syncs to a Linux gateway over on-die OpenAMP/rpmsg, keeping its own cloud identity and end-to-end encryption while the Linux side forwards opaque pouches.

Per #274, this is an adapter on the serial transport core (#232), not a new transport — each rpmsg message carries one Pouch Serial frame, reusing the serial channel model, no SAR. This PR does not modify the serial core.

Status

  • rpmsg device adapter (port/zephyr/transport/serial/rpmsg_device.c): ipc_service endpoint ↔ serial device core + POUCH_RPMSG_DEVICE Kconfig.
  • UART device adapter (port/zephyr/transport/serial/uart_device.c): interrupt-driven native-tty adapter for native_sim, sharing the framing module.
  • Framing module (uart_framing.[ch]): pure, testable length-delimited framing (encoder + streaming decoder).
  • SAEAD example (examples/zephyr/rpmsg_device): builds in nsim CI; compiles the UART adapter + serial device path with SAEAD.
  • Tests (tests/pouch/rpmsg/exchange, native_sim, passing in twister CI): framing round-trip / resync / rejection, plus an end-to-end broker↔device serial exchange routed through the framing.
  • Renode Tier-2 (mechanism validated) — see doc/renode-tier2/. Real Linux remoteproc + virtio-rpmsg in Renode brings up the RFC's exact interface (/dev/rpmsg_ctrl0), loads/starts the Zephyr R5 firmware, and the full R5 firmware bring-up is worked out and documented (TCM link; zephyr,ipc → child mailbox; SoC MPU region for the DDR shared memory, incl. a patch). The final fault-free R5 channel announcement is blocked by an emulator-model IPI stall after remoteproc start, so the end-to-end Pouch-over-rpmsg round-trip is deferred to real MPU+MCU hardware (details + remaining steps in the doc).

Notes for reviewers

  • The full two-process device+broker integration was validated in-process (exchange-through-framing ztest) because on the Add serial transport #232 base the gateway is hardwired to the Golioth client (the pluggable cloud-transport mock only exists on main).
  • The west-ncs twister job fails because this branch's base is behind main; resolves on rebase after Add serial transport #232. The nsim example job is red only from pre-existing backend-dependent coap/http pytests; sample.pouch.rpmsg_device builds clean.
  • Renode Tier-2 is local-only (no OpenAMP/ZynqMP in pouch CI).

🤖 Generated with Claude Code

beriberikix and others added 11 commits July 23, 2026 10:40
Add an rpmsg (OpenAMP) physical adapter for the Pouch Serial device
transport. rpmsg is reliable, ordered and message-oriented, so each rpmsg
message carries exactly one Pouch Serial frame (1-byte header + payload)
with no extra framing or segmentation; the adapter feeds received messages
straight into the serial device core and pulls frames to send when the core
signals data is available.

The adapter is a device-side driver only (the broker runs on the Linux
application processor). It initializes from the ipc_service instance
referenced by the "golioth,pouch-rpmsg-ipc" chosen node at APPLICATION init
priority.

This establishes the port/zephyr/transport/serial adapter directory that
future bus adapters can share.

Part of the rpmsg transport work for #274. Depends on the
serial transport core (#232); do not merge until that lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an interrupt-driven UART device adapter on the serial transport core for
development and native_sim end-to-end testing. rpmsg is not available on
native_sim, so the same serial device core is exercised over a host pty (the
native-tty UART driver) with the broker running as a host process. A minimal
length-delimited framing (SOF + 2-byte length) recovers frame boundaries on
the byte stream; the serial frame's own 1-byte header carries the channel.

Also add the examples/zephyr/rpmsg_device sample: a Pouch device that reports
telemetry and handles a setting over the serial transport. It builds for
native_sim (build_only) so CI compiles the serial-core + adapter path. The
same device code runs over the rpmsg adapter on real MPU+MCU hardware.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The serial device wired the server-cert and device-cert channels
unconditionally, but those endpoints are only compiled when SAEAD encryption
is enabled, so a POUCH_ENCRYPTION_NONE build failed to link with undefined
references to pouch_device_endpoint_server_cert/device_cert. Guard the two
cert channels on CONFIG_POUCH_ENCRYPTION_SAEAD (mirroring the BLE GATT
transport, which only exposes its cert characteristics under SAEAD), and
defensively ignore frames that arrive for a channel with no endpoint.

This lets the serial transport build without encryption, e.g. for the
native_sim rpmsg_device example.

Candidate to fold into the serial transport core (#232).

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the UART adapter's length-delimited framing (SOF + 2-byte length) into
a pure, dependency-free module (uart_framing.[ch]) with an encoder and a
byte-at-a-time streaming decoder, and use it from uart_device.c. This makes the
framing unit-testable and lets a host-side broker mirror the exact wire format.

Add tests/pouch/rpmsg/exchange (native_sim ztest):
- direct framing tests: encode/decode round-trip, resync after garbage,
  rejection of oversized and malformed frames;
- an end-to-end test that runs a full broker <-> device serial exchange (reusing
  the serial exchange stub endpoints, so no gateway/cloud is needed) with every
  frame passed through encode + streaming decode, asserting all channel payloads
  survive the framing round-trip.

Unlike the coap/http example pytests, this runs in the twister CI without any
backend.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ample

The earlier cert-channel SAEAD guard regressed the serial transport unit tests
(pouch.serial.exchange, pouch.serial.device): those tests build the serial
device core without CONFIG_POUCH_ENCRYPTION_SAEAD but still exercise the
certificate channels through stub endpoints, so guarding the channels on SAEAD
removed endpoints the tests rely on.

Revert src/transport/serial/{device,channel}.c to the #232 base so this PR no
longer modifies the serial core, and drop the native_sim rpmsg_device example
that depended on POUCH_ENCRYPTION_NONE. The serial device requires SAEAD in a
real build (its certificate endpoints are only compiled under SAEAD); a proper
SAEAD-based example (which also compiles the UART/rpmsg adapters) is a
follow-up.

The rpmsg framing + end-to-end exchange test (tests/pouch/rpmsg/exchange) is
unaffected: it drives the real broker/device serial cores with the shared stub
endpoints, so it needs no encryption and exercises the framing round-trip.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a native_sim example that exercises the serial transport's UART device
adapter with SAEAD end-to-end encryption (Pouch's default), so CI compiles the
UART adapter and the serial device path. The device credentials are embedded
self-signed placeholders (P-256), keeping the example self-contained with no
provisioned filesystem; a real deployment provisions a CA-signed device
certificate (see coap_client/ble_gatt for the filesystem pattern).

The example reports telemetry over the serial transport; on native_sim the link
is a host pty (native-tty UART) and on a real MPU+MCU part the same device code
runs over the rpmsg adapter. Built build_only on native_sim in CI.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capture the work-in-progress harness for validating the Pouch rpmsg (OpenAMP)
transport against a real Linux remoteproc + virtio-rpmsg stack in Renode, with
no hardware. This complements the native_sim tests, which cannot exercise the
ipc_service/OpenAMP adapter.

Includes:
- README documenting the validated approach: platform (AMD Kria KV260 / ZynqMP
  Cortex-R5, Zephyr board kv260_r5), the OpenAMP DDR carve-out addresses taken
  from the demo's Linux DTB (vrings @0x3ed40000/0x3ed44000, buffers
  @0x3ed48000), the IPI mailbox wiring, and how to build/run.
- kv260_r5 openamp overlay + conf (zephyr,ipc_shm @0x3ed40000, zephyr,ipc =
  &rpu0_ipi) that lets zephyr/samples/subsys/ipc/openamp_rsc_table build for
  kv260_r5.
- A robot smoke test that boots a kv260_r5 Zephyr image on the emulated R5.

Validated so far: Renode's shipped ZynqMP OpenAMP echo test passes (real
remoteproc + virtio-rpmsg round-trip); a kv260_r5 hello_world boots on the
emulated R5; the openamp_rsc_table firmware builds for kv260_r5. Remaining:
load the firmware via Linux remoteproc, swap in the Pouch device + mock broker,
Ubuntu rootfs, and an end-to-end Robot test.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Zephyr OpenAMP firmware built for kv260_r5 now loads and starts on the R5
via real Linux remoteproc in the Renode ZynqMP demo (remoteproc state ->
running). Add the robot test that boots the demo Linux, injects the firmware
into a copy of the rootfs (debugfs, no root), and drives the remoteproc
load/start; document the debugfs injection flow.

Fix firmware placement: device-address 0x0 maps to the 64K R5 TCM, which the
~155K openamp image overflows (remoteproc "bad phdr da 0x0"). Relocate &sram0
to the DDR rproc carve-out at 0x3ed00000 (256K), which the Xilinx R5
remoteproc driver maps.

Remaining: confirm the rpmsg channel end to end, swap in the Pouch device +
adapter firmware, an Ubuntu rootfs, and a mock broker.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root-cause the R5-side bring-up: the ZynqMP R5 boots/executes from TCM (reset
at 0x0), not DDR. A DDR-relinked firmware loads via remoteproc (state=running)
but never runs. Switch the overlay back to the default TCM link address and
keep only the vrings/buffers in DDR (zephyr,ipc_shm @ 0x3ed40000); route the
R5 console to uart0 (Linux owns uart1). Trim the OpenAMP sample (no shell/log)
to ~45 KiB so it fits ATCM.

With that, the Linux side is fully up (virtio_rpmsg_bus online, /dev/rpmsg_ctrl0
+ rpmsg_ctrl/rpmsg_ns devices) and the R5 now executes the firmware (prints on
uart0). Remaining open blocker: the firmware faults in its IPM/IPI setup
(ipm_set_enabled -> prefetch abort) before completing the rpmsg handshake;
documented as the next step.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document and capture the R5-side bring-up progress. Three real, sequential
ZynqMP-R5-OpenAMP requirements the board lacked out of the box:

1. TCM-linked firmware (R5 boots from TCM, not DDR).
2. zephyr,ipc must point at the child mailbox (&rpu0_apu_mailbox); the
   ipm_xlnx_ipi driver leaves the parent rpu0_ipi device with a NULL api, so
   pointing at the parent faults in ipm_set_enabled.
3. An MPU region for the DDR OpenAMP carve-out: the ZynqMP SoC uses a static
   MPU table that doesn't map it, so the R5 data-aborts in virtqueue init.
   Captured as zynqmp-r5-openamp-mpu.patch.

With all three the R5 runs the OpenAMP firmware without faulting. Remaining
open blocker: after remoteproc start the emulation nearly stalls and no
R5-announced rpmsg channel is observed yet (likely an IPI notify /
interrupt-storm issue); documented for follow-up.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rdware

Frame the Renode Tier-2 outcome: the Linux remoteproc + virtio-rpmsg mechanism
(the RFC's /dev/rpmsg_ctrl0 interface) and the full Zephyr R5 firmware bring-up
(TCM link, child-mailbox IPM, MPU shared-memory region) are validated and
documented. The final fault-free R5 rpmsg channel announcement is blocked by an
emulator-model IPI interrupt-storm/stall after remoteproc start, so the
end-to-end Pouch-over-rpmsg round-trip is deferred to real MPU+MCU hardware.

Part of #274. Do not merge until #232 lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant