DSP多核通信——IPC组件

IPC多核开发组件是TI为多核开发设计的一款组件,使用IPC多核开发组件,可以不过多的关注底层的内容,提供了底层的API接口,在各个平台都是通用的。

IPC是一个包含多核通信开发环境的组件。可以实现的通信包括:

1、同一处理器的不同线程之间的通信

2、与运行SYS/BIOS的不同处理器通信(多个核心通信)

3、与运行SYS/LINK的GPP通用处理器(ARM)通信

要使用IPC组件必须要用到SYS/BIOS和XDCtools。

SYS/BIOS是一个可扩展的实时操作系统内核。具有非常快速的响应时间,优化的内存分配和堆栈管理(尽量少的消耗和碎片)。能够实现系统的模块化并可裁剪。最大的特点就是实时。

XDC是TI公司为嵌入式实时系统可重用软件组件制定的一套标准。它包括一些有用的工具,标准 的API函数静态配置文件和打包操作。

IPC组件包含的模块:

 GateMP通常用于保护共享内存的读写,在访问内存区域的时候,进入设置的GateMP后可以防止被同核心的其他线程抢占,也可以防止远端处理器进入相同的Gate从而保护要访问的内存区域。

IPC初始化相关API:

使用IPC组件首先要使用的函数是

Ipc_start:从ID为0的共享内存区获取内存进行IPC基础模块和资源的初始化。在调用Ipc_start时,ID为0的共享内存区还未被建立(由该内存区的Onwer建立),则会返回Ipc_E_NOTREADY的错误,这时应该再次调用Ipc_start,以完成初始化。对于SYS/BIOS若Ipc,procSync IPC同步被设置成Ipc,procSync_ALL也就是全核同步的话,在内部就会调用Ipc,attach,若没有设置成全核同步则需要手动调用。

Ipc_attach:打开默认建立的几个IPC模块,并于多核系统中的其他处理器建立连接,需要依次调用以连接其他处理器,以C6678为例,有8个核就需要调用7次。(ID为0的共享区的onwer处理器作为首位)

while(Ipc_attach(remoteProcld)<0){Task_sleep(1);}    

//remoteProcld就是要连接的核心号,若这是核0的代码,核0想与核1连接此处传入的值为1

Ipc_detach:断开与其他处理器的连接。需要依次与其他处理器断开(ID为0的共享区的onwer处理器作为首位)

while(Ipc_detach(remoteProcld)<0){Task_sleep(1);}    

//remoteProcld就是要断开连接的核心号,若这是核0的代码,核0想与核1断开连接此处传入的值为1

Notify:最简单的核间通信机制,支持最多32bit的数据传输

蓝色为需要使用的API模块,红色为内部调用模块,只要配置一下就可以了。 使用它主要涉及到几个函数。

void cbFxn
(
UInt16 procID, 
UInt16 lineID, 
UInt32 eventID, 
UArd arg, 
UInt32 payload
);

/*
自己定义的回调函数,形参是固定的,处理器接收到Notify后回调函数就会被调用,
参数就会通过形参的方式传到回调函数里

参数1:发送notify事件的远端处理器ID
参数2:远端处理器发送的lineID
参数3:远端处理器发送的eventID
参数4:注册回调函数时配置的固定值
参数5:notify事件携带的32bit数据
*/

int Notify_registerEvent
(
UInt16 procID,
UInt16 lineID,        //通常取0 
UInt32 eventID,       //取值0~31
Notify_FnNotifyCbck fnNotifyCbck, 
UArg cbckArg
);

/*
注册函数
参数4:自己定义的回调函数
参数5:传递到回调函数的固定值
*/

int Notify_sendEvent
(
    UInt16 procID,
    UInt16 lineID, 
    UInt32 eventID,
    UInt32 payload,
    Bool waitClear
);

/*
发送事件函数
参数1:发送事件目标处理器的ID
参数4:发送事件携带的32bit数据
参数5:bool变量当它为输入时event就会等待目标处理器对这个事件做出响应才会返回值
*/

Notify使用

slaver向master发送一个notify然后master做出响应。

首先要调用start和attach函数,在master上注册一个事件用于接收slaver发送的通知,因此注册函数的第1个参数为slaverID,lineID取0,eventID取10,回调函数是cbFxn,传到回调函数的固定值是0x1010;在slaver上会调用send_Event给master发送一个通知,lineID与eventID与master注册时的一致,payload是发送的数据。调用后master就会做出响应这时master就会调用回调函数cbFxn,它与中断服务函数类似,传入的参数,第1个参数为发送通知的event处理器的ID,然后是lineID和eventID,第4个参数是注册时使用的固定值,payload就是发送过来的数据。 

同一个lineID和eventID可以注册多个回调函数,在接收到事件的时候回调函数就会被依次执行。

Notify一般作为中断来使用,但当需要传递大量数据的时候,单独的notify就不能满足需求了。这时候就需要用SharedRegion。

SharedRegion添加入口;

SharedRegion模块本身并不占用任何的共享内存空间,它的状态都是保存在本地。模块通过创建查找表的方式来管理共享内存和获取共享内存信息。每一个核心都有自己的查找表,通过它可以找到共享内存的位置、大小、访问权限等。

使用SharedRegion首先要添加查找表的入口,也就是首先要定义好共享内存的信息。
添加入口有两种方式:一种是静态的(在cfg文件里添加),一种是的动态的(在代码里添加)。

静态:

动态:

int ShareRegion_setEntry(UInt16 regionID, SharedRegion_Entry *entry);

/*
参数1:regionID
参数2:结构体,这个结构体的内容与静态配置里面的内容类似,使用指针指向这个结构体
*/

 需要注意的是,由于IPC_start()中初始化的资源和基础模块(GateMP、NameServer、Notify等)会用到SharedRegion0,因此SharedRegion0对于所有核心必须都是可以访问的。也就是SharedRegion0里面的所有isValid必须是true。

SharedRegion的使用:

主要有几个工作:

1、申请内存

inBuf = (unsigned char*)Memory_alloc(SharedRegion_getHeap(0), dateNum, 128, NULL);
/*返回的是一个指针*/

2、根据地址获取SharedRegionID

id = SharedRegion_getID(addr);
/*返回值是SharedRegion的ID*/

3、地址转换,根据地址和SharedRegionID获取共享区域指针

inBuf_srptr = SharedRegion_getSRPtr(inBuf, 0);
/*
将内存的地址值转换为共享区域指针,
传入的参数1是指针也就是内存的地址值,
参数2是SharedRegion的ID,
返回值为共享区域的指针,为32bit的值,包含了ID和SharedRegion的偏移地址
*/

4、将共享区域指针转换成内存地址

inBuf = SharedRegion_getPtr(inBuf_drptr);
/*
传入的参数就是共享区域指针返回的值,也就是内存地址
*/

SharedRegion的使用:

 同样需要调start和attach函数,然后master注册一个notify,因为其可以携带32bit的数据,所以可以将共享区域指针通过notify传到其他核心,然后master申请一个空间,返回了一个指针,再向这个指针所指空间写数据,写完后将这个inBuf地址转换为共享区域指针,将它通过notify传到slaver核心上。

slaver核心也注册了notify,设置了一个信号量,一直等待信号量释放,当master发送event之后,slaver的回调函数cbFxn就会被调用,进入这个回调函数,就可以获取这个共享区域指针同时释放信号量,收到信号量后slaver的主函数会继续往下执行,slaver就通过SharedRegion_getPtr函数将共享区域指针转换成内存地址,slaver就可以读取这个内存地址里面的值。

MessageQ:可变长度的结构化消息通信

这里MessageQ传递的是一个结构且可以自行定义。

MessageQ Creator:

从MessageQ读取消息的线程(只能在一个线程里存在)

 调用:1MessageQ_create(), 5MessageQ_get(), 6MessageQ_free(), 7MessageQ_delete()

MessageQ Opener:

往MessageQ写入消息的线程(可以多个)

调用:2MessageQ_open(), 3MessageQ_alloc(), 4MessageQ_put(), 5MessageQ_close

执行顺序参照函数前的数字。

typedef struct MyMsg
{
    MessageQ_MsgHeader header;    //Required
    SomeEnumType type       
    ... 
}MyMsg;
/*
定义一个结构体,定义了msgq的内容
第一个参数必须那样写
从第二个参数开始可以自定义,可以是任何值
*/



 

核0核1都创建一个msgQ是因为这样可以又put又get,这个过程就是核心0把消息发给核心1,核心1 又将消息发回给核心0; 

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值