RPMsg Port UART
Overview
RPMsg Port UART is a transport layer implementation that enables RPMsg communication between two SoCs connected via UART. This is useful when shared memory is not available between chips, such as two separate chips on the same PCB.
Hardware Requirements
The UART interface requires four signals with hardware flow control support:
┌─────────────┐ ┌─────────────┐
│ SoC A │ │ SoC B │
│ │ TX ────────> │ │
│ UART │ RX <──────── │ UART │
│ │ RTS ────────> │ │
│ │ CTS <──────── │ │
└─────────────┘ └─────────────┘
Requirements:
Hardware flow control (RTS/CTS) must be supported to prevent data loss
RX pin should support wake-up for low power mode
Data corruption during wake-up is not allowed, but data loss is acceptable (handled by software)
Software Architecture
┌────────────────────────────────────────┐
│ RPMsg Framework │
├────────────────────────────────────────┤
│ RPMsg Port │
│ (Buffer Management, Name Service) │
├────────────────────────────────────────┤
│ RPMsg Port UART │
│ (Escape Coding, Connection Protocol, │
│ Data Transfer, Low Power Support) │
├────────────────────────────────────────┤
│ UART Driver │
└────────────────────────────────────────┘
Protocol Details
Escape Coding
Special characters (0x70-0x7f) are used to distinguish commands from data:
#define RPMSG_PORT_UART_START 0x7f /* Frame start */
#define RPMSG_PORT_UART_END 0x70 /* Frame end */
#define RPMSG_PORT_UART_ESCAPE 0x7c /* Escape character */
#define RPMSG_PORT_UART_CONNREQ 0x7e /* Connection request */
#define RPMSG_PORT_UART_CONNACK 0x7d /* Connection acknowledge */
#define RPMSG_PORT_UART_ESCAPE_MASK 0x20 /* Escape XOR mask */
Encoding example:
Original: [0x01, 0x7f, 0x02, 0x70, 0x03]
Encoded: [START] [0x01] [ESC] [0x5f] [0x02] [ESC] [0x50] [0x03] [END]
[0x7f] [0x01] [0x7c][0x5f] [0x02] [0x7c][0x50] [0x03] [0x70]
When a byte falls in the 0x70-0x7f range, it is escaped by sending the ESCAPE character followed by the original byte XORed with 0x20.
Connection Protocol
The connection protocol supports:
Either side can start first during system boot
Reconnection after either side restarts
Normal startup:
┌────────┐ ┌────────┐
│ CPU A │ │ CPU B │
└────────┘ └────────┘
│ │
│ ──── CONNREQ (0x7e) ────────> │
│ │ Initialize resources
│ <──── CONNACK (0x7d) ──────── │
│ Initialize resources │
│ │
│ Connection established │
Restart scenario (CPU B restarts):
┌────────┐ ┌────────┐
│ CPU A │ │ CPU B │
│(running)│ │(restart)│
└────────┘ └────────┘
│ │
│ <──── CONNREQ (0x7e) ──────── │
│ Reinitialize resources │
│ ──── CONNACK (0x7d) ────────> │
│ │ Initialize resources
│ Connection established │
Data Frame Format
┌───────┬─────────────────────────────────┬───────┐
│ START │ Header + Payload (escaped) │ END │
│ 0x7f │ │ 0x70 │
└───────┴─────────────────────────────────┴───────┘
Header structure (rpmsg_port_header_s):
┌────────┬────────┬────────┬────────┬──────────────┐
│ crc │ cmd │ avail │ len │ payload │
│ 2 byte │ 2 byte │ 2 byte │ 2 byte │ N bytes │
└────────┴────────┴────────┴────────┴──────────────┘
crc: CRC16 checksum (optional, enabled via CONFIG_RPMSG_PORT_UART_CRC)
cmd: Command field (unused for data frames, set to 0)
avail: Available space (unused for data frames, set to 0)
len: Total length including header
payload: Actual RPMsg data
Low Power Support
RPMsg Port UART implements low power support without additional GPIOs using special wake/sleep commands:
#define RPMSG_PORT_UART_STAYWAKE1 0x79 /* Wake request 1 */
#define RPMSG_PORT_UART_STAYWAKEACK1 0x78 /* Wake acknowledge 1 */
#define RPMSG_PORT_UART_STAYWAKE2 0x75 /* Wake request 2 */
#define RPMSG_PORT_UART_STAYWAKEACK2 0x74 /* Wake acknowledge 2 */
#define RPMSG_PORT_UART_RELAXWAKE 0x77 /* Allow sleep */
Wake/Sleep Flow
┌────────┐ ┌────────┐
│ CPU A │ │ CPU B │
│(sender)│ │(asleep)│
└────────┘ └────────┘
│ │
│ Acquire TX PM lock │
│ ──── STAYWAKE1 ──────────────────> │
│ │ Acquire RX PM lock
│ <──── STAYWAKEACK1 ─────────────── │
│ │
│ ════ Data Transfer ══════════════> │
│ │
│ ──── RELAXWAKE ──────────────────> │
│ Release TX PM lock │ Release RX PM lock
│ │
Ping-Pong Wake Mechanism
Two sets of wake commands (STAYWAKE1/ACK1 and STAYWAKE2/ACK2) are used alternately to prevent false wake acknowledgments:
First transfer: STAYWAKE1 → STAYWAKEACK1 → Data → RELAXWAKE
Second transfer: STAYWAKE2 → STAYWAKEACK2 → Data → RELAXWAKE
Third transfer: STAYWAKE1 → STAYWAKEACK1 → Data → RELAXWAKE
...
This prevents the sender from mistakenly accepting a delayed ACK from a previous transfer as confirmation for the current transfer.
Source Files
nuttx/drivers/rpmsg/rpmsg_port_uart.c