睿擎平台AMP共享内存:Linux 与 RT-Thread 高速数据交换

描述

在 AMP(Asymmetric Multi-Processing,非对称多处理)混合部署架构中,Linux 和 RT-Thread 运行在同一颗芯片的两个不同核心上,如何高效地在两个系统之间传递大数据,是一个核心问题。


 

上一篇文章介绍了 DSMC 总线方案,适合与 FPGA 等外部设备高速通信。今天我们聚焦另一个场景——睿擎平台 AMP 共享内存(SHM)通信,介绍 RC3562 平台如何利用芯片内置的共享内存机制,实现 Linux 与 RT-Thread 之间的高速数据交换,并提供完整示例代码和实测数据。


 


 

一、为什么需要共享内存通信?

在 AMP 混合部署中,Linux 和 RT-Thread 各司其职:

Linux 负责复杂的人机交互、网络通信、文件系统等

RT-Thread 负责实时控制、电机驱动、传感器采集等


 

两者之间需要频繁交换数据:

Linux


 

传统方案(如 RPMSG)基于消息队列,适合小数据量、事件驱动的通信场景。但当需要传递大数组、实时流数据时,消息队列的多次拷贝会带来显著延迟和 CPU 开销。


 

共享内存(SHM) 的核心思想是:划定一块物理内存,两个核心都能直接访问,数据无需拷贝,延迟可降至微秒级,是 AMP 双系统大数据量通信的终极方案。


 

二、共享内存原理


 

2.1 芯片级共享内存架构

睿擎平台的 RK3562 和 RK3506 芯片在内部 SRAM 区域预留了一块共享内存区域,两个核心都可以直接访问。共享内存区域布局:

Linux
 

关键参数:

● 地址:两块核心映射到同一物理地址 0xc000000,无需额外地址转换

● 大小:2MB(0x200000),适合中等体量的数据交换

 通知机制:通过 Mailbox 硬件发送中断,告知对方数据已就绪

● 缓存同步:Linux 使用 msync,RT-Thread 使用 AMP_SHM_IOCTL_FLUSH_CACHE / AMP_SHM_IOCTL_INVALID_CACHE


 

三、Linux 侧示例代码

Linux 侧应用程序通过标准 POSIX 接口访问共享内存设备 /dev/amp_shm,核心操作为:open → ioctl → mmap → 读写数据 → msync → munmap。

  •  

#include #include #include #include #include #include #include #include #include  #define AMP_SHM_IOCTL_BASE      'A'#define AMP_SHM_IOCTL_KICK      _IO(AMP_SHM_IOCTL_BASE, 1)#define AMP_SHM_IOCTL_GET_SIZE  _IOR(AMP_SHM_IOCTL_BASE, 2, size_t) int main(int argc, char **argv){    const char *dev_name = argv[1];    int fd = open(dev_name, O_RDWR);    if (fd < 0) { perror("open"); return -1; }     /* ========== 发送通知(Linux → RT-Thread)========== */    if (!strcmp(argv[2], "send_notify")) {        ioctl(fd, AMP_SHM_IOCTL_KICK);        printf("send a notify.\n");    }     /* ========== 等待通知(Linux 阻塞等待 RT-Thread 发来的中断)========== */    if (!strcmp(argv[2], "wait_notify")) {        struct pollfd pfd = { .fd = fd, .events = POLLIN };        poll(&pfd, 1, -1);        printf("receive a notify.\n");    }     /* ========== 写入数据(Linux → RT-Thread)========== */    if (!strcmp(argv[2], "write") && argc == 5) {        uint32_t size;        ioctl(fd, AMP_SHM_IOCTL_GET_SIZE, &size);        uint8_t *vaddr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);        uint32_t offset = (uint32_t)strtoul(argv[3], NULL, 0);        const char *str = argv[4];         memcpy(vaddr + offset, str, strlen(str));        msync(vaddr + offset, strlen(str), MS_SYNC);        munmap(vaddr, size);    }     /* ========== 读取数据(Linux ← RT-Thread)========== */    if (!strcmp(argv[2], "read") && argc == 5) {        uint32_t size;        ioctl(fd, AMP_SHM_IOCTL_GET_SIZE, &size);        uint8_t *vaddr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);        uint32_t offset = (uint32_t)strtoul(argv[3], NULL, 0);        uint32_t len = (uint32_t)strtoul(argv[4], NULL, 0);         msync(vaddr + offset, len, MS_INVALIDATE);        for (uint32_t i = 0; i < len; i++) {            printf("[%02u] = %02x\n", i, *(vaddr + offset + i));        }        munmap(vaddr, size);    }     close(fd);    return 0;}

编译方式(使用 SDK 构建系统):

  •  

# 使用 SDK 提供的 cmake 配置交叉编译cd /path/to/shm_demomkdir build && cd buildcmake .. -DTOOLCHAIN_ROOT=/path/to/toolchainmake# 产出:amp_shm_device_app(部署到开发板 Linux 文件系统)

四、RT-Thread 侧示例代码

RT-Thread 侧通过设备框架(RT-Device)访问共享内存,设备名为 amp-shm。MSH 命令 amp_shm_test 提供与 Linux 侧对应的所有操作。

  •  

#include #include  #define AMP_SHM_IOCTL_GET_INFO          0x40#define AMP_SHM_IOCTL_KICK             0x41#define AMP_SHM_IOCTL_WAIT             0x42#define AMP_SHM_IOCTL_MAP              0x43#define AMP_SHM_IOCTL_UNMAP            0x44#define AMP_SHM_IOCTL_FLUSH_CACHE      0x45#define AMP_SHM_IOCTL_INVALID_CACHE     0x46 struct amp_shm_args {    void *vaddr;    rt_uint32_t size;    rt_uint32_t cache_offset;    rt_uint32_t cache_len;}; static int amp_shm_test(int argc, char **argv){    rt_device_t dev = rt_device_find(argv[1]);    struct amp_shm_args args = { 0 };    rt_device_open(dev, 0);     /* ========== 发送通知(RT-Thread → Linux)========== */    if (!strcmp(argv[2], "send_notify")) {        rt_device_control(dev, AMP_SHM_IOCTL_KICK, RT_NULL);    }     /* ========== 等待通知(RT-Thread 阻塞等待 Linux 发来的中断)========== */    if (!strcmp(argv[2], "wait_notify")) {        rt_device_control(dev, AMP_SHM_IOCTL_WAIT, RT_NULL);    }     /* ========== 写入数据(RT-Thread → Linux)========== */    if (!strcmp(argv[2], "write") && argc == 5) {        rt_device_control(dev, AMP_SHM_IOCTL_GET_INFO, &args);        rt_device_control(dev, AMP_SHM_IOCTL_MAP, &args);         rt_uint8_t *vaddr = (rt_uint8_t *)args.vaddr;        rt_memcpy(vaddr + atol(argv[3]), argv[4], rt_strlen(argv[4]));         /* 刷新缓存,确保数据写回物理内存 */        args.cache_offset = atol(argv[3]);        args.cache_len = rt_strlen(argv[4]);        rt_device_control(dev, AMP_SHM_IOCTL_FLUSH_CACHE, &args);        rt_device_control(dev, AMP_SHM_IOCTL_UNMAP, &args);    }     /* ========== 读取数据(RT-Thread ← Linux)========== */    if (!strcmp(argv[2], "read") && argc == 5) {        rt_device_control(dev, AMP_SHM_IOCTL_GET_INFO, &args);        rt_device_control(dev, AMP_SHM_IOCTL_MAP, &args);         rt_uint8_t *vaddr = (rt_uint8_t *)args.vaddr;        rt_uint32_t offset = atol(argv[3]);        rt_uint32_t len = atol(argv[4]);         /* 使 CPU 缓存失效,从物理内存读取最新数据 */        args.cache_offset = offset;        args.cache_len = len;        rt_device_control(dev, AMP_SHM_IOCTL_INVALID_CACHE, &args);         for (int i = 0; i < len; i++) {            rt_kprintf("[%2d] = %02x\n", i, *(vaddr + offset + i));        }        rt_device_control(dev, AMP_SHM_IOCTL_UNMAP, &args);    }     return 0;} MSH_CMD_EXPORT(amp_shm_test, amp shm test);

五、API 对比:Linux vs RT-Thread

Linux


 

六、运行测试

6.1 中断通知测试

Linux 发通知,RT-Thread 等待

  •  
  •  
  •  

# RT-Thread 串口终端 - 阻塞等待 Linux 发来的中断msh />amp_shm_test amp-shm wait_notify # Linux 串口终端 - 发送通知到 RT-Threadroot@rc3562:/# amp_shm_device_app /dev/amp_shm send_notifysend a notify.

RT-Thread 发通知,Linux 等待

  •  
  •  
  •  
  •  
  •  

# Linux 串口终端 - 阻塞等待 RT-Thread 发来的中断root@rc3562:/# amp_shm_device_app /dev/amp_shm wait_notify # RT-Thread 串口终端 - 发送通知到 Linuxmsh />amp_shm_test amp-shm send_notify


 

6.2 数据读写测试

Linux 写数据,RT-Thread 读取

  •  

# Linux 侧 - 向共享内存偏移 0 处写入 "hello"root@rc3562:/# amp_shm_device_app /dev/amp_shm write 0 "hello" # RT-Thread 侧 - 从共享内存偏移 0 处读取 16 字节msh />amp_shm_test amp-shm read 0 16[ 0] = 68 (h)[ 1] = 65 (e)[ 2] = 6c (l)[ 3] = 6c (l)[ 4] = 6f (o)...(后续字节为 0x00)

RT-Thread 写数据,Linux 读取

  •  

# RT-Thread 侧 - 向共享内存偏移 0 处写入 "world"msh />amp_shm_test amp-shm write 0 "world" # Linux 侧 - 从共享内存偏移 0 处读取 16 字节root@rc3562:/# amp_shm_device_app /dev/amp_shm read 0 16[00] = 77 (w)[01] = 6f (o)[02] = 72 (r)[03] = 6c (l)[04] = 64 (d)[05] = 00 (.)…

七、应用场景

7.1 工业机器人控制

Linux


 

Linux 侧做运动学规划和视觉处理,通过共享内存将目标轨迹高速下发到 RT-Thread;RT-Thread 侧控制电机,通过共享内存回传编码器位置数据。


 

7.2 高速传感器采集

Linux


 

RT-Thread 侧以 100kHz 采样率采集多路传感器数据,通过共享内存以 10+ MB/s 的速度传输到 Linux 侧做存储和分析。


 

八、总结

本文基于实际 SDK 源码,完整介绍了睿擎平台 AMP 共享内存通信机制:

理解共享内存在 AMP 架构中的定位和优势(2MB SRAM,Mailbox 中断通知)

掌握 Linux 侧 POSIX 接口(open/mmap/msync)使用方法

掌握 RT-Thread 侧设备框架接口(rt_device_* + IOCTL)使用方法

理解缓存同步机制(FLUSH_CACHE / INVALID_CACHE)


 

共享内存是睿擎平台 AMP 双系统通信的性能天花板,适合对实时性和带宽有极致要求的工业场景。结合 DSMC(外部设备高速通信)和 RPMSG(控制命令传输),睿擎平台提供了完整的 AMP 通信解决方案,开发者可以根据场景灵活选择。


 

相关文档:

● 睿擎平台开发文档

● RuiChing Studio 下载

示例工程:

● Linux 侧:shm_demo/amp_shm_device_app.c

● RT-Thread 侧:

08_misc_amp_factory_default/applications/amp_shm_device_app.c


 

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分