MilkV Duo 核间通信分析

MilkV Duo 核间通信分析

一直对 milkv duo 的大小核 Linux 和 FreeRTOS 之间的交互很好奇,但苦于目前相关的资料还很少,就根据目前官方的文档和源码先简单分析一下吧,本人为 Linux 新手,很多理解错误或不到位的地方还希望大家指正补充。

官方文档示例

首先分析官方文档中的一个点灯示例:简介 | Milk-V

文档中给出一个在大核 Linux 中运行的 .c 文件,调用小核的 FreeRTOS 进行点灯。

#define RTOS_CMDQU_DEV_NAME "/dev/cvi-rtos-cmdqu"

int fd = open(RTOS_CMDQU_DEV_NAME, O_RDWR);

这里可以看出运行 FreeRTOS 的小核被描述为一个设备,对应为 /dev/cvi-rtos-cmdqu Linux 通过其进行访问。

struct cmdqu_t cmd = {0};
cmd.ip_id = 0;
cmd.cmd_id = CMD_DUO_LED;
cmd.resv.mstime = 100;
cmd.param_ptr = DUO_LED_ON;

ret = ioctl(fd , RTOS_CMDQU_SEND_WAIT, &cmd);

并通过 ioctl 对小核 RTOS 设备进行控制 (ioctl 的具体用法和支持的操作取决于设备和设备驱动程序。)

分析 ioctl 是大小核通信的关键部分,Linux 内核中是使用 mailbox 驱动,猜测其与 ioctl 的具体实现相关。

官方源码

大核Linux

https://github.com/milkv-duo/duo-buildroot-sdk/tree/develop/linux_5.10/drivers/soc/cvitek/rtos_cmdqu 通过该文件夹中的源码,分析 Linux 核间通信实现。

cvi_mailbox.h 为 mailbox 相关声明;

cvi_spinlock.c/.h 为自旋锁实现,用于线程通讯时同步保护;

rtos_cmdqu.c/.h 为与小核 RTOS 交互消息队列的主要部分。

在 rtos_cmdqu.h 文件中又能看到对小核 RTOS 设备的描述:

#define RTOS_CMDQU_DEV_NAME "cvi-rtos-cmdqu"

向外提供以下几个向消息队列发送消息的api:

int rtos_cmdqu_send(cmdqu_t *cmdq);
int rtos_cmdqu_send_wait(cmdqu_t *cmdq, int wait_cmd_id);

在 rtos_cmdqu.c 文件中可以找到 ioctl 的具体实现:

static long cvi_rtos_cmdqu_ioctl(struct file *filp, unsigned int cmd, unsigned long  arg)

实现了 IOCTL 处理程序(cvi_rtos_cmdqu_ioctl)以处理特定的 ioctl 命令,包括 RTOS_CMDQU_SENDRTOS_CMDQU_SEND_WAITRTOS_CMDQU_SEND_WAKEUP。其中调用 rtos_cmdqu_send 等向小核发起通信,rtos_cmdqu_send 中使用自旋锁进行同步保护,通过 mailbox 机制访问共享内存。

大核 Linux 通过 request_irq 注册中断处理函数:

err = request_irq(mailbox_irq, rtos_irq_handler, 0, "mailbox",
		(void *)ndev);

rtos_irq_handler 中通过 mailbox 机制访问读取共享内存,中断号 mailbox_irq 通过 platform_get_irq_byname 获取:

mailbox_irq = platform_get_irq_byname(pdev, "mailbox");

小核RTOS

rtos_cmdqu.h 中多出了以下操作:

#define RTOS_CMDQU_REQUEST                      _IOW('r', CMDQU_REQUEST, unsigned long)
#define RTOS_CMDQU_REQUEST_FREE                 _IOW('r', CMDQU_REQUEST_FREE, unsigned long)

riscv64 该文件夹中可以找到提供的 led io 操作,以及 FreeRTOS 中负责核间通信的主要部分 comm_main.c ,其中使用命令队列 queHandle 存储来自 Linux 的命令,并创建了一个名为 CMDQU 的任务,用于处理核间通信,根据Linux 的指令进行控制处理。接受来自 Linux 的命令是在中断服务中进行,通过 xQueueSendFromISR 将接受到的命令存储到命令队列中。

总结

以上就是目前 milkv duo 大小核之间通信的简单分析,通过自旋锁进行同步保护,使用中断进行通知触发,通过 mailbox 的机制访问共享内存进行通讯。

感兴趣可以去看下OpenAMP,这种多核异构,都会涉及核间通信硬件单元,邮箱、共享内存的等,搭配中断和锁实现通信。

感谢,目前官方提供的示例还没有使用openAMP吧

这种邮箱的方式感觉比较麻烦,代码就看着挺啰嗦的,实际效果还是个战5渣(不能传数据块)。不如大小核心间映射一个串口,各自对这个串口操作没有任何代码耦合关系,好像STM32MP157就是这么做的,这个串口是芯片级互连可以轻松跑10Mbps以上速率。如果能在大小核心间映射一个网口,那就是王者了,大核心开Docker映射端口做服务,小核心直接通过网口调用服务。
芯片也容易做,把eth的IP复制粘贴给大小核心,phy层都不需要,die上mac层直接互连 :smiling_face_with_three_hearts: