深入芯驰D9360核间通信案例,RPMSG关键技术深度剖析

[复制链接]
2786|1
gztwdz4379 发表于 2025-9-10 10:24 | 显示全部楼层 |阅读模式
技术, , , ,
本帖最后由 gztwdz4379 于 2025-9-10 10:30 编辑

前言:

在多核异构架构成为工业SoC主流方案的当下,芯驰D9360凭借其Cortex-A55与Cortex-R5的协同设计,在需同时处理复杂应用与实时任务的场景中表现突出。本文将以眺望电子Core-D9360平台为例,详解如何利用RPMSG与VirtIO机制实现A核与R核间的可靠通信,并提供关键代码实现与调试方法。



图 1 Core-D9360 核心板


一、通信基础:RPMSG框架


RPMSG(Remote Processor Messaging)是Linux内核中用于处理器间通信的框架,采用virtio + rpmsg + IPCC三层架构实现:

●virtio:提供虚拟化队列管理

●rpmsg:实现消息封装与路由

●IPCC:硬件中断控制器


在Core-D9360核心板中,A55核运行Linux,R5核运行RTOS,两者通过虚拟通道(VirtIO ring buffer)进行数据传输,支持双向、异步、多通道通信。


图 2  核间通讯架构

二、核心代码实现


2.1 添加例程驱动

RTOS系统的例程添加在SDK/source/ssdk/examples/kunlun/drivers下,分别创建Kconfig和rules.mk文件,其中Kconfig文件内容如下:


rules.mk文件内容如下:


返回上一层目录下,在Kconfig下添加talowe驱动模块的初始状态和图形界面类型,添加内容如下:


在rules.mk文件下,添加如下内容:


MODULES += $(LOCAL_DIR)/talowe: 如果配置选项 CONFIG_TALOWE_TEST为 "y",则会将$(LOCAL_DIR)/talowe添加到变量MODULES中。当启用了"talowe test"功能时,会将$(LOCAL_DIR)/talowe模块添加到模块列表中。

2.2 R5核发送机制实现

将rpmsg目录下的rpmsg_test.c文件复制到talowe.c文件中,该驱动描述如何通过RPMSG协议进行核间通信。我们可以在此程序基础上加入自己的协议。

以下是R5核发送"Talowe:RtoA"字符串到A55核,并打印出接收到的信息的函数。

  1. <font color="#000000" face="微软雅黑" size="3">void send_receive_string(rpmsg_channel_t channel, const char *message) {
  2. int max_payload;
  3. struct dcf_ccm_hdr *snd_pkg;
  4. struct dcf_ccm_hdr *rcv_pkg;
  5. status_t ret;

  6.         max_payload = rpmsg_channel_max_payload(channel);
  7.         snd_pkg = osAlloc(max_payload);
  8.         rcv_pkg = osAlloc(max_payload);
  9. ASSERT(snd_pkg && rcv_pkg);

  10. memset(snd_pkg, 0x0, max_payload);
  11. memset(rcv_pkg, 0x0, max_payload);

  12.         snd_pkg->dmsg.msg_type = COMM_MSG_CCM_ECHO;
  13.         snd_pkg->dmsg.opflags |= DCF_MSGF_TMS;
  14.         snd_pkg->dmsg.msg_len = sizeof(struct dcf_ccm_hdr) - sizeof(struct dcf_message);

  15. strncpy(snd_pkg->data, message, max_payload - sizeof(struct dcf_ccm_hdr));

  16.         ret = rpmsg_channel_send(channel, RPMSG_ECHO_EPT_ADDR, snd_pkg, max_payload, 3000);
  17. if (ret == RPMSG_SUCCESS) {
  18. printf("Sent: %s\n", message);
  19.         } else {
  20. printf("Failed to send message\n");
  21.         }
  22. int received_len;
  23.         ret = rpmsg_channel_recv(channel, rcv_pkg, max_payload, &received_len, 3000);
  24. if (ret == RPMSG_SUCCESS) {
  25. printf("Received: %s\n", rcv_pkg->data);
  26.         } else {
  27. printf("Failed to receive message\n");
  28.         }
  29. osFree(snd_pkg);
  30. osFree(rcv_pkg);
  31. }</font>

将以上函数添加至talowe.c文件内,并在主函数rpmsg_test编写调用该函数条件,如下:

  1. <font color="#000000" face="微软雅黑" size="3">const char* message_to_send = "Talowe:RtoA";

  2. static int rpmsg_test(int argc, char *argv[])
  3. {
  4.     int test_case = -1;
  5.     int type, rproc, times;
  6.     ......
  7.     else if (!strcmp(argv[0], "perf")) {
  8.         test_case = 3;
  9.         type = atoi(argv[1]);
  10.         rproc = atoi(argv[2]);
  11.         times = atoi(argv[3]);
  12.     }
  13.     else if(!strcmp(argv[0], "send")) {
  14.                 test_case = 4;
  15.                 type = atoi(argv[1]);
  16.                 rproc = atoi(argv[2]);
  17.                 times = atoi(argv[3]);
  18.     }
  19.     else {
  20.         printf("Unknown cmd %s\n", argv[0]);
  21.         goto exit;
  22.     }
  23.    ......
  24.    ......
  25.         case 3:
  26.             do_rpmsg_perf_test(channel, times);
  27.         break;

  28.         case 4:
  29.                      send_receive_string(channel, message_to_send);
  30. break;

  31.         default:
  32.             printf("Unknown case %d\n", test_case);
  33.         break;
  34.     }
  35. </font>
     加入串口打印提示:
  1. <font color="#000000" face="微软雅黑" size="3">static void rpmsg_test_show(void)
  2. {
  3.     printf("\nList rpmsg communicate with remote:\n");
  4.     printf("\tType\t\t\tRemote-Proc\n");
  5.     ......
  6.     ......
  7.     printf("\trpmsg_test perf <type> <remote-proc> <times>\n");
  8.     printf("\trpmsg_test send <type> <remote-proc> <times>\n");

  9.     printf("\ne.g: test ping rpmsg.virtio 10 times with secure, use command:\n");
  10.     printf("\trpmsg_test ping 0 1 10\n");
  11. }</font>

编辑如下文件:SDK/source/ssdk/ middleware/rpmsg_service/rpmsg_echo.c

  1. <font color="#000000" face="微软雅黑" size="3">static int echo_channel_cb(void *data, int len, unsigned long src, void *arg)

  2. {
  3.     rpmsg_channel_t chan = arg;
  4.     struct dcf_ccm_hdr *ccm_pkg = data;
  5. const char *char_data = (char *)data;
  6.     int ret = 0;

  7.     if (ccm_pkg->dmsg.msg_type == COMM_MSG_CCM_ECHO) {
  8.         ccm_pkg->time[2] = timer_get_current_time(g_syscnt_timer) * g_sdrv_syscnt_dev.cnt_per_us;
  9.         ret = rpmsg_channel_send(chan, src, data, len, 1000);
  10.         printf("Sending data (length %d): ", len);
  11. for (int i = 8; i < len; i++) {
  12. printf("%c ", char_data[i]);
  13.                 }
  14. printf("\n");
  15.     }
  16.     else if (ccm_pkg->dmsg.msg_type == COMM_MSG_CCM_ACK) {
  17.         ret = rpmsg_channel_send(chan, src, (char *)"ACK", 4, 1000);
  18.         ssdk_printf(SSDK_INFO,"send ACK\n");
  19.     }
  20.     else {
  21.         ssdk_printf(SSDK_WARNING, "echo_channel_cb: unknown %d bytes from addr %ld\n", len, src);
  22.     }

  23.     if (ret != 0) {
  24.         ssdk_printf(SSDK_WARNING, "echo_channel_cb: channel send failed\n");
  25.     }

  26.     return ret;
  27. }</font>

通过以下指令打开图形化配置界面,进入到Driver and Application Examples->Driver Example Support下,可以看到我们新加的talowe test Support驱动配置选项,需要关闭 RPMSG Example Application Support 选项,如下:


  1. <font color="#000000" face="微软雅黑" size="3">./tools/menuconfig.sh -b d9360_ref -p ref -c secure</font>



2.3 A55核发送数据到R5核

A55核Linux系统通过echo_test命令与R5核进行通讯,以下是该命令源码的文件位置:


自定义一个发送和接收字符串的函数,如下:

  1. <font color="#000000" face="微软雅黑" size="3">
  2. int send_receive_string(int fd, const char *send_str,int ntimes, int seconds) {
  3. int i = 0, j = 0;
  4. int size, bytes_rcvd, bytes_sent;
  5. long elapse = 0;
  6.         err_cnt = 0;
  7.         i_payload = (struct _payload *)malloc(sizeof(struct _payload) + payload_max_size);
  8.         r_payload = (struct _payload *)malloc(sizeof(struct _payload) + payload_max_size);
  9. if (i_payload == 0 || r_payload == 0) {
  10. printf("ERROR: Failed to allocate memory for payload.\n");
  11. return -1;
  12.         }
  13. if (seconds)
  14. gettimeofday(&start_test, NULL);

  15.         i_payload->magic = 0xA5;
  16. for (j = 0; j < ntimes; j++) {
  17.                 i_payload->num = i;
  18.                 i_payload->size = size;
  19. strcpy(i_payload->data, send_str);
  20. if (verbose)
  21. printf("\r\n sending payload number");
  22. if (verbose)
  23. printf(" %d of size %lu\r\n", i_payload->num, (sizeof(struct _payload)) + strlen(i_payload->data));
  24.                 bytes_sent = write(fd, i_payload, sizeof(struct _payload) + strlen(i_payload->data));
  25. if (bytes_sent <= 0) {
  26. if (verbose) {
  27. perror("\r\n Error sending data\n");
  28. break;
  29.                         } else
  30. fprintf(stderr, "#");
  31.                         err_cnt++;
  32. continue;
  33.                 }
  34. printf("send string:%s\n",i_payload->data);
  35.         r_payload->num = 0;
  36.         bytes_rcvd = read(fd, r_payload, sizeof(struct _payload) + payload_max_size);
  37. while (bytes_rcvd <= 0) {
  38. usleep(10000);
  39.                 bytes_rcvd = read(fd, r_payload, sizeof(struct _payload) + payload_max_size);
  40.         }
  41.         r_payload->data[bytes_rcvd] = '\0';
  42. printf("receive string:%s\n",r_payload->data);

  43. if (interval)
  44. sleep(interval);
  45.         }
  46. free(i_payload);
  47. free(r_payload);
  48. return 0;
  49. }</font>

在main函数内添加新增函数条件,执行指令时实现A55核发送"Talowe:AtoR"字符串到R5核:

  1. <font color="#000000" face="微软雅黑" size="3">
  2. int main(int argc, char *argv[])
  3. {
  4.     int opt;
  5.     char *rpmsg_dev = RPMSG_DEVICE_NAME;
  6.     ......
  7.     int test_second = 0;
  8.     int send_char = 0;
  9.     char *value_c;

  10.     if (strstr(argv[0], "property")) {
  11.         do_property_test(argc, argv);
  12.         return 0;
  13.     }
  14. ......
  15.     while ((opt = getopt(argc, argv, "a:d:n:cbpxvst:hi:lm:S:P:")) != -1) {
  16.         switch (opt) {
  17.            ......
  18.             case 'v':
  19.                 verbose = 1;
  20.                 break;

  21.             case 'c':
  22.                                 send_char = 1;
  23. break;

  24.             case 'b':
  25.                 benchmark = 1;
  26.                 break;
  27.             ......
  28.             default:
  29.                 printf("getopt return unsupported option: -%c\n", opt);
  30.                 usage("echo_test");
  31.                 break;
  32.         }
  33.     }
  34.     else if (benchmark == 2) {
  35.         iperf_test(fd, ntimes);
  36.     } else if (benchmark == 3) {

  37.         throughput_test(fd, ntimes, test_second);

  38.     } else if( send_char == 1){
  39. const char *message_to_send = "Talowe:AtoR";
  40. int result = send_receive_string(fd, message_to_send,ntimes, test_second);
  41. if (result < 0) {
  42. printf("Error sending/receiving string.\n");
  43.                 }
  44.     }
  45.     else {
  46.         if (test_second) {
  47.             ntimes = 0x7fffffff;
  48.         }
  49. ......
  50.     return 0;
  51. }
  52. 在打印列表添加如下提示:

  53. static void usage(const char *cmd)
  54. {
  55. printf("This is s rpmsg echo test %s, MTU=%d\n", ECHO_TEST_VERSION, rpmsg_payload_test);
  56. ......
  57.     printf("%s -v\t\t: Verbose mode\n", cmd);
  58.     printf("%s -c [char]\t: send Talowe:AtoR\n", cmd);
  59.            ......
  60.     printf("echo_test -d soc:ipcc@0.ipcc-echo.-1.30 -x -t 10   ; Throughput test in average (Ack'd) in 10 seconds (print in 1 second)\n");

  61.     exit(0);
  62. }</font>

编辑如下文件:SDK/source/linux/drivers/rpmsg/virtio_rpmsg_bus.c

  1. <font color="#000000" face="微软雅黑" size="3">static int rpmsg_echo_cb(struct rpmsg_device *rpdev, void *data, int len,

  2.                        void *priv, u32 src)
  3. {
  4. struct dcf_message *msg = data;
  5. struct virtproc_info *vrp = priv;
  6.         ......
  7. switch (msg->msg_type) {
  8. case COMM_MSG_CCM_ECHO:
  9.                 dev_err(dev,"virtio send susses");
  10. /* Add timestamp in the time[2] */
  11.                 ......
  12. break;
  13. case COMM_MSG_CCM_ACK:
  14.                 dev_err(dev,"rpmsg recv ACK");
  15.                 err = __send_offchannel_raw(vrp, RPMSG_ECHO_ADDR, src, "ACK", 4, true);
  16.                 ......
  17. break;
  18. default:
  19. /* No more action, just drop the packet */
  20. break;
  21.         }
  22. return 0;
  23. }</font>

编辑如下文件:SDK/source/linux/drivers/rpmsg/semidrive_ipcc.c

  1. <font color="#000000" face="微软雅黑" size="3">


  2. static int rpmsg_ipcc_echo_cb(struct rpmsg_device *rpdev, void *data, int len,

  3.                               void *priv, u32 src)
  4. {
  5. struct rpmsg_ipcc_device *vrp = priv;
  6. struct device *dev = vrp->dev;
  7. ......
  8. switch (dmsg->msg_type) {
  9. case COMM_MSG_CCM_ECHO:
  10.                 dev_err(dev,"ipcc send susses");
  11. /* Add timestamp in the time[2] */
  12.             ......
  13. break;
  14. case COMM_MSG_CCM_ACK:
  15.                 dev_err(dev,"ipcc recv ACK");
  16.                 err = __send_offchannel_raw(vrp, RPMSG_ECHO_ADDR, src, "ACK", 4,true);
  17.         ......
  18. break;
  19. default:
  20. /* No more action, just drop the packet */
  21. break;
  22.         }
  23. return 0;
  24. }</font>

内核需打开以下配置:

  1. <font color="#000000" face="微软雅黑" size="3">CONFIG_RPMSG=y
  2. CONFIG_RPMSG_CHAR=y
  3. CONFIG_RPMSG_VIRTIO=y
  4. CONFIG_RPMSG_SEMIDRIVE=y</font>



三、烧写测试验证


3.1 R5核向A55核发送信息

以下测试内容为R5核发送"Talowe:RtoA"字符串到A55核,A55核接收到字符串之后重新发送给R5核。

R5核:

  1. <font color="#000000" face="微软雅黑" size="3">rpmsg_test send 0 3 1</font>



A55核:


3.2 A55核向R5发送信息

以下测试内容为A55核发送"Talowe:AtoR"字符串到R5核,R5核接收到字符串之后重新发送给A55核。

A55核:

  1. <font color="#000000" face="微软雅黑" size="3">echo_test -d virtio0.rpmsg-echo.-1.30 -c 1</font>



R5核:




四、结语


通过RPMsg在单芯片上实现了这种高效的核间通信机制,使得异构多核SoC能够真正发挥"实时控制+高性能计算"的协同优势,成为工业4.0、自动驾驶、AIoT等领域的核心技术底座。

广州眺望电子科技有限公司推出Core-D9360核心板与EVM-D9载板外,还提供完整的SDK、编译指南、测试手册及技术文档,覆盖从环境搭建、代码编译、镜像烧写到功能验证的全流程,助力开发者快速实现产品化。

如果您希望获取完整核心板开发资料与产品价格,欢迎关注我们的公众号或官方淘宝店铺,联系获取!


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
yangjiaxu 发表于 2025-9-28 10:16 | 显示全部楼层
这个方案一般应用在哪儿?什么场景啊?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

13

主题

16

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部