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的软件栈分三层:
- libmetal:硬件抽象层,管理共享内存、中断、锁
- libopenamp:核心库,实现RPMsg协议、virtio、remoteproc
- 应用层:你写的通信代码
编译流程我习惯用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别碰。
整体环境搭建流程
我把整个流程画成了一张图,方便你对照着操作。
启动验证
所有配置完成后,上电启动。在Linux终端执行:
ls /dev/rpmsg*
如果看到/dev/rpmsg_ctrl0或/dev/rpmsg0,说明环境搭建成功。如果没有,按这个顺序排查:
- 检查设备树中共享内存地址是否与协处理器固件中的地址一致
- 检查mailbox中断号是否匹配
- 检查内核是否真的编译了RPMSG模块(
zcat /proc/config.gz | grep RPMSG)
我曾经遇到的情况:设备树里共享内存地址写对了,但协处理器固件里用的是物理地址,Linux端用的是虚拟地址,两边没对齐。结果rpmsg设备注册了,但一收发数据就死机。后来在共享内存区域加了
no-map属性才解决。
环境搭建这一步,说白了就是给两个核之间铺一条双向车道。车道铺好了,后面才能跑数据、调性能。别急,慢慢来,每个配置项都确认一遍,比后面出问题再回头查要省时间得多。