C6678学习-IPC

1、简介

IPC: Inter-Processor Communication 处理器间通信,指提供多处理器环境中的处理器之间的通信、相同处理器不同线程间的通信。包括数据传递、数据流和链表。

IPC可用于以下通信:

  • 在同一处理器上的其他线程
  • 在其他处理器上运行的SYS/BIOS线程
  • GPP处理器上运行的SysLink的线程(例如Linux)

IPC传输分为两种:

  • QMSS(队列管理器):发送任务和内核之间的数据
  • SRIO IPC:发送任务、内核和芯片之间的数据
2、模块
MultiProc

许多IPC模块需要在多处理器环境中识别处理器。MultiProc在一个模块中集中管理处理器id。因为这配置几乎是普遍要求,大多数IPC应用程序需要提供这个模块的配置。MultiProc可以在cfg文件中静态配置,也可以在程序中动态配置,一般都是用静态配置。 var MultiProc = xdc.useModule(‘ti.sdo.utils.MultiProc’); 调用模块,相当于是获取一个接口,然后用接口进行配置

静态设置(cfg设置)
/*********在cfg文件中配置************************/
MultiProc.numProcessors = 8; //设置系统的核数为8,这里的值就是nameList数组的总长度
	MultiProc.setConfig(null, ["CORE0", "CORE1", 
		"CORE2", "CORE3", "CORE4", "CORE5", "CORE6", "CORE7"]); //设置各个核的名字,其ID是根据其名字在setConfig第二个参数数组中的位置+BaseID来确定的,BaseID默认是0,若位置为0,则ID就是0,其在nameList数组的位置就是0(nameList数组和setConfig中的数组不是同一个)
/**************在一个系统中有两个DSP,总共16个,这个16个核之间要进行通信,可以进行如下设置,使得各个核的ID不一样************************/
/*For first C6678 device:*/

  var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc');
  MultiProc.baseIdOfCluster = 0; //设置baseid
  MultiProc.numProcessors = 16;//nameList总共有16个元素
  MultiProc.setConfig(null, ["CORE0", "CORE1", "CORE2", "CORE3",
                             "CORE4", "CORE5", "CORE6", "CORE7"]);
/***DSP0中的核name占据了nameList的0~7位*****/
/*For second C6678 device:*/

  var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc');
  MultiProc.baseIdOfCluster = 8;//设置baseid
  MultiProc.numProcessors = 16;
  MultiProc.setConfig(null, ["CORE0", "CORE1", "CORE2", "CORE3",
                             "CORE4", "CORE5", "CORE6", "CORE7"]);
/***DSP1中的核name占据了nameList的8~15位*****/
动态设置
MultiProc_getNumProcessors();//获取MultiProc.numProcessors的值
MultiProc_self()//获取当前核的id
MultiProc_getName(id)//根据id获取核的name
MultiProc_getId("name")//根据name获取ID
IPC

IPC的启动很简单,只需要在函数中调用Ipc_start()就行。该函数只能被调用一次,除非返回值是Ipc_E_NOTREADY。它表示ShareRegion 0 无效或者还没有建立,所以Ipc_start可能需要再次调用,一旦它成功启动,后面调用的返回值是Ipc_S_ALREADYSETUP。

IPC的配置是在cfg文件中完成的:

var Ipc 	= xdc.useModule('ti.sdo.ipc.Ipc');
Ipc.procSync = Ipc.ProcSync_ALL;

Ipc.ProcSync_ALL :调用ipc_start还将在内部调用ipc_attach同步所有核,应用程序不应该再调用Ipc_attach。如果在一个设备上的所有的IPC核都是同时启动的话,可以选择这个同步方式。

Ipc.ProcSync_PAIR ipc_start不会调用ipc_attach,所有此时需要在代码中由应用程序调用ipc_attach;

Ipc.ProcSync_NONE Ipc_start将与ProcSync_PAIR工作完全一样,但是ipc_attach不会同步远程的核;

核的同步要按照ID号从小到大的顺序进行,比如当前核必须先同步核0,才能同步核1。两核之间的相互同步必须先满足ID号小的先同步ID号大的,比如核2需要等到核1调用了Ipc_attach(2)之后才能调用Ipc_attach(1) 。

Notify
/*注册事件,和回调函数进行绑定
 *  @param[in]  procId          远端的核号
 *  @param[in]  lineId          Line id (大部分系统为0)
 *  @param[in]  eventId         事件ID 最大31
 *  @param[in]  fnNotifyCbck    回调函数
 *  @param[in]  cbckArg         回调函数的参数 */
Int Notify_registerEvent(UInt16 procId, 
                         UInt16 lineId, 
                         UInt32 eventId,
                         Notify_FnNotifyCbck fnNotifyCbck, 
                         UArg cbckArg);
/*发送事件
 *  @param[in]  procId      目的的核号
 *  @param[in]  lineId      Line id
 *  @param[in]  eventId     事件ID
 *  @param[in]  payload     和事件一起发送的数据.是一个int型的数据
 *  @param[in]  waitClear   如果为TRUE,则当前发送的事件会等待上一个相同ID的事件被目标核处理掉,如果为FALSE,则被挂起的事件中与最近发送的事件的ID相同的事件会被覆盖掉。如果notify设备是通过FIFO传递事件的,那么当FIFO满的时候,如果为TRUE则会一直等待到FIFO有空间,如果为FALSE则会返回Notify_E_FAIL*/
Int Notify_sendEvent(UInt16 procId, 
                     UInt16 lineId,
                     UInt32 eventId, 
                     UInt32 payload,
                     Bool waitClear);
/****回调函数的形式******************************************************/
Void myFxn2(UInt16 procId, UInt16 lineId, UInt32 eventNo, UArg arg, 
            UInt32 payload)
MessageQ

MessageQ同Notify模块一样,也是用于多核之间的通信的,不过不同的是,Notify模块更加侧重于通知,其只能传递一个参数,而MessageQ却可以传递变长度的消息,更侧重于传递消息,另外不同线程间的消息是独立的,例如对于每个MessageQ来说,存在一个读者却可能有多个写者。

MessageQ模块的主要特点:
1. 实现了处理期间变长消息的传递,所需要传递的消息一般超过32bit;
2. 其消息的传递都是通过操作消息队列来实现的;
3. 每个消息队列可以有多个写者,但只能有一个读者,而每个任务(task)可以对多个消息队列进行读写;
4. 一个宿主在准备接收消息时,必须先创建消息队列,而在发送消息前,需要打开预定的接收消息队列;


HeapBufMP:固定大小的内存管理器,其分配的所有缓冲区都是一样的,当然也可以通过不同HeapBufMP实例来管理不同的大小的缓冲区。
HeapMultiBufMP:每个HeapMultiBufMP支持8个不同大小的缓冲区。当一个分配需求被发送,HeapMultiBufMP的实例从不同大小的待分配缓冲区中,选择一个能满足要求的最小缓冲区。如果待分配缓冲区为空,那么这个分配就失败了。
HeapMemMP:这是个能分配变长大小的内存管理器。另外HeapMemMP管理共享内存区(Shared memory)的一个缓冲区。
在cfg文件中开启MessageQ

/****需要HeapBufMP模块来给消息分配空间***************************************/
var MessageQ    = xdc.useModule('ti.sdo.ipc.MessageQ');
var HeapBufMP   = xdc.useModule('ti.sdo.ipc.heaps.HeapBufMP');

创建MessageQ对象

/***MessageQ不是共享内存,只能有一个读者,所以要每个核都去创建自己的MessageQ***/
MessageQ_Handle MessageQ_create(String name, const MessageQ_Params *params);

消息创建好之后,需要给消息分配堆空间。消息其实就是一个结构体。但是其结构体的第一个元素必须是 MessageQ_MsgHeader类型的。

创建一个堆对象

/*因为测试例程只需要发送一个消息就可以了,所以HeapBufMP就只分配一个消息块*/
HeapBufMP_Params_init(&heapBufParams);
heapBufParams.regionId       = 0;
heapBufParams.name           = HEAP_NAME;
heapBufParams.numBlocks      = 1; //分配的块数
heapBufParams.blockSize      = sizeof(Msg);//块的大小,Msg是创建的消息结构体
heapHandle = HeapBufMP_create(&heapBufParams);

创建好对象后,需要给堆对象注册一个id,后面给消息分配内存的时候,就直接调用ID

/*   @param[in]  heap        要注册id的堆对象*/
/*   @param[in]  heapId      得到的堆ID*/
Int MessageQ_registerHeap(Ptr heap, UInt16 heapId);

根据堆ID给堆分配空间

//返回一个msg,这个msg就是用来传递消息的。
MessageQ_Msg MessageQ_alloc(UInt16 heapId, UInt32 size);

分配完空间得到msg后就可以发送了

/*通过接收核创建消息时注册的名称来打开消息队列,并返回消息队列的ID
根据消息队列ID,将消息发送给消息队列*/
Int MessageQ_open(String name, MessageQ_QueueId *queueId); //打开队列
Int MessageQ_put(MessageQ_QueueId queueId, MessageQ_Msg msg);//发送消息queueId就是打开消息的时候得到的,msg就是堆内存分配的时候返回的。

消息接收

Int MessageQ_get(MessageQ_Handle handle, MessageQ_Msg *msg, UInt timeout);
ShareRegion

SharedRegion模块是一个共享区域,特别是对于多处理器环境下,SharedRegion模块就是用于让一个内存区域能被不同处理器共享并操作。这个模块会给每个处理器上创建一个共享内存区域查找表,这个查找表保证各个处理器能查看到系统内的所有共享区域。查找表中共享内存区域在所有查找表中的区域ID是一致的,在运行时,查找表可以通过共享区域ID及共享区域名称来快速查找共享区域

通过cfg配置共享区域

var SharedRegion = xdc.useModule('ti.sdo.ipc.SharedRegion');
SharedRegion.numEntries = 4;  //共享区域的最大个数
SharedRegion.translate = true; //是否需要进行地址转换

/* Shared Memory base address and length */
var SHAREDMEM     = 0x88000000;  
var SHAREDMEMSIZE = 0x00300000; 
SharedRegion.setEntryMeta(0,//共享区域的ID号
    { base: SHAREDMEM,  //共享区域的基地址
      len:  SHAREDMEMSIZE, //共享区域大小
      ownerProcId: 0, //共享区域所有者的核ID
      isValid: true, //对于当前核,该区域是否有效
      name: "SR0",  //区域的名称
    });

1)base:区域的基地址,不同处理器其基地址可以是不同的。
2)len:区域的长度,同一个共享区域在所有处理器的查找表中的长度应该是相同的。
3)ownerProcID:管理该区域的处理器ID,如果存在区域所有者,这个区域所有者(owner)就是创造HeapMemMp实例的,而其他核打开这个实例。
4)isValid:表明该区域在当前核上是否可用,判断当前核能否使用此共享区域的。
5)cacheLineSize:这个值在所有核的查找表中都应该是相同的
6)createHeap:表明是否需要给当前区域创建一个堆。
7)name:区域的名称。

共享内存的使用

heaphandle  = (IHeap_Handle)SharedRegion_getHeap(regionId);  // 通过区域ID获得共享区域的堆句柄
buf = Memory_alloc(heap, size, align, NULL);  // 通过堆句柄分配区域内存

地址转换

共享内存的地址在各个核上会被映射到不同的地址空间,因此我们需要进行地址转换。

/*先将本地分配的共享内存得到一个本地地址转换,通过SharedRegion_getSRPtr()将本地地址转换成共享内存地址,然后通过MessageQ将共享内存地址发送到其他核。其他核通过SharedRegion_getPtr()来得到共享内存对应的本地地址,然后就可以通过本地地址来访问共享内存的内容了*/
SharedRegion_getSRPtr() //根据给定的本地指针及区域ID来获得当前共享区域指针
SharedRegion_getPtr() //根据共享区域指针来获得本地指针

能否使用此共享区域的。
5)cacheLineSize:这个值在所有核的查找表中都应该是相同的
6)createHeap:表明是否需要给当前区域创建一个堆。
7)name:区域的名称。


共享内存的使用

```c
heaphandle  = (IHeap_Handle)SharedRegion_getHeap(regionId);  // 通过区域ID获得共享区域的堆句柄
buf = Memory_alloc(heap, size, align, NULL);  // 通过堆句柄分配区域内存

地址转换

共享内存的地址在各个核上会被映射到不同的地址空间,因此我们需要进行地址转换。

/*先将本地分配的共享内存得到一个本地地址转换,通过SharedRegion_getSRPtr()将本地地址转换成共享内存地址,然后通过MessageQ将共享内存地址发送到其他核。其他核通过SharedRegion_getPtr()来得到共享内存对应的本地地址,然后就可以通过本地地址来访问共享内存的内容了*/
SharedRegion_getSRPtr() //根据给定的本地指针及区域ID来获得当前共享区域指针
SharedRegion_getPtr() //根据共享区域指针来获得本地指针
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值