前言
在C6678中,IPC通常指的是处理器间通信(Inter-Processor Communication)。在多核DSP系统中,IPC机制允许不同处理器核心之间交换数据并同步其操作,这对于实现高效的并行处理非常重要。本篇博客主要介绍使用共享内存的方式来使用多核IPC。
一、IPC是什么?
IPC是一个包含软件包的组件,它被设计为允许在多处理器环境中的处理器之间的通信和与外设之间的通信。此通信包括消息传递、流和链接表。它们在单处理器和多处理器配置中都能透明地工作。
IPC被设计用于运行SYS/BIOS应用程序的处理器。这通常是一个DSP,但在某些情况下可能是一个ARM设备。以前的SYS/BIOS版本被称为DSP/BIOS。这个新名称反映了该操作系统也可以在dsp以外的处理器上使用。
通过IPC模块可以进行多核核间,甚至跨dsp之间的通信。
二、配置步骤
本案例使用的ipc是多核间的通信,可以多核公用一个工程,也可以分别配置编写两个工程,本案例配置的是两个工程。两个工程的cfg配置文件是相同的,
1.整体IPC配置流程
先为两个配置cfg文件,cfg文件基本一样,然后分别按照如上流程图进行编码。
2.cfg文件
首先需要启动Ipc模块
Ipc = xdc.useModule('ti.sdo.ipc.Ipc');
默认IPC_Start 不会主动去attch所有的其他核,默认配置参数为Ipc.ProcSync_PAIR,若设置为Ipc.ProcSync_ALL,Ipc_start()API将自动附加到并同步所有远程处理器。如果使用此选项,则应用程序永远不应该调用Ipc_attach()。可以通过如下修改:
Ipc.procSync = Ipc.ProcSync_ALL;
本案例中使用的是默认参数Ipc.ProcSync_PAIR。
配置Message使用共享内存,如下代码所示,主要几个关键参数已做注释
var SharedRegion = xdc.useModule('ti.sdo.ipc.SharedRegion');
SharedRegion.translate = false;
SharedRegion.setEntryMeta(0, //共享内存ID号
{ base: Program.global.shmBase, //共享内存地址
len: Program.global.shmSize, //共享内存大小
ownerProcId: 0, //共享内存拥有者id
isValid: true,
cacheEnable: true,
cacheLineSize: 128,
name: "internal_shared_mem",
});
配置了该共享内存后,可以通过如下向共享内存区域申请内存:
buf = Memory_alloc(SharedRegion_getHeap(0), sizeof(Tester) * COUNT, 128, NULL);
多核MultiProc配置,通过setConfig可以配置使用的核数量:
var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc');
MultiProc.setConfig(null, ["CORE0", "CORE1"]);
编写ipc代码时候主要头文件如下
/* IPC module Headers */
#include <ti/ipc/MessageQ.h>
#include <ti/ipc/MultiProc.h>
#include <ti/ipc/SharedRegion.h>
#include <ti/ipc/Notify.h>
#include <ti/ipc/Ipc.h>
3.接收方配置
首先根据需要定义通信之间的数据结构体,必须将MessageQ_MsgHeader 作为第一个成员,发送和接收时不需要修改其内容,ipc模块会使用该成员。
typedef struct {
MessageQ_MsgHeader header; /* 32 bytes */
int src;
int flags;
int numMsgs;
int seqNum;
int heartbeat;
}CoreMsg;
第一步,先调用Ipc_start启动ipc模块,连接共享内存区拥有者,这一步在BIOS_Start之前执行
int InitIpc(void)
{
SharedRegion_Entry entry;
int status;
/* Call Ipc_start() */
status = Ipc_start(); // 启动ipc,默认参数下不会阻塞
if (status < 0) {
printf("Ipc_start failed!\n");
return -1;
}
/* get region 0 information */
SharedRegion_getEntry(0, &entry); //获取共享内存区域的句柄
/* if entry is not valid then return */
if (entry.isValid == FALSE) {
printf("entry is not valid failed!\n");
return -1;
}
/* Must attach to owner first to get default GateMP */
if (MultiProc_self() != entry.ownerProcId) { // 本身是拥有者则跳过
do {
status = Ipc_attach(entry.ownerProcId); // 连接共享内存区域的拥有者
} while (status < 0);
}
return 0;
}
第二步,发送和接收方互相attach,必须先attach核号小的那个,即核0和核1相互attach的话,必须核0先attach核1,此时程序会进入阻塞状态:
int AttachCore(int coreNum)
{
SharedRegion_Entry entry;
Int status;
SharedRegion_getEntry(0, &entry);
if ((coreNum == MultiProc_self()) ||
(coreNum == entry.ownerProcId)) {
return -1;
}
if (Notify_numIntLines(coreNum) == 0) {
return -1;
}
// 前面的处理均是为了跳过已经连接的核,因为可能已经连接过拥有者
/* call Ipc_attach for every remote processor */
do {
status = Ipc_attach(coreNum); // 阻塞等待所有核连接完毕
} while (status < 0);
return 0;
}
第四步,接收方连接注册共享内存区域
int RegisterMem(int heapid)
{
MessageQ_registerHeap((IHeap_Handle)SharedRegion_getHeap(0), heapid);
return 0;
}
第五步,接收方创建消息队列,创建只需要指定队列名字即可,返回一个消息句柄,get消息的时候通过这个句柄来获取消息,创建之后,接收方即可直接进入MessageQ_get,阻塞等待,也可以设置为非阻塞,通过返回的错误码判断
int MessageCreate(void)
{
messageQ = MessageQ_create(QueueName[MultiProc_self()], NULL);
if (messageQ == NULL) {
System_abort("MessageQ_create failed\n" );
return -1;
}
return 0;
}
第六步,接收方阻塞等待消息
int MessageGet(MessageQ_Handle handle, MessageQ_Msg *msg)
{
int32_t status;
status = MessageQ_get(handle, msg, MessageQ_FOREVER);
if (status < 0) {
printf("MessageQ_get failed\n");
return status;
}
return status;
}
第七步,消息接收到后,读取消息,释放消息占用的内存。如果接收方也需要返回消息,那么可以将该消息内存作为返回的消息内存继续使用,不释放。
int MessageFree(MessageQ_Msg msg)
{
int ret;
ret = MessageQ_free(msg);
if (ret == MessageQ_E_FAIL) {
printf("MessageQ_alloc failed\n");
return ret;
}
return ret;
}
以上,即为消息接收方的配置流程,将上面的流程调用编写为一个任务,并启动BIOS_start如下所示:
int main()
{
Task_Params taskParams;
InitIpc();
Task_Params_init(&taskParams);
taskParams.priority = 1;
tsk1 = Task_create (task1, &taskParams, NULL);
BIOS_start(); /* does not return */
return(0);
}
void task1(UArg arg0, UArg arg1)
{
CoreMsg *msg;
AttachCore(1);
RegisterMem(0);
MessageCreate();
for (;;) {
printf("Running core0 function\n");
MessageGet(messageQ, (MessageQ_Msg*)&msg);
printf("src:%d, flags:%d, numMsgs:%d, seqNum:%d, heartbeat:%d \n", msg->src, msg->flags, msg->numMsgs, msg->seqNum, msg->heartbeat);
MessageFree((MessageQ_Msg)msg);
Task_sleep(1000);
}
}
4.发送方配置
(同接收方)第一步,先调用Ipc_start启动ipc模块,连接共享内存区拥有者,这一步在BIOS_Start之前执行;
(同接收方)第二步,送和接收方互相attach,必须先attach核号小的那个,即核0和核1相互attach的话,必须核0先attach核1,此时程序会进入阻塞状态:;
第三步,发送方打开接收方的消息队列,打开所需的参数是队列名字(与接收方创建的名字相同即可),打开后会返回一个队列id,通过这个id进行发送的操作;
int MessageOpen(int corenum, MessageQ_QueueId *QueueId)
{
int32_t status;
do {
status = MessageQ_open(QueueName[corenum], QueueId);
Task_yield();
}while (status < 0);
return 0;
}
第四步,发送方向共享内存堆申请消息内存区域,MessageQ_alloc(HEAP_ID,MESSAGE_SIZE_IN_BYTES)两个参数分别是堆的id号和申请的大小;
CoreMsg * MessageAlloc(int headid)
{
CoreMsg *msg;
msg = (CoreMsg *)MessageQ_alloc(headid, MESSAGE_SIZE_IN_BYTES);
if (msg == NULL) {
printf("MessageQ_alloc failed\n");
return NULL;
}
return msg;
}
第五步,填充发送的msg,通过MessageQ_put,发送出去,需要指定发送的队列id号和msg,发送方可以调用MessageQ_close结束本次发送。
int MessagePut(MessageQ_QueueId QueueId, MessageQ_Msg msg)
{
int32_t status;
status = MessageQ_put(QueueId, msg);
if (status < 0) {
printf("MessageQ_put failed\n");
return status;
}
return status;
}
最终,根据以上调用顺序,编写任务函数,启动BIOS_start;
int main()
{
Task_Params taskParams;
InitIpc();
Task_Params_init(&taskParams);
taskParams.priority = 1;
tsk1 = Task_create (task1, &taskParams, NULL);
BIOS_start(); /* does not return */
return(0);
}
void task1(UArg arg0, UArg arg1)
{
CoreMsg *msg;
int i = 0;
MessageQ_QueueId QueueId;
AttachCore(0);
RegisterMem(0);
MessageOpen(0, &QueueId);
for (;;) {
msg = MessageAlloc(0);
printf("Running core1 function\n");
msg->src = 1;
msg->flags = 0x12;
msg->heartbeat = 10 + i;
msg->numMsgs = 1;
msg->seqNum = i+1;
MessagePut(QueueId, (MessageQ_Msg)msg);
i++;
Task_sleep(1000);
}
}
5.运行结果
最终实现核1不断向核0发送心跳消息,如下所示:
—
总结
本文主要简述了6678多核IPC的配置过程,最终实现了核1向核0发送心跳包消息。6678的IPC通信宗旨是”谁接收,谁创建(CREATE);谁发送,谁分配(ALLOC);谁接收,谁释放(FREE)“。