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
- Build the Zephyr RPU firmware with Yocto embedded development framework (EDF).
For details, refer to Yocto Project Build.
- Go to the Yocto build directory.
- Source the build environment.
- Build the firmware.
cd <yocto-workspace> source setupsdk bitbake openamp-zephyr-demoNote: If you want to build only the firmware ELF file, use this command:bitbake zephyr-openamp-rpmsg-multi-services. - Build target differences:
-
openamp-zephyr-demobuilds the Zephyr firmware and installs it in the Linux root file system at/lib/firmware/. -
zephyr-openamp-rpmsg-multi-servicesbuilds only the firmware ELF binary.
-
- 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.
- 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.- Copy the firmware to the target and place the file in
/lib/firmware/.
scp rpmsg_multi_services.elf root@<target-ip>:/lib/firmware/ - Or, if you use NFS or TFTP
boot:
cp rpmsg_multi_services.elf <rootfs>/lib/firmware/ -
scp rpmsg_multi_services.elf root@<target-ip>:/lib/firmware/
- Copy the firmware to the target and place the file in
/lib/firmware/.
- Load RPMsg kernel modules.
Run these commands on the target Linux system.
- Load the RPMsg character driver:
modprobe rpmsg_char - Load the RPMsg control driver:
modprobe rpmsg_ctrl - Load the RPMsg TTY driver:
modprobe rpmsg_tty - Optional: load the sample client driver:
modprobe rpmsg_client_sample
- Load the RPMsg character driver:
- Start the RPU through RemoteProc.
- Verify that the remoteproc device exists:
ls /sys/class/remoteproc/. - Set the firmware name:
echo "rpmsg_multi_services.elf" > /sys/class/remoteproc/remoteproc0/firmware. - Start the RPU:
echo start > /sys/class/remoteproc/remoteproc0/state.
Note: When you runecho start, the remoteproc driver:- Reads the ELF firmware from
/lib/firmware/. - Parses the resource table in the ELF file.
- Maps memory regions, including carveouts and vrings.
- Powers on the R52 core and TCMs through embedded energy management interface (EEMI).
- Loads ELF segments into the DDR region at
0x9800100. - Sets the R52 entry point.
- Starts the R52 processor.
- Initializes VirtIO and the RPMsg bus.
- Creates device nodes such as
/dev/ttyRPMSG0and/dev/rpmsg0.
- Verify that the remoteproc device exists:
- 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:
Examplerunningdmesgoutput:[ 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
- Review the Zephyr side.
- The Zephyr application creates RPMsg endpoints and announces them to Linux.
- Reference implementation: main_remote.c.
Each endpoint has a receive callback. The callback runs when a message arrives./* 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); - 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/ttyRPMSGxfor terminal communication -
rpmsg-char: creates/dev/rpmsgxfor raw character device access
Testing RPMsg Communication
- Test
rpmsg-tty.- Open a read session in the background.
- Send a message to
Zephyr:
cat /dev/ttyRPMSG0 & echo "Hello Zephyr" > /dev/ttyRPMSG0 - Check the response. The expected response is as
follows:
The Zephyr application echoes the message and adds a prefix that shows the TTY instance.TTY 0: Hello Zephyr
- Test
rpmsg-char. Ifrpmsg-utilsis installed, ping the raw endpoint.- Run
rpmsg_ping.rpmsg_ping /dev/rpmsg0 - Check the response. The expected response is as
follows:
message for /dev/rpmsg0: "from ept 0x0402: ping /dev/rpmsg0"
- Run
- Test
rpmsg-client-sample. This test runs automatically at startup and exchanges 100 messages.- Check
dmesg.dmesg | grep rpmsg_client_sample - 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!
- Check