STM32 STM32MP15X 是基于 CM4+CA7 的异构 SOC,CM4 侧用于实时任务的处理, CA7 侧运行 Linux,负责更加复杂的计算和任务处理,CM4 侧采集到的数据通常会给到 CA7 去 做处理,因此必然需要核间通信的机制,核间通信需要硬件和软件的支持。
2)共享内存:负责数据交互
软件:
1)CM4 侧 OPenAMP
2)CA7 Linux 侧 RPMsg framework(Mailbox,Remoteproc,RPMsg,VirtIO)
对于核间通信的基本原理请阅读相关 wiki 站点文档。
https://wiki.stmicroelectronics.cn/stm32mpu/wiki/Exchanging_buffers_with_the_coprocessor
本文将结合几个实际案例讨论核间通信可能遇到的问题:
如何增加 RPMsg buffer size
如何增加 RPMsg 通道数
RPMsg 通信过程出现死锁
双核通信过程出现 No more buffer in queue,通信终止
2.1. 默认配置
首先我们来看一下当前代码里和 RPMSG 相关的默认配置,见如下代码:
CA7:virtio_rpmsg_bus.c
CM4:rpmsg_virtio.h
Reserve memory 配置,参考如下 linux 设备树配置。
Stm32mp15xx-dkx.dtsi:
vdev0vring0: vdev0vring0@10040000 {
compatible = "shared-dma-pool";
reg = <0x10040000 0x1000>;
no-map;
};
vdev0vring1: vdev0vring1@10041000 {
compatible = "shared-dma-pool";
reg = <0x10041000 0x1000>;
no-map;
};
vdev0buffer: vdev0buffer@10042000 {
compatible = "shared-dma-pool";
reg = <0x10042000 0x4000>;
no-map;
};
通道数目:定义在 CM4 核的头文件中,这个值最终会传到 CA7 Linux 侧。
CM4: openamp_conf.h define VRING_NUM_BUFFS 16
从如上配置可以看到当前总共有 16 个 RPMSG 通道,每个通道的大小为 512 byte。
每次通信发送和接收的信息包含信息头和信息的实际内容两部分,其中 Vring 用来传输 Message header, Buffer pool 传输真正的数据。
TX Vring :vdev0vring0,reserve 0x1000 即 4K byte,
RX Vring :vdev0vring1,reserve 0x1000 即 4K byte
static inline unsigned vring_size(unsigned int num, unsigned long align)
{
return ((sizeof(struct vring_desc) * num + sizeof(__virtio16) * (3 + num)
+ align - 1) & ~(align - 1))
+ sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num;
}
CM4:Virtio_vring.h
static inline int vring_size(unsigned int num, unsigned long align)
{
int size;
size = num * sizeof(struct vring_desc);
size += sizeof(struct vring_avail) + (num * sizeof(uint16_t)) + sizeof(uint16_t);
size = (size + align - 1) & ~(align - 1);
size += sizeof(struct vring_used) +
(num * sizeof(struct vrindg_used_elem)) + sizeof(uint16_t);
return size;
}
根据上述方法计算出 16 通道下的 vring size 为 438byte,但是由于 linux 系统的中 reserve memory 的规则需要按 page(4096byte)对齐,因此 0x1000 是最小 reserve 的值。
基于当前的配置在实际发送数据时,单次只能发送 512byte 的数据,一旦超过则会被截断丢弃,因此如果在遇到这种单帧数据量超过 512 byte 的场景,又不想截断数据分批发送的话,那么可 以考虑将 buffer size 增大。
2.2. 如何将 Buffer size 改为 2048 byte
Stm32mp15xx-dkx.dtsi:
vdev0vring0: vdev0vring0@10040000 {
compatible = "shared-dma-pool";
reg = <0x10040000 0x1000>;
no-map;
};
vdev0vring1: vdev0vring1@10041000 {
compatible = "shared-dma-pool";
reg = <0x10041000 0x1000>; no-map;
};
vdev0buffer: vdev0buffer@10042000 {
compatible = "shared-dma-pool";
reg = <0x10042000 0x4000>;
no-map;
};
define VRING_NUM_BUFFS 4
通过前文的理解,我们已经知道如何计算 vdev0buffer 和 vdev0vring 的 size 了, 根据通道 vdev size 的计算公式 vdev0buffer_size = buffer_size * number_of buffer * 2 可计算所需的vdev0buffer_size=256 * 64*2=32768byte=0x8000 Vring size:16 通道,vring size 为 438byte, 那么可得 64 通道时的 vring_size 约为 1752byte=0x6db, 由于 reserve memory 按照 4K page size(0x1000)对齐,且 64 通道数的情况下所需要的 vring_size 0x6db 仍然小于 0x1000,vring size 的大小可保持不变。
Stm32mp15xx-dkx.dtsi:
vdev0vring0: vdev0vring0@10040000 {
compatible = "shared-dma-pool";
reg = <0x10040000 0x1000>;
no-map;
};
vdev0vring1: vdev0vring1@10041000 {
compatible = "shared-dma-pool";
reg = <0x10041000 0x1000>;
no-map;
};
vdev0buffer: vdev0buffer@10042000 {
compatible = "shared-dma-pool";
reg = <0x10042000 0x8000>;
no-map;
};
问题现象是 RPMSG 通信过程中出现死锁,导致通信中断,系统重启
4.1. 参考日志片段:
4.2. 解析
该问题表现为在 RPMSG 正常通信一段时间后,CA7 linux 内核不停打印日志 No more buffer in queue,通信过程中断,此时即使重新加载 CM4 仍然不能恢复,只能重启系统,该问题仅在 OPENSTLINUX_V1.0 版本,即对应的内核 v4.19 版本中出现,在后续版本中已修复.
5.1. 参考日志
5.2. 分析
END