摘要:本文重点介绍RK3568内置的MCU与AP之间的通信功能,首先介绍RPMsg的框架,然后介绍MCU端RPMSG的构建方法,最后介绍AP端的RPMSG的配置方法,并进行MCU和AP之间通信的演示。
本文重点介绍RK3568内置的MCU与AP之间的通信功能,首先介绍RPMsg的框架,然后介绍MCU端RPMSG的构建方法,最后介绍AP端的RPMSG的配置方法,并进行MCU和AP之间通信的演示。
RK3568 MCU开发的入门介绍可以参考笔者的上一篇文章。
硬件准备
首先介绍一下硬件。主板为风火轮科技的YY3568开发板,主控RK3568。此开发板的相关介绍可以参考
RK3568的MCU核心需要使用串口调试,笔者这里使用的是UART4,这里也可以选择其他的,不要跟AP核心的调试串口UART2冲突就行。UART4的位置如下
RPMsg框架介绍
RPMsg(Remote Processor Messaging)是一种基于virtio的消息传递总线,专为异构处理器系统之间的通信设计。其定义了一个标准化的通信接口,使得不同架构的核心能够方便地进行通信。
RK3568上面有ARM核心和RISCV核心,需要使用RPMsg这种标准化的接口,这样可以不用考虑大小端转换等兼容问题。在RK3568上面,AP核心是RPMSG的master端,而MCU核心则是remote端。
使用RPMSG通信时,通常会从VirtIo-Ring缓冲区中申请一块buffer,然后将这个buffer作为共享内存,在两个核心之间传输数据。另外二者之间通信时,还需要中断通知对方有数据到达。以master向remote发数据为例,
1. Master Core 发送时,从 vring0中取得一块 buffer,再将消息按照 RPMsg 协议填充
2. 将处理好的内存 buffer 链接到vring1
3. 触发中断通知 Remote Core 有数据处理待处理
这个通信流程如下图所示
反过来也是一样的
在RK3568,可以选择软中断,或者mailbox实现这个中断,通常的做法是选择mailbox,这样还可以在触发中断时额外附带少量信息。
MCU端RPMSG适配
RK SDK里面提供了两个可选的rpmsg组件,分别是由NXP主导开发的rpmsg-lite,以及openamp项目下面的rpmsg。前者比较简单,不需要太多额外的组件,因此选择前者。其工程路径为
external/hal/middleware/rpmsg-lite
如果想要进一步了解rpmsg-lite,其原始的git项目地址为
原始的rpmsg-lite工程仅支持NXP的异构处理器,如imx6/imx7/imx8等,RK在此基础上增加了RK3562/RK3568/RK3588等处理器的支持。
不过RK3568仅支持AP运行这个rpmsg-lite,因此,这里我们要添加支持RK3568 MCU的相关源码。具体步骤如下
1、打开
external/hal/middleware/rpmsg-lite/lib/include/rpmsg_compiler.h,增加riscv处理器的MEM_BARRIER实现
RISCV处理器使用fence作为内存屏障指令,功能类似ARM的dsb指令。
2、屏蔽rpmsg-lite/lib/init/platform/RK3568/rpmsg_init.c中的功能,这个源文件中的API专门用于AP运行bare-metal时使用rpmsg-lite的,这里使用MCU,需要将其禁用
3、接下来修改
rpmsg-lite/lib/rpmsg_lite/porting/platform/RK3568/rpmsg_platform.c
文件。这个文件就是实现上述rpmsg的传输流程,即通过mbox触发中断,然后用virtio来传输数据。其中virtio传输数据的功能已经由rpmsg-lite实现了,这里只需要调用。
platform_init platform_deinit函数是用于平台初始化的,在rpmsg功能init和deinit的时候会调用这两个函数,保持跟原来的一致即可
platform_map_mem_region
platform_cache_all_flush_invalidate
platform_cache_disable
platform_vatopa
platform_patova
几个函数在MCU上没有特殊实现,保持跟原来的一致
platform_interrupt_enable
platform_interrupt_disable
两个函数用于开启和关闭mbox中断,RISCV和ARM中断控制器不同,需要将原来的GIC的API换成INTMUX的API。此外,此处要选择一个mbox通道,笔者选用ch3,后续AP Linux端也要配置同样的通道。
platform_notify函数为virtqueue的回调函数,用于在接收数据的时候产生一个中断。这里实现是要通过mbox应答AP,这样AP才认为收到了数据
platform_init_interrupt用于平台初始化中断,这里的实现则是初始化mbox,具体修改如下
platform_global_isr_disable
platform_global_isr_enable
用于开启和关闭全局中断,这里使用默认的实现即可
然后要定义mbox的通道信息
rpmsg_remote_cb函数属于rpmsg的回调,具体由上述的rpmsg_mbox_isr调用,这两个函数实现如下
修改完上面的rpmsg之后,还需要实现一个测试demo,对前一篇文章介绍的external/hal/project/rk3568-mcu目录做修改
1.增加mcu对rpmsg的共享内存区域的访问。rpmsg使用的共享内存区域由AP端Linux指定,作为预留内存。修改GCC目录下面的 Makefile,改用自定义的ld文件。这里同步打开rpmsg-lite的mk文件编译
这个ld文件可以从lib/CMSIS/Device/RK3568/Source/Templates/GCC复制一个过来,然后添加如下内容
2.修改testdemo,增加rpmsg测试功能
这个测试demo实现的功能是remote端收到数据后,就给master端发送一条固定内容的数据
先增加如下所示宏定义
然后添加一个名为 rpmsg_linux_test的测试函数
实现如下
这里使用rpmsg_lite_remote_init初始化,这个初始化会调用上述rpmsg_platform.c中实现的API,就是初始化mbox和中断
然后使用rpmsg_lite_wait_for_link_up,这个API会循环等待AP Linux端的mbox初始化完成才返回
然后使用rpmsg_ns_bind指定rpmsg回调函数为rpmsg_ns_cb。下面看下rpmsg_ns_cb的实现,在进入此回调之后,同时会向master端,也就是AP Linux端发送一条数据
然后使用rpmsg_lite_send发送一条数据给master端,发起第一次通信
到这里,mcu端的修改就完成了,按照上一篇文章介绍的方法编译,下载到板上。
AP端RPMSG适配
AP端的Linux上已经有完整的rpmsg框架了,这里只需要修改下配置即可。首先在defconfig文件中打开如下内容,这里将支持mbox触发rpmsg相关中断的源码编译进内核,将测试demo编译为模块
然后在设备树打开rpmsg的配置,注意rpmsg-tx的通道需要为3,跟上面MCU配置的一样
完成这些配置之后,编译出内核,然后烧录到板上,同时将kernel/drivers/rpmsg/ rockchip_rpmsg_test.ko通过adb/ssh等方式推到板上。
MCU与AP rpmsg通信演示
按上面的步骤烧录amp和kernel后,重启板子,可以看到MCU端串口有如下打印信息
AP端的dmesg log有如下信息
然后在AP端加载 rockchip_rpmsg_test.ko
可以看到这个信息跟源码里面配置的是一致的
说明MCU确实有收到AP的数据,并且发送的数据能被AP正确接收
总结
本文介绍RK3568内置的MCU与AP之间的通信功能,MCU端使用rpmsg-lite组件,AP端使用Linux自带的rpmsg框架,中断依靠mbox实现,此种方式可以在异构核之间可靠传输大量数据。
来源:啊将登胡