Establishing IPC Communication Through RPMsg - Establishing IPC Communication Through RPMsg - 2026.1 English - UG1186

Libmetal and OpenAMP User Guide (UG1186)

Document ID
UG1186
Release Date
2026-06-24
Version
2026.1 English

Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                    Linux (Cortex-A78)                           │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │ rpmsg_client │  │  rpmsg_tty   │  │  rpmsg_char  │          │
│  │   sample     │  │   driver     │  │   driver     │          │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘          │
│         │                 │                  │                  │
│  ┌──────▼─────────────────▼──────────────────▼───────┐          │
│  │         RPMsg Bus (virtio_rpmsg_bus)              │          │
│  └──────────────────────┬────────────────────────────┘          │
│  ┌──────────────────────▼────────────────────────────┐          │
│  │         VirtIO (rpmsg_virtio)                     │          │
│  └──────────────────────┬────────────────────────────┘          │
│  ┌──────────────────────▼────────────────────────────┐          │
│  │      RemoteProc (xlnx_r5_remoteproc)              │          │
│  └──────────────────────┬────────────────────────────┘          │
│                         │                                        │
│  ┌──────────────────────▼────────────────────────────┐          │
│  │    IPI Mailbox (zynqmp_ipi_mbox)                  │          │
│  └──────────────────────┬────────────────────────────┘          │
└─────────────────────────┼──────────────────────────────────────┘
                          │
                   [IPI Hardware]
                          │
┌─────────────────────────▼──────────────────────────────────────┐
│                  Zephyr (Cortex-R52)                            │
│  ┌──────────────────────┴────────────────────────────┐          │
│  │         IPM Driver (Zephyr IPC)                   │          │
│  └──────────────────────┬────────────────────────────┘          │
│  ┌──────────────────────▼────────────────────────────┐          │
│  │         OpenAMP (libmetal + libopen_amp)          │          │
│  │  ┌────────────────┐  ┌────────────────────────┐   │          │
│  │  │ rpmsg_virtio   │  │  virtio device         │   │          │
│  │  └────────┬───────┘  └───────┬────────────────┘   │          │
│  └───────────┼──────────────────┼────────────────────┘          │
│  ┌───────────▼──────────────────▼────────────────────┐          │
│  │   RPMsg Endpoints (client, tty, raw)              │          │
│  └───────────────────────────────────────────────────┘          │
│                  Application Logic                              │
└─────────────────────────────────────────────────────────────────┘

         Shared Memory (DDR 0x9860000-0x98a8000)
         ┌─────────────────────────────────────┐
         │ VRing0 │ VRing1 │ Buffer Pool       │
         └─────────────────────────────────────┘

Firmware Build Process

  1. Build the Zephyr RPU firmware with Yocto embedded development framework (EDF). For details, refer to Yocto Project Build.
    1. Go to the Yocto build directory.
    2. Source the build environment.
    3. Build the firmware.
    cd <yocto-workspace>
    source setupsdk
    bitbake openamp-zephyr-demo
    Note: If you want to build only the firmware ELF file, use this command: bitbake zephyr-openamp-rpmsg-multi-services.
  2. Build target differences:
    • openamp-zephyr-demo builds the Zephyr firmware and installs it in the Linux root file system at /lib/firmware/.
    • zephyr-openamp-rpmsg-multi-services builds only the firmware ELF binary.
  3. Key build artifacts:
    • rpmsg_multi_services.elf: Zephyr ELF binary with resource table
    • Symbols and debug info for GDB debugging
    • When using openamp-zephyr-demo, firmware is automatically deployed to rootfs

Firmware Loading and RemoteProc Startup

For details, refer toRunning the Demo.

  1. Copy the firmware to the target. Skip this task if you used bitbake openamp-zephyr-demo. That target installs the firmware in the root file system automatically.
    1. Copy the firmware to the target and place the file in /lib/firmware/.
      scp rpmsg_multi_services.elf root@<target-ip>:/lib/firmware/
    2. Or, if you use NFS or TFTP boot:
      cp rpmsg_multi_services.elf <rootfs>/lib/firmware/
    3. scp rpmsg_multi_services.elf root@<target-ip>:/lib/firmware/
  2. Load RPMsg kernel modules.

    Run these commands on the target Linux system.

    1. Load the RPMsg character driver: modprobe rpmsg_char
    2. Load the RPMsg control driver: modprobe rpmsg_ctrl
    3. Load the RPMsg TTY driver: modprobe rpmsg_tty
    4. Optional: load the sample client driver: modprobe rpmsg_client_sample
  3. Start the RPU through RemoteProc.
    1. Verify that the remoteproc device exists: ls /sys/class/remoteproc/.
    2. Set the firmware name: echo "rpmsg_multi_services.elf" > /sys/class/remoteproc/remoteproc0/firmware.
    3. Start the RPU: echo start > /sys/class/remoteproc/remoteproc0/state.
    Note: When you run echo start, the remoteproc driver:
    1. Reads the ELF firmware from /lib/firmware/.
    2. Parses the resource table in the ELF file.
    3. Maps memory regions, including carveouts and vrings.
    4. Powers on the R52 core and TCMs through embedded energy management interface (EEMI).
    5. Loads ELF segments into the DDR region at 0x9800100.
    6. Sets the R52 entry point.
    7. Starts the R52 processor.
    8. Initializes VirtIO and the RPMsg bus.
    9. Creates device nodes such as /dev/ttyRPMSG0 and /dev/rpmsg0.
  4. Verify the startup by checking the remoteproc state and the kernel log:
    cat /sys/class/remoteproc/remoteproc0/state
    dmesg | grep -i "rpmsg\|remoteproc\|virtio"

    Expected state: running

    Example dmesg output:
    [   10.123456] remoteproc remoteproc0: powering up r52f@0
    [   10.234567] remoteproc remoteproc0: Booting fw image rpmsg_multi_services.elf
    [   10.345678] remoteproc remoteproc0: remote processor r52f@0 is now up
    [   10.456789] virtio_rpmsg_bus virtio0: rpmsg host is online
    [   10.567890] virtio_rpmsg_bus virtio0: creating channel rpmsg-client-sample addr 0x400
    [   10.678901] virtio_rpmsg_bus virtio0: creating channel rpmsg-tty addr 0x401
    [   10.789012] virtio_rpmsg_bus virtio0: creating channel rpmsg-raw addr 0x402

RPMsg Channel Creation

  1. Review the Zephyr side.
    • The Zephyr application creates RPMsg endpoints and announces them to Linux.
    • Reference implementation: main_remote.c.
    /* From main_remote.c */
    
    /* 1. Initialize platform (shared memory, IPC, resource table) */
    platform_init();
    
    /* 2. Create RPMsg virtio device */
    rpdev = platform_create_rpmsg_vdev(0, VIRTIO_DEV_DEVICE, NULL, new_service_cb);
    
    /* 3. Create RPMsg endpoints for different services */
    
    /* Client Sample Endpoint */
    rpmsg_create_ept(&cs_ept, rpdev, "rpmsg-client-sample",
                     RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
                     rpmsg_recv_cs_callback, NULL);
    
    /* TTY Endpoint */
    rpmsg_create_ept(&tty_ept[0], rpdev, "rpmsg-tty",
                     RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
                     rpmsg_recv_tty_callback, NULL);
    
    /* Raw/Char Endpoint */
    rpmsg_create_ept(&raw_ept[0], rpdev, "rpmsg-raw",
                     RPMSG_ADDR_ANY, RPMSG_ADDR_ANY,
                     rpmsg_recv_raw_callback, NULL);
    
    Each endpoint has a receive callback. The callback runs when a message arrives.
  2. Review the Linux side. Linux creates device nodes for announced channels automatically.
    # Check for rpmsg-tty device
    ls /dev/ttyRPMSG*
    # Output: /dev/ttyRPMSG0
    
    # Check for rpmsg-char device
    ls /dev/rpmsg*
    # Output: /dev/rpmsg0  /dev/rpmsg_ctrl0
    

Device types:

  • rpmsg-client-sample: test endpoint that sends 100 messages automatically
  • rpmsg-tty: creates /dev/ttyRPMSGx for terminal communication
  • rpmsg-char: creates /dev/rpmsgx for raw character device access

Testing RPMsg Communication

  1. Test rpmsg-tty.
    1. Open a read session in the background.
    2. Send a message to Zephyr:
      cat /dev/ttyRPMSG0 &
      echo "Hello Zephyr" > /dev/ttyRPMSG0
    3. Check the response. The expected response is as follows:
      TTY 0: Hello Zephyr
      The Zephyr application echoes the message and adds a prefix that shows the TTY instance.
  2. Test rpmsg-char. If rpmsg-utils is installed, ping the raw endpoint.
    1. Run rpmsg_ping.
      rpmsg_ping /dev/rpmsg0
    2. Check the response. The expected response is as follows:
      message for /dev/rpmsg0: "from ept 0x0402: ping /dev/rpmsg0"
  3. Test rpmsg-client-sample. This test runs automatically at startup and exchanges 100 messages.
    1. Check dmesg.
      dmesg | grep rpmsg_client_sample
    2. Confirm that the message count reaches 100. The expected output is as follows:
      rpmsg_client_sample virtio0.rpmsg-client-sample.-1.1024: new channel: 0x402 -> 0x400!
      rpmsg_client_sample virtio0.rpmsg-client-sample.-1.1024: incoming msg 1 (src: 0x400)
      ...
      rpmsg_client_sample virtio0.rpmsg-client-sample.-1.1024: incoming msg 100 (src: 0x400)
      rpmsg_client_sample virtio0.rpmsg-client-sample.-1.1024: goodbye!