【SemiDrive源码分析】【X9芯片启动流程】23 - MailBox 核间通信机制介绍(代码分析篇)之 RPMSG-IPCC 篇
- 一、RPMSG 接口
- 1.1 Linux Kernel 接口
- 1.2 Linux Kernel 示例代码分析:rpmsg-vdev.c 基于 virtio 总线方式实现 RPMSG
- 1.3 Linux Kernel 示例代码分析:semidrive_ipcc.c 基于 IPCC 总线方式实现 RPMSG
- 1.3 Linux 用户层接口
- 1.4 RTOS API
- 1.5 QNX API
本 SemiDrive源码分析 之 Yocto源码分析 系列文章汇总如下:
- 《【SemiDrive源码分析】【Yocto源码分析】01 - yocto/base目录源码分析(编译环境初始化流程)》
- 《【SemiDrive源码分析】【Yocto源码分析】02 - yocto/meta-openembedded目录源码分析》
- 《【SemiDrive源码分析】【Yocto源码分析】03 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(上)》
- 《【SemiDrive源码分析】【Yocto源码分析】04 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(下)》
- 《【SemiDrive源码分析】【Yocto源码分析】05 - 找一找Yocto Kernel编译过程中所有Task的源码在哪定义的呢?》
- 《【SemiDrive源码分析】【Yocto源码分析】06 - Kernel编译生成的Image.bin、Image_nobt.dtb、modules.tgz 这三个文件分别是如何生成的?》
- 《【SemiDrive源码分析】【Yocto源码分析】07 - core-image-base-x9h_ref_serdes.rootfs.ext4 文件系统是如何生成的》
- 《【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍》
- 《【SemiDrive源码分析】【X9芯片启动流程】09 - X9平台系统启动流程分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】10 - BareMetal_Suite目录R5 DIL.bin 引导程序源代码分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】11 - freertos_safetyos目录Cortex-R5 DIL2.bin 引导程序源代码分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析》
- 《【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法》
- 《【SemiDrive源码分析】【X9芯片启动流程】14 - freertos_safetyos目录Cortex-R5 SafetyOS/RTOS工作流程分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】15 - freertos_safetyos目录 R5 SafetyOS 之 tcpip_init() 代码流程分析》
- 《【SemiDrive源码分析】【X9 Audio音频模块分析】16 - 音频模块框图及硬件原理图分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】17 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程分析(上)dcf_init 核间通信初始化》
- 《【SemiDrive源码分析】【X9芯片启动流程】18 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程分析(下)》
- 《【SemiDrive源码分析】【X9芯片启动流程】19 - MailBox 核间通信机制介绍(理论篇)》
- 《【SemiDrive源码分析】【X9芯片启动流程】20 - MailBox 核间通信机制介绍(代码分析篇)之 MailBox for RTOS 篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】22 - MailBox 核间通信机制介绍(代码分析篇)之 RPMSG-VIRTIO Kernel 篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】23 - MailBox 核间通信机制介绍(代码分析篇)之 RPMSG-IPCC Kernel 篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】24 - MailBox 核间通信机制介绍(代码分析篇)之 Property篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】25 - MailBox 核间通信机制介绍(代码分析篇)之 RPCall篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】26 - MailBox 核间通信机制介绍(代码分析篇)之 Notify篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】27 - MailBox 核间通信机制介绍(代码分析篇)之 Socket篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】28 - MailBox 核间通信机制介绍(代码分析篇)之 /dev/vircan篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】29 - freertos_safetyos目录 R5 SafetyOS 之 LK_INIT_LEVEL_TARGET 阶段代码流程分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】30 - freertos_safetyos目录 R5 SafetyOS 之 .apps 应用启动代码流程分析》
在前面文章中,我们主要从理论方面介绍了,基于MailBox的各种场景的API,
其特点如下:
RTOS | Linux Kernel | Linux User | Dom0 Linux | DomU Linux | QNX | TX/RX Buffer大小 | 特点和使用场景 | |
Mailbox | 支持 | 支持 | 不支持 | 支持 | 支持 | 支持 | RX/TX均为512字节 | 中断上下文回调,低延迟,无消息队列,不推荐用户直接使用 |
RPMSG-VIRTIO | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 | RX/TX 两个回环队列 每个队列最大支持512块Buffer,最小256块 每块Buffer为512字节 | 大数据块传输,使用DDR 共享内存数据,高吞吐量,MTU 可调节 |
RPMSG-IPCC | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 | 控制命令,事件,通知等,交互数据小,延迟较小 | |
RPCall | 支持 | 支持 | 不支持 | 支持 | 支持 | 支持 | 基于 request / reply模型的分布式数据交换方式 | |
Property | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 一种分布式、运行时异构数据存储机制,可以保存运行状态或执行结果,方便异构OS 之间共用某些资源 | |
Notify | 支持 | 支持 | 不支持 | 支持 | 支持 | 不支持 | 短数据,低延迟,单向传输 | |
Socket | 无支持 | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 AF_RPMSG 协议,用户层使用,缺点是需要修改 Linux uapi 头文件 | |
/dev/vircan | NA | NA | 支持 | 支持 | 支持 | 不支持 | Linux 访 问 Safety CAN |
前面,我们分析学习了 MailBox 的RTOS 及 Linux实现方式,那本文我们来看看 RPMSG 在代码中又是如何实现的。
RPMSG 是 Remote Processor Messaging System, 远程处理器消息传输系统。
-
RPMSG 是一个用于异构系统间的数据传输协议。
RPMSG 标准并未规定底层传输介质和方法, 因为共享内存的普遍性,且对具体硬件差异依赖较少,VIRTIO 作为一种 RPMSG 常见实现形式,得到了广泛应用。 -
VIRTIO 是基于 Shared Memory 的一种数据传输标准,包含了缓冲区和消息队列管理,不仅用于异构多处理器通信,也常用于 KVM 虚拟化设备。 -
VirtQueue 是数据收发队列,包含一对发送队列和接收队列 Vring。 -
VRing 是 VirtQueue 中的队列,一种具有 Buffer Descriptor 的索引型量子化的循环缓冲区实现方法,每个 VRing 只有一个发送者和一个接收者,双向通讯需要有两个 VRing 组成一对,整个队列的索引结构体和缓冲区都存在于共享内存区,由通信两端处理器访问。为了
保证异构处理器的数据访问一致性,共享内存都配置成设备内存模型,Strong Order,Non-Cacheable - 标准规定通知机制(
notify)是平台有关,在芯驰平台使用 Mailbox 硬件实现。
一、RPMSG 接口
芯驰完全遵循标准的 RPMSG 实现,在此基础上,为了便捷使用,提出了 RPMSG Channel 的概念和一套接口实现。
同时,为了利用 Mailbox 片上内存的芯片特性,提出了 IPCC Channel 的概念,即保持 RPMSG 接口逻辑,
遵循标准 RPMSG 的基础上,替换了 VIRTIO 的底层部分。这两部分功能是可以共存的,用户可以根据需要选择其中一种。
IPCC 充分利用了 Mailbox 硬件的特点,具有独立的缓冲队列管理和线程模型,而且 IPCC 无需外部 DDR 作为共享内存,对于安全较高 MCU 有明显优点,推荐优先使用 IPCC 接口函数完成相同功能。
另外,考虑到 VIRTIO 要求有主从设备的概念,通常是 Linux 或者 QNX 端作为主设备,负责 Virtqueue缓冲区队列的初始化,导致 MCU 如果要使用 VIRTIO,时序上依赖于主设备的初始化,必然会影响启动。
而 IPCC 所有端都是平等的,启动过程中有握手机制确保双方连接正常,也支持一方重启动断开连接等异常情况,更加符合功能安全的要求。
当用户有使用大块内存传输需求时,或者对带宽有更激进的需求时,可以考虑使用 RPMSG VIRTIO实现版本。
增大 MTU 可以获得更大的单次传输能力,提高传输带宽。
1.1 Linux Kernel 接口
内核部分的接口通常用于实现以 RPMSG 为通信基础设施的其他设备驱动,比如视频,音频等多媒体设备。
使用者需要先注册 rpmsg driver,设置匹配关键字字符串,当 rpmsg device 挂载到 bus 之后,会进行匹配,匹配成功后进行 probe 回调处理,和通常的 platform device 设备驱动模型相似。
Linux rpmsg API 的申明和定义分别位于以下文件:
${LINUX_KERNEL}/include/linux/rpmsg/rpmsg.h
${LINUX_KERNEL}/drivers/rpmsg/rpmsg_core.c基本实现原理:
-
rpmsg driver, 由rpmsg 的使用者实现,并嵌入到其他子系统中 -
rpmsg bus 核心, 由内核提供的一套API 接口,可以视为RPMSG 基类。 -
rpmsg bus 实现类, 取决于媒体访问层实现,有两个实现版本:
基于VIRTIO 总线,实现文件是:${LINUX_KERNEL}/drivers/rpmsg/rpmsg-vdev.c 基于 IPCC 总线,实现文件是:${LINUX_KERNEL}/drivers/rpmsg/semidrive_ipcc.c
rpmsg driver 和 rpmsg device 由 bus 通过相同的 device id 匹配,这个过程和其他 Linux 设备模型类似。
注意:Linux dts 中定义的 RPMSG 节点是指 rpmsg bus,而非 rpmsg device,
Linux 上的 rpmsg device 概念接近于 endpoint,
当远端处理器创建 RPMSG channel 或者 endpoint,并显式地申明(announce)后,
Linux rpmsg bus 核心会把这个 endpoint 挂载到 bus,然后创建一个rpmsg device
1.2 Linux Kernel 示例代码分析:rpmsg-vdev.c 基于 virtio 总线方式实现 RPMSG
见:《【SemiDrive源码分析】【X9芯片启动流程】22 - MailBox 核间通信机制介绍(代码分析篇)之 RPMSG-VIRTIO Kernel 篇》
1.3 Linux Kernel 示例代码分析:semidrive_ipcc.c 基于 IPCC 总线方式实现 RPMSG
1.3 Linux 用户层接口
首先,可以使用 echo_test -l 或者 ls -1 /sys/bus/rpmsg/devices/命令查看系统中存在的 rpmsg device。
比如:
soc:ipcc@0.ipcc-echo.-1.30
soc:ipcc@0.rpmsg-ipcc-rpc.-1.16
soc:ipcc@0.rpmsg-property.-1.13
soc:ipcc@1.ipcc-echo.-1.30
soc:ipcc@1.rpmsg-ipcc-rpc.-1.16
soc:ipcc@1.rpmsg-ssystem.-1.72
virtio1.rpmsg-echo.-1.30设备命名规则分成几个部分,分别标记位是 xxx@kk.yyy.sss.ddd,各部含义如下:
-
xxx 指RPMSG bus 字符,是在dts 中指定的,目前含有virtio 和ipcc 两种bus 类型。 -
kk 指的是对端处理器的编号。 -
yyy 是指设备endpoint 字符名称,是由对端Endpoint/Channel 创建时指定的。 -
sss 是Endpoint 源地址,也就是本机地址,通常用-1 表示,表示广播地址。 -
ddd 是Endpoint 目标地址,也就是远程地址,指定对端接收者。
比如:
soc:ipcc@0 是 IPCC 总线名,@后数字和CPU-ID 一致,0 表示Safety 域;1 表示Security 域。
virtio1 是VIRTIO 总线名,数字同上所示,rpmsg-echo 或者rpmsg-property 是endpoint 名称,数字30 是专用于测试的endpoint 编号,系统默认会创建用于回包测试endpoint,16 是用于RPC。
Linux 用户层有两种方法使用 rpmsg device
- 通过
Linux 标准的 rpmsg char driver,把 rpmsg 设备功能 export 到用户层。 - 芯驰自有的
rpmsg driver,通过 DCF 字符设备驱动,把 rpmsg device 映射成 /dev/xxx 设备文件,用户层可以按标准 Unix 文件方式访问。
常用 rpmsg device 包括:
/dev/映射设备 | rpmsg device 设备名 | 用途 |
/dev/vircan | virtio0.rpmsg-vircan.-1.90 | 访问 Safety 域 CAN 总线 |
/dev/property | soc:ipcc@0.rpmsg-property.-1.13 | 访问 Safety 域属性 Property |
N/A | soc:ipcc@0.ipcc-echo.-1.30 | 访问 Safety 域回包测试 |
N/A | virtio1.rpmsg-echo.-1.30 | 访问 Secure 域回包测试(virtio) |
关于用户层接口的具体使用,可以参考芯驰 Yocto SDK 的 echo_test 测试工具,
位于 ${LINUX_SDK_TOP}/meta-semidrive/recipes-bsp/rpmsg-echo-test/
1.4 RTOS API
在芯驰 RTOS SDK 中使用 rpmsg virtio 功能需要包含以下头文件: #include <rpmsg_rtos.h>
rpmsg channel基于开源社区rpmsg lite做进一步封装,使用virtio作为数据传输,使用Mailbox作为跨核事件通知。
当使用 rpmsg channel 接口创建的 endpoint 成功后,Linux 端会出现相应的以 virtio0(如果 security 域则为 virtio1)为前缀的 rpmsg device
rpmsg lite 的代码位于 ${RTOS_SDK_TOP}/3rd/rpmsg-literpmsg channel 封装层位于 ${RTOS_SDK_TOP}/framework/service/rpmsg
rpmsg channel API 定义:
struct rpmsg_channel * rpmsg_channel_create(int rproc, int dst, const char *name);
int rpmsg_channel_start(struct rpmsg_channel *rpchn, rpmsg_msg_handler handler);
void rpmsg_channel_destroy(struct rpmsg_channel *rpchn);
status_t rpmsg_channel_stop(struct rpmsg_channel *rpchn);
void rpmsg_channel_listall(struct rpmsg_device *dev);
status_t rpmsg_channel_sendmsg(struct rpmsg_channel *rpchn, struct dcf_message *msg, int len, lk_time_t timeout);
status_t rpmsg_channel_recvmsg(struct rpmsg_channel *rpchn, struct dcf_message *msg, int msglen, lk_time_t timeout);
status_t rpmsg_channel_recvfrom(struct rpmsg_channel *rpchn, unsigned long *src, char *data, int maxlen, int *len, unsigned long timeout);
status_t rpmsg_channel_sendto(struct rpmsg_channel *rpchn, unsigned long dst, char *data, unsigned long size, unsigned long timeout);在芯驰 RTOS SDK 中使用 IPCC 功能需要包含以下头文件:#include <dcf.h>
IPCC Channel API 定义与上类似,详情请查看接口申明:
${RTOS_SDK_TOP}/framework/communication/include/ipcc_device.h
接口实现 C 文件:
${RTOS_SDK_TOP}/framework/communication/ipcc_device.c
参考 SDK 中包含的示例代码
RPMSG VIRTIO: ${RTOS_SDK_TOP}/framework/test/dcf/test_rpmsg_linux.cRPMSG IPCC: ${RTOS_SDK_TOP}/framework/test/dcf/dcf_sample.c
1.5 QNX API
从 X9 PTG3.9 发布开始,核间通信正式支持 QNX,参考代码 echo_test.c 通过和 Linux RPMSG 相似的接口与他核进行数据交互。
目前 QNX API 支持字符类型和传统类型两种访问形式。
- 字符设备类型:
兼容 POSIX 文件操作,通过 open, close, write,read 接口打开核间通信 endpoint。 - 传统类型:
通过 RPMSG 特有的 API,buffer 处理数据收发,可以给底层开发人员提供底层更高细粒度的控制。
字符类型接口是对传统类型的封装,方便用户使用,对于普通用户来说,字符设备类型更简单方便,推荐使用。
详细的使用请参考 Semidrive QNX SDK 附带的 echo_test 程序。
与 Linux 用户层接口稍有区别的是,QNX 系统上的 RPMSG 设备名称修改成/dev/rpmsgX 的形式。
X 是十进制数字,对应关系如下:
/dev/rpmsgX | RPSMG 物理层 | 对端处理器 | 备注 |
0 | Virtio | Safety | |
1 | Virtio | Secure | |
2 | Virtio | MP core | 用于 G9 系列 |
3 | IPCC | AP1 IVI | 用于 X9HP QNX 仪表 |
10 | IPCC | Safety | |
11 | IPCC | Secure | |
12 | IPCC | MP Core |










