2. RPMsg基础概念

环境搭建这件事,说白了就是给RPMsg通信铺好路。路没铺好,后面调得再欢也是白搭。我刚开始接触OpenAMP时,就吃过硬件没配对的亏——折腾了两天才发现是设备树里漏了个中断映射。

这一章,我们重点搞定三件事:选对硬件平台、编译OpenAMP软件栈、配置Linux内核。每一步我都会把坑点指出来。

硬件平台选型:Zynq vs STM32MP1

目前主流的RPMsg硬件平台就两个阵营:Xilinx的Zynq系列和ST的STM32MP1系列。我两个都用过,给你做个对比。

对比项 Zynq-7000 / Zynq UltraScale+ STM32MP157 / STM32MP135
CPU架构 Cortex-A + Cortex-R(或MicroBlaze) Cortex-A7 + Cortex-M4
共享内存 OCM(On-Chip Memory)或DDR保留区 SYSRAM或DDR保留区
中断机制 IPI(Inter-Processor Interrupt) M4专用中断 + mailbox
OpenAMP支持 官方例程完善,文档多 官方例程较新,社区活跃
调试工具 XSDK / Vitis + JTAG STM32CubeIDE + ST-Link
适合场景 高性能计算、FPGA加速 工业控制、低功耗边缘计算
我的建议:如果你是第一次接触RPMsg,优先选Zynq。原因很简单——资料多,踩坑的人多,你搜到的解决方案也多。STM32MP1的M4核跑OpenAMP时,共享内存地址对齐问题容易让人抓狂。

硬件连接与启动方式

不管选哪个平台,硬件上你至少需要:

  • 开发板:Zynq选ZedBoard或Pynq-Z2,STM32MP1选Discovery Kit或Evaluation Board
  • 调试器:JTAG(Zynq用Digilent HS2,STM32用ST-Link)
  • 串口线:至少两路UART,一路给A核,一路给M/R核
  • SD卡或QSPI Flash:存放启动镜像

嗯,这里要注意:两个核的串口一定要分开。我见过有人把A核和M核的打印信息混到同一个串口,调试时根本分不清谁是谁。

OpenAMP软件栈编译

OpenAMP的软件栈分三层:

  1. libmetal:硬件抽象层,管理共享内存、中断、锁
  2. libopenamp:核心库,实现RPMsg协议、virtio、remoteproc
  3. 应用层:你写的通信代码

编译流程我习惯用Yocto或手动交叉编译。这里给出手动编译的步骤,因为看得更清楚。

1. 下载源码

git clone https://github.com/OpenAMP/libmetal.git
git clone https://github.com/OpenAMP/open-amp.git
git clone https://github.com/OpenAMP/remoteproc.git  # 可选,用于加载固件

2. 交叉编译libmetal

cd libmetal
mkdir build && cd build
cmake .. \
  -DCMAKE_TOOLCHAIN_FILE=../cmake/platforms/zynq7_generic.cmake \
  -DCMAKE_INSTALL_PREFIX=/usr/local/openamp
make -j4
make install
我曾经踩过的坑:编译libmetal时,工具链文件里的CROSS_COMPILE路径一定要写绝对路径。我一开始用了相对路径,结果链接时死活找不到libgcc。折腾了三个小时才发现是路径问题。

3. 编译libopenamp

cd open-amp
mkdir build && cd build
cmake .. \
  -DCMAKE_TOOLCHAIN_FILE=../cmake/platforms/zynq7_generic.cmake \
  -DLIBMETAL_INCLUDE_DIR=/usr/local/openamp/include \
  -DLIBMETAL_LIBRARY=/usr/local/openamp/lib/libmetal.a \
  -DCMAKE_INSTALL_PREFIX=/usr/local/openamp
make -j4
make install

编译完成后,你会得到三个关键产物:

  • libmetal.a — 硬件抽象静态库
  • libopenamp.a — RPMsg核心静态库
  • echo_test / matrix_multiply — 官方示例程序

Linux内核配置

Linux端要支持RPMsg,内核必须开启以下选项:

内核配置项 说明 推荐设置
CONFIG_RPMSG RPMsg核心支持 y
CONFIG_RPMSG_VIRTIO 基于virtio的RPMsg传输 y
CONFIG_REMOTEPROC 远程处理器管理框架 y
CONFIG_REMOTEPROC_CDEV 用户空间控制remoteproc y
CONFIG_VIRTIO virtio框架 y
CONFIG_VIRTIO_RING virtio ring buffer y

配置方法:

cd <你的内核源码目录>
make menuconfig
# 进入 Device Drivers → Mailbox Hardware Support → 开启对应平台的mailbox驱动
# 进入 Device Drivers → Remoteproc drivers → 开启对应平台的remoteproc驱动
# 进入 Device Drivers → Rpmsg drivers → 开启rpmsg相关选项

关键点:设备树里必须定义共享内存区域和中断号。以Zynq为例,设备树中需要添加:

reserved-memory {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;
    rproc_0_reserved: rproc@3e000000 {
        no-map;
        reg = <0x3e000000 0x01000000>;
    };
};

这段代码告诉内核:0x3e000000开始的16MB内存留给协处理器用,Linux别碰。

整体环境搭建流程

我把整个流程画成了一张图,方便你对照着操作。

RPMsg环境搭建流程 RPMsg环境搭建流程 1. 硬件平台准备 2. OpenAMP编译 3. Linux内核配置 选择开发板(Zynq/STM32MP1) 连接调试器与串口线 确认启动方式(SD/QSPI) 下载libmetal + open-amp源码 交叉编译libmetal 交叉编译libopenamp 验证示例程序(echo_test) 开启RPMSG/REMOTEPROC选项 配置mailbox驱动 设备树添加共享内存 编译内核并生成dtb 启动验证 检查 /dev/rpmsg_ctrl0 是否存在

启动验证

所有配置完成后,上电启动。在Linux终端执行:

ls /dev/rpmsg*

如果看到/dev/rpmsg_ctrl0或/dev/rpmsg0,说明环境搭建成功。如果没有,按这个顺序排查:

  • 检查设备树中共享内存地址是否与协处理器固件中的地址一致
  • 检查mailbox中断号是否匹配
  • 检查内核是否真的编译了RPMSG模块(zcat /proc/config.gz | grep RPMSG)
我曾经遇到的情况:设备树里共享内存地址写对了,但协处理器固件里用的是物理地址,Linux端用的是虚拟地址,两边没对齐。结果rpmsg设备注册了,但一收发数据就死机。后来在共享内存区域加了no-map属性才解决。

环境搭建这一步,说白了就是给两个核之间铺一条双向车道。车道铺好了,后面才能跑数据、调性能。别急,慢慢来,每个配置项都确认一遍,比后面出问题再回头查要省时间得多。