核间ipc通信机制ipc qmessage需要什么包

查看: 678|回复: 0
TMS320C6678多核DSP的核间通信方法
本帖最后由 taline 于
09:38 编辑
& && && &摘 要: 嵌入式应用中采用多处理系统所面临的主要难题是多处理器内核之间的通信。对KeyStone架构TMS320处理器的多核间通信机制进行研究,利用处理器间中断和核间通信寄存器,设计并实现了多核之间的通信。从系统的角度出发,设计与仿真了两种多核通信拓扑结构,并分析对比了性能。对设计多核处理器的核间通信有一定的指导价值。
& && && &关键词: TMS320C6678;KeyStone;处理器间中断;多核通信
& && && &嵌入式领域的处理器设计已向多核处理器迅速发展,TI公司的KeyStone架构的多核处理器就是其中之一。2010年11月,TI公司发布了最新的KeyStone架构的8核DSP处理器TMS320C6678,每个C66x内核频率为1.25 GHz,提供每秒高达40 GB MAC定点运算和20 GB FLOP浮点运算能力;1片8核的TMS320C6678提供等效达10 GHz的内核频率,单精度浮点并行运算能力理论上可达160 GB FLOP,是TS201S的50倍、C67x+的115.2倍[1],适合于诸如油气勘探、雷达信号处理以及分子动力学等对定浮点运算能力及实时性有较高要求的超高性能计算应用。
& && && &核间通信是多核处理器系统所面临的主要难点,通信机制的优劣直接影响多核处理器的性能,高效的通信机制是发挥多核处理器高性能的重要保障。TMS320C6678采用TI全新KeyStone多核架构,属于单芯片多核架构,有别于常见的板载多芯片通信方式[2]。而KeyStone架构的通信研究才刚刚起步,因多核通信复杂,需要搭建合适的通信拓扑结构,因此拓扑结构的选取会直接影响通信代价和并行计算的效率[3]。TMS320C6678采用基于KeyStone架构的中断控制器、核间通信寄存器以及合适的通信拓扑结构实现多核间的通信。通过中断系统激活处理器,触发具有通信功能的中断服务程序,调用寄存器完成相应的功能,并通过合适拓扑结构完成通信。
& && && &基于以上分析,本文针对TMS320C6678多核处理器,首先分析了中断控制器和核间中断原理及其实现;然后分析了核间通信的原理,给出了通信的发起和响应的实现方法;最后介绍了主辅和数据流两种多核通信的拓扑结构,并通过仿真对其通信代价进行了对比,得出了两种结构的优缺点以及适用范围。对设计多核处理器核间通信有一定指导意义。
& && && &1 TMS320C6678中断控制器
& && && &TMS320C6678采用基于KeyStone架构的中断控制器INTC(Interrupt Controller)[4]、激活处理器触发相应的中断服务程序,完成通信的第一步。
首先需要配置中断向量表,启动CPU中断功能。TMS320C6678的CPU可接收15个中断,其中:1个硬件异常(EXCEP)、1个不可屏蔽中断(NMI)、1个复位(RESET)和12个可屏蔽中断(INT4~INT15),中断源支持最多128个。每个核心通过事件控制器产生事件(Event),触发核间中断(IPI)和其他核心进行通信。在TMS320C6678中,核间中断(IPC_LOCAL)默认对应91号事件,而核间中断属于可屏蔽中断,通过中断控制器可以映射到INT4~INT15任意一个中断上。为了实现核间中断,必须按以下方式进行设置:
& && && &(1)控制状态寄存器(CSR)中的全局中断使能位置为1,全局中断使能;
& && && &(2)中断使能寄存器(IER)中的NMIE位置为1,可屏蔽中断使能;
& && && &(3)中断使能寄存器(IER)将要映射的可屏蔽中断的相应位置1;
& && && &(4)选择91号事件作为中断源,映射事件到指定的物理中断号。中断发生后,将中断标志寄存器(IFR)的相应位置1。
& && && &中断发生时,由事先配置好的中断向量表跳入中断服务程序(ISR),完成核间通信,如图1所示。
& && && &SRCS0~SRCS27)提供了可以识别多达28种的中断来源。IPC中断确认寄存器IPCARx(0≤x≤7)的0~3位是保留位,4~31位(SRCC0~SRCC27)分别对应28种不同的中断来源。当SRCSx被置1时,寄存器将相应的中断确认寄存器的SRCCx位置1。当中断被确认后,寄存器将SRCCx和相应的SRCSx位同时置0。
当TMS320C6678的一个处理器核准备与其他处理器核通信时,根据TMS320C6678的中断事件映射表,引发91号事件,产生可屏蔽的核间中断,调用中断服务例程。中断服务例程IPC_ISR函数设计如下:
void IPC_ISR()
{
KICK0 = KICK0_UNLOCK;
KICK1 = KICK1_UNLOCK;
*(volatile uint32_t *) IPCGR[2] = 0x20;
*(volatile uint32_t *) IPCGR [2] |= 1;
KICK0 = KICK0_UNLOCK;
KICK1 = KICK1_UNLOCK;
}复制代码& && &&&以向core_2发送0x20信息的中断为例,对应的0x20的信息存入SRCS位中,用于识别中断源。同时将当前CPU核心内的中断产生寄存器IPCGR2的最后一位IPCG位置1,触发IPC中断。当目标处理器核被中断触发后,会自动跳转到中断异常向量表中相应的入口点,读取当前核心中断产生寄存器IPCGRx(0≤x≤7),从寄存器的SRCS位中获取通信发起方传来的核间信息。然后将信息存入对应的中断确认寄存器IPCARx中,清空SRCC和相应的SRCS位,用以接收下一次的核间中断。其中的KICK0和KICK1为陷阱控制寄存器,用来避免通信冲突的发生。
& && && &3 拓扑结构设计与性能测试
& && && &以上对TMS320C6678基本核间通信机制及其实现过程进行了分析,但是要实现TMS320C6678强大的多核功能,必须从系统的角度上设计良好的并行计算方案,设计合适的系统并行拓扑是其中的关键所在。通信代价、带宽和功能是评测通信的重要指标,下面介绍了两种多核通信并行方式,分析了它们的拓扑结构,并对上述指标做了测试对比。
& && && &3.1 通信的拓扑结构
& && && &适用于多核DSP通信的并行方式有两种:一种是主辅拓扑结构(Master Slave)[5],另一种是数据流拓扑结构(Data Flow)[6]。
主辅拓扑结构,在TMS320C6678中如图2所示。作为主核(控制核)的处理器通过EDMA与外部存储器DDR进行数据交换,然后主核通过核间中断与辅核通信。主核起到控制的作用,所有辅核(计算核)的中断都由控制核来处理,辅核只负责计算任务,辅核之间没有任何核间通信的产生。
& && && &3.2 性能测试实验
& && && &本文设计了核间通信测试程序来测试两种结构。程序的功能是:当一个核收到来自其他核的中断后,立即确认并按照拓扑结构依次发出核间中断,没有其他耗时的操作。程序在TMDXEVM6678L评估板上进行仿真,板载有一块TMS320C6678芯片,处理器运行的频率设置为1 GHz,采用的编译环境是TI公司的v5.0。
& && && &通信测试结果如表1所示,主辅结构运行完测试程序所需要的总通信代价是171 352个时钟周期。其中,作为主核的core_0耗费116 311个时钟周期,而7个辅核每个核均耗费7 863个时钟周期。采用数据流结构运行完测试程序所需要的总通信代价是171 319个时钟周期,其中core_0耗费21 385个时钟周期,core_7耗费21 366个时钟周期,其他6个核耗费21 428个时钟周期。
& && && &如图4所示,采用主辅结构时,作为辅助核的7个核可以同时并行运行,理论上通信时间可以减少到124 174个时钟周期。主辅结构由于利用了多核的并行处理,总的通信时间是数据流结构的72.5%,以测试环境中的1 GHz的主频计算,则节省了47.1 μs。
站长推荐 /1
Code Composer Studio(TM) (CCStudio) 是用于德州仪器 (TI) 嵌入式处理器系列的集成开发环境 (IDE)。TI CCS5.2.1破解说明:下载许可证文件拷贝到目录即可..
Powered byDaVinci开发(4)

Davinci中的多核系统一般由GPP+DSP构成,也就是所谓的异构多核(同构是指内部核的结构是相同的,而异构是指内部的核结构是不同的),为了为异构多核处理器间提供高效的异构多核协作,需要建立异构多核间的通信机制。在TI提供的异构多核间通信组件SysLink中,核间通信机制为用户提供了多种实现方法。下面的内容将介绍SysLink架构、特性和相关的API。
Higher&Level&Operating&System
Real&Time&Operating&System
Code&Composer&Studio
Inter-Processor&Communication
General&Purpose&Processor&e.g.ARM
Digital&Signal&Processor&e.g.&C64X
Code&Gen&Tools&piler&Linker&Archiver
SysLink工具包为异构多核之间的通信提供基础开发接口,使得多核系统之间更方便的交换信息。在多核异构系统中,每个核心运行的操作系统和所处地位各不相同。所运行的OS可以是HLOS,如Linux、WinCE等,也可以是RTOS如SYS/BIOS或者QNX。异构多核系统中主处理器(Host&Processor)肩负着控制从处理器(Slave&Processor)的责任。
广义上的SysLink包含了运行在HLOS上的SysLink和运行在RTOS上的IPC。其基本架构如下图所示:
SysLink工具包主要针对于嵌入式操作系统中的应用,主要由以下组件构成:
系统管理(System&Manager)
处理器管理(Processor&Manager——PM)
核间通信(Inter-Processor&Communication——IPC)
其他模块(Utility&Modules)
系统管理模块(IPC&module)为方便多核管理提供了简单快捷的方法,此外也为系统资源(e.g.系统内存)的管理提供了接口。
IPC&模块提供的功能包括:
SysLink系统初始化(syslink_setup()、syslink_destroy())并为其他SysLink组件分配内存,包括IPC模块和PM模块(MemoryOS_setup()、Ipc_setup(&config))
系统配置:任何系统级别的配置信息是由系统管理;
ProcMgr模块为从处理器提供了以下services:
具体例子可以参见DVRRDK_xx.xx.xx.xx\dvr_rdk\mcfw\src_linux\utils\fw_load中firmware&load的例子。
boot-loading从处理器
读写从处理器的内存区
从处理器电源管理
因此该模块为以上services提供了以下接口:&
Loader:处理器的Loader接口有多种实现实现方式,被写入的文件形式可能是如COFF、ELF、动态loader(不太清楚这是啥)或者自定义类型的文件等等;
Power&Manager:考虑到处理器管理模块的通用性并且希望电源管理模块可以自定义,在SysLink中电源管理是可嵌入处理器管理的独立模块;
Processor&Manager:为处理器提供了加载、MMU管理(A8支持)、读写从处理器内存等接口。
Loader流程图:
Processor&Manager系统框架图如下&
在SysLink系统中,为了方便管理,每个处理器都会被编码(即Processor&ID);如图中所示,在该系统中使用了硬件抽象层来屏蔽底层硬件差异,这样做的好处就是为不同的底层硬件提供了通用的软件接口。
注:SysLink中从处理器的Loader文件理论上支持多种格式,在SysLink&Release版本中主要支持COFF和ELF。在TI的编译系统中,可以以可执行文件的后缀名来区别COFF文件和ELF文件,后缀名中带有‘e’的是ELF(如:xxx.xe64P),不带‘e’的是COFF文件(如:xxx.x64P)。
当前的ELF&Loader只支持顺序加载,即只有当一个从处理器加载并启动后才能去加载下一个从处理器,不支持并行加载。
SysLink下定义了以下几种通信机制:
FrameQ(通常用于raw&视频数据)
RingIO(通常用于音频数据)
这些通信机制的接口都有一下几个共同点:&
所有IPC通信机制的接口都由系统规范化的命名;
在HLOS端,所有IPC&接口都有&Module&_setup()&and&&Module&_destroy()&API用于初始化或者销毁相应的IPC&Module;部分初始化还需要提供配置接口,&Module&_config();
所有的实例化都需要使用&Module&_create()来创建,使用&Module&_delete()来删除;
在更深层次使用IPC时需要用API&&Module&_open()来获取handle,在结束使用IPC时需要用API&&Module&_close()来回收handle;
IPC的配置多数都是在SYS/BIOS下完成配置的,对于支持XDC配置的则可以使用静态配置方法;
每个IPC模块都支持Trace信息用于调试,而且支持不同的trace等级;
部分IPCs提供了专门的APIs来用于提取分析信息;
Notify组件将硬件中断抽象成多组逻辑事件,是一种简单快捷的发送低于32bit信息的通信方式。
Notify组件提供了以下接口:
初始化并配置Notify组件;Notify_attach();
注册/注销事件;Notify_registerEvent()/Notify_unregisterEvent()/Notify_registerEventSingle()/Notify_unregisterEventSingle()
发送带参数的事件给某处理器;Notify_sendEvent()
通过回调函数接收事件;Notify_FnNotifyCbck()
使能/禁用事件;Notify_diableEvent()/Notify_enableEvent()
其他逻辑接口;Notify_eventAvailable()/Notify_intLineRegistered()/Notify_numIntLines()/Notify_restore()
注:&同一个中断号可以注册多个事件,同一个事件可以有多个回调函数或者多个宿主(可以是处理器、线程或者任务),事件被触发后所有宿主都会被唤醒;
一个事件可以接收多个宿主发送来的通知(notification),事件所携带的参数最大支持32bit;
事件是有优先级的,EventId越小优先级越高,事件0的优先级最高,随着EventId增大优先级依次递减;当多个事件被触发,优先级最高的会最先响应;
Notify模块使用硬件中断,因此不能被频繁调度。
Notify组件常用于传递附带消息少于32bit的场景,如信令传递、buffer指针传递等。在信令传递时使用高优先级的事件,如事件0;而在传递buffer指针是可以使用低优先级的事件,如事件30等。
在Notify_sentEvent()&API中带有参数waitClear,该参数为可选参数,如果waitClear为TRUE,这就意味着多宿主事件无法及时响应,必须等待前一宿主事件结束后才能响应下一宿主;如果waitClear为FALSE,最好不要为事件附带参数,否则多宿主事件可能会由于消息被覆盖而出现丢消息的现象。该API最好不要在中断服务程序(ISR)中调用(特别是waitClear&=&TRUE时),否则会导致中断调度出现异常(表现之一:高优先级的中断响应会延迟);此外该API不能再使用GateMP模块锁保护的程序段中调用,否则可能会导致操作系统死锁。
由于其他模块使用了Notify机制,因此在SysLink中预留了部分事件号,这部分事件号用户需要慎重选用(如果你没有使用其他组建的话,可以考虑占用这部分事件号),在注册事件前可以使用Notify_eventAvailable()来检查该事件是否可用,即该中断号上的该事件号是否被注册。
FrameQBufMgr
MessageQ(TransportShm)
NameServerRemoteNotify
MessageQ,顾名思义,基于队列的消息传递,可不是MaggieQ噢,哈哈。
MessageQ有以下特点:
实现了处理期间变长消息的传递;
消息的传递都是通过操作消息队列来实现的;
每个消息队列可以有多个写者,但只能有一个读者;每个任务(task)可以对多个消息队列进行读写;
一个宿主在准备接收消息时,必须先创建消息队列,而在发送消息前,需要打开预定的接收消息队列;
MessageQ组件常用在满足以下条件的场景中:
在消息传递中有多个写者,但仅有一个读者;
所需要传递的消息超过32bit,且长度可变;读写者的缓冲区大小相同;
处理期间需要频繁传递消息,在这种情况下,消息被依次放入队列,能保证不会丢消息;
消息队列为空时,调用MessageQ_get()获取消息时会被阻塞,直到消息队列被写入消息;
支持处理器间移动消息队列,在这种情况下,调用MessageQ_open()来定位队列位置,而消息传递部分代码不需要改动;
MessageQ组件提供了以下几个API:
消息队列初始化:MessageQ_Params_init()
消息队列创建/销毁:MessageQ_create()/MessageQ_delete(),create创建消息队列,并分配相应存储空间
消息队列打开/关闭:MessageQ_open()/MessageQ_close(),open时会返回远程处理器上的QueID的地址。
为消息队列分配堆内存:MessageQ_alloc()/MessageQ_free()
为消息队列注册/注销堆内存:MessageQ_registerHeap()/MessageQ_unregisterHeap()
向消息队列中放入/获取消息:MessageQ_put()/MessageQ_get()
其他逻辑API:
获取消息队列ID:MessageQ_getQueueId()
获取消息队列中消息数:MessageQ_count()
在消息队列中嵌入消息:MessageQ_setReplyQueue()
为消息队列解阻塞:MessageQ_unblock()
为调试消息队列加入Trace:MessageQ_setMsgTrace()
ListMP实现了多宿主双向循环链表,即该双向循环链表为多个处理器共同拥有,可以由多个处理器共同维护,共同使用。
ListMP的实现区别于一般的双向循环链表,因此它不仅具有双向循环链表的特性外,还增添了其他的特性,比如以下几点:
实现了简单的多宿主协议,支持多个读写者(multi-reader、multi-writer);
使用Gate作为内部保护机制,防止多个宿主处理器同时访问该链表;
ListMP的实现并未加入通知机制,如果需要的话,可以在外部封装是引入Notify机制来实现;使用ListMP机制来管理的buffers都需要从共享内存区分配,包括从堆内存分配的buffers以及动态分配的内存。&
ListMP组件常用于满足一下条件的场景中:
需要被多个宿主访问并且需要频繁传递消息或者数据;
可用于无规则的消息传递,基于链表实现,因此读者可以遍历所有对象,并选出需要的对象进行处理;如果硬件支持快速队列,则无法完成队列遍历操作(WHY);
可以自定义消息优先级,同样是基于链表实现,读者可以随意的选择在链表头部还是链表的尾部来插入消息或者实现链表对象的位置调整,进而实现消息的优先级选择;如果硬件支持快速队列,则无法完成队列遍历操作(WHY);
无内置通知机制,可以灵活的外部通知机制来实现。譬如根据实际情况,选用Notify来实现,亦或是使用选用MessageQ则可以使用最少的中断资源实现性能优良的通知机制,缺点是需要额外的代码实现通知机制。
ListMP组件提供了以下API接口:&
ListMP参数初始化:ListMP_Params_init()
ListMP创建/销毁:ListMP_create()/ListMP_delete()
ListMP打开/关闭:ListMP_open()/ListMP_close()
ListMP相关链表操作:
判断链表空:ListMP_empty()
获取保护锁:ListMP_getGate()
获取链表头/表尾:ListMP_getHead()/ListMP_getTail()
链表插入操作:ListMP_insert()
获取链表上游元素/下游元素:ListMP_next()/ListMP_prev()
插入元素到链表头/尾:ListMP_putHead()/ListMP_putTail()
删除元素:ListMP_remove()
GateMP是针对于多处理器共享资源的一种保护机制,就如其名字一样,把共享资源比作房子,那么GateMP就是这个房子的门。GateMP组件实现了开关门的机制,用于保护共享资源一次只被一个处理器读写。根据SOC硬件资源配置的不同,GateMP的实现有所不同。对于硬件支持Hardware&Spinlock的可以基于H/W&spinlock来实现GateHwSpinlock;而对于没有该硬件资源的系统中,则使用软件方法(Peterson算法)来实现GatePeterson。
GateMP组件框架如下:
GateMP组件对用户提供了以下API接口:
GateMP初始化:GateMP_Params_init();
GateMP创建/删除:GateMP_create()/GateMP_delete();
GateMP打开/关闭:GateMP_open()/GateMP_close();
进入/离开GateMP保护:GateMP_enter()/GateMP_leave();
获取当前GateMP的保护类型:
GateMP_getLocalProtect()/GateMP_getRemoteProtect();
注:如果某个处理器在想使用被某个GateMP保护的共享资源,那么该处理器会被阻塞,直到该资源被释放(即GateMP_leave())。
HeapMP主要包括HeapBufMP和HeapMemMP,用于共享内存区的堆内存配置和管理。
HeapMP具备以下几个特征:
支持多宿主,即无论是运行HLOS的主处理器还是运行SYS/BIOS的从处理器都可以配置和管理堆内存;
可以将共享内存区配置成缓冲池(buffer&pools);
可以从共享内存区分配和释放缓冲区;
HeapBufMP为用户提供了固定大小的缓冲池管理接口;
HeapBufMP组件为用户提供的API接口如下:
HeapBufMP创建/删除:HeapBufMP_create();HeapBufMP_delete();
HeapBufMP打开/关闭:HeapBufMP_open();HeapBufMP_close();
HeapBufMP参数初始化:HeapBufMP_Params_init();
HeapBufMP分配/释放内存:HeapBufMP_alloc();HeapBufMP_free();
HeapBufMP获取所有状态:HeapBufMP_getExtendedStats();HeapBufMP_getStats();
在SysLink包中并没有找到相关API,但SysLink&UserGuide中有提到.
HepMultiBufMP为用户提供了可配置大小的缓冲池管理接口。
HeapMemMp为用户提供了基于堆的可变大小的内存管理机制。
HeapMemMp组件为用户提供的接口如下:
HeapMemMP参数初始化:HeapMemMP_Params_init();
HeapMemMP创建/删除:HeapMemMP_create()/HeapMemMP_delete();
HeapMemMP打开/关闭:HeapMemMP_open()/HeapMemMP_close();
HeapMemMP分配/释放内存:HeapMemMP_alloc()/HeapMemMP_free();
HeapMemMP获取内存状态:HeapMemMP_getExtendedStats()/HeamMemMP_getStats();
HeapMemMP恢复内存初始状态:HeapMemMP_restore();
FrameQ是专门为传递视频帧而设计出来的组件。FrameQ的基本数据结构是可用于queue/dequeue数据的数据队列,封装了视频帧缓存指针、帧数据类型、帧宽、帧高、时间戳等信息。
对于FrameQ模块具有以下特征:
支持多个读者,但写者唯一;
可以分配和释放Frames;
可以对指向同一块内存区多次分配和初始化成新的帧buffer;
FrameQ允许有多个队列,在多通道的运用中,视频帧会根据通道号被分配到相应的帧队列中;
FrameQ中用于buffer管理的模块称为FrameQBufMgr,该模块用于提供buffer管理接口和通知机制。
FrameQ提供以下API接口:
FrameQ组件初始化/销毁:FrameQ_setup()/FrameQ_destroy();
创建/删除FrameQ实例:FrameQ_create()/FrameQ_delete();
打开/关闭FrameQ实例:FrameQ_open()/FrameQ_close();FrameQ_openByAddr();
为FrameQ实例分配/释放内存:FrameQ_alloc()/FrameQ_free();FrameQ_allocv/FrameQ_freev();
插入/释放FrameQ中帧:FrameQ_put()/FrameQ_get();FrameQ_putv()/FrameQ_getv();
复制给定的帧:FrameQ_dup();
注册/注销FrameQ通知:FrameQ_registerNotifier()/FrameQ_unregisterNotifier();
强制发送通知:FrameQ_sendNotify()
获取FrameQ中有效帧数/已被释放的帧数:
FrameQ_getNumFrames()/FrameQ_getNumFreeFrames();
FrameQ_getvNumFrames()/FrameQ_getvNumFreeFrames();
FrameQ控制:FrameQ_control()
获取FrameQ的头指针:FrameQ_getExtendedHeaderPtr();
获取帧buffer/帧大小/帧数:FrameQ_getFrameBuffer()/FrameQ_getFrameBufSize()/FrameQ_getNumFrameBuffers();
获取空数据帧大小/位置:FrameQ_getFrameBufValidSize()/FrameQ_getFrameBufDataStartOffset();
设置空数据帧大小/位置:FrameQ_setFrameBufValidSize()/FrameQ_setFrameBufDataStartOffset();
获取FrameQ默认设置:FrameQ_getConfig();
RingIO是基于数据流的环形缓冲buffer,而且针对于音视频数据的特性做了优化。
RingIO支持一下特性:
仅支持一个读者和一个写者;
读写相对独立,可以在不同的进程或者处理器中同时进行读写操作;
RingIO为用户提供了以下接口:
RingIO参数初始化:RingIO_Params_init();
创建/删除RingIO对象:RingIO_create()/RingIO_delete();
打开/关闭RingIO对象:RingIO_open()/RingIO_close();RingIO_openByAddr();
获取共享内存请求:RingIO_sharedMemReq();
注册/注销RingIO通知:RingIO_registerNotifier()/RingIO_unregisterNotifier();
强制发送RingIO通知:RingIO_sendNotify();
获取RingIO通知类型:RingIO_setNotifyType();
设置/获取水印标志/通知类型:RIngIO_setWaterMark()/RIngIO_getWaterMark()
获取/释放RingIO数据:RingIO_acquire()/RingIO_release();
设置/获取RingIO属性:RingIO_setvAttribute()/RingIO_getvAttribute();
设置/获取RingIO固定大小的属性:RingIO_setAttribute()/RingIO_getAttribute();
刷新RingIO的buffer:RingIO_flush();
获取有效/空buffer大小:RingIO_getValidSize()/RingIO_getEmptySize();
获取有效/空属性大小:RingIO_getValidAttrSize()/RingIO_getEmptyAttrSize();
获取用户需求buffer的大小/位置:RingIO_getAcquiredSize()/RingIO_getAcquiredOffset();
Utility&Modules包括SharedRegion(IPC中属于类库ti.sdo.ipc.SharedRegion)、List、Trace、MultiProc、NameServer等,这些模块是上层组件实现的基础。在IPC包中,该组件对应于类库ti.sdo.utils.
(非常重要,SysLink模块最基础的模块)
SharedRegion顾名思义,是共享内存区的意思。SharedRegion模块负责管理共享内存区。在一个有共享内存的多核架构中,普遍会遇到共享内存映射虚拟地址转换问题,如下图所示:
SharedRegion有两种配置方式,即静态配置方法(对于SYS/BIOS侧可以通过cfg脚本配置,而对于HLOS则当从处理器被加载的时候会通过读取SYS/BIOS共享内存区配置信息来获取,请参考内核
driver/dsp/syslink/notify_shmdriver/notify_shm_drv.c中实现)和动态配置方法(通过SharedRegion模块提供的API&SharedRegion_setEntry()来设置,但值得注意的是这个API只是把入口信息放入该处理器对应的共享内存查找表中,而其他处理器也需要在自己的系统中使用该API来加入该入口)。实际配置中需要指明共享内存区在各个处理器中映射的虚拟地址及堆栈设置等,如上图所示,对于proc0来说,SR0映射出的虚拟地址为0x,而对于Proc[1..3]映射出的地址则是0x;SR0中有部分预留区域且被配置成HeapMemMP(见3.5.3节);此外由上图还可以知道,SharedRegion&1是Proc1--Proc6构成的子系统的内部共享内存,且配置成使用HeapMemMP来管理。
SharedRegion模块由于其状态都存在处理器本地的内存中,因此其本身并不会占用共享内存区空间。所有的SharedRegion模块API都是用Gate用于进程互斥操作。
SharedRegion模块会为系统中每个处理器创建一个共享内存查找表。在这个查找表中包含了所有处理器与共享内存区的关系及相关设置。如果某块共享内存区对于某处理器是不能访问,那么在表中会设置为空。
在runtime时,共享内存查找表与共享内存区指针一起被用于快速的地址转换操作;
在共享内存查找表中最大入口数(即SharedRegion的个数)使用ShareRegion.numEntries静态配置。在系统runtime中可以使用静态配置或者动态配置增减入口数,但必须在更改后更新所有处理器的表。共享内存入口数越多耗费在地址转换上的时间越长,因此考虑到效率,尽可能设计少的入口数。
SharedRegion&0的意义比较特殊,在所有使用IPC组建的环境中都必须配置并且所有的处理器都有权限访问该共享区域,虽然应用程序也可以使用这部分内存,但不建议用户在不了解该区域详细的内存分布时使用,否则容易造成系统挂死。由DM8168的配置DVRRDK_xx.xx.xx.xx\dvr_rdk\mcfw\src_bios6\cfg\ti816x\SYSLINK_common.cfg可以看出该区域用于MsgQ。
配置方式举例
一般来说配置一个SharedRegion需要设置以下几个:
Base&-&The&base&address共享内存区的基地址,这个所谓的基地址实际上是映射后的虚拟地址,并非物理地址;
Len&-&The&length共享内存区的大小,对于同一片共享内存,其所有者的查找表中该项值应该是相同的;
Name&-&The&name&of&the&region该共享内存区的名字;
isValid&-&Whether&the&region&is&valid对于该处理器而言,是否具有权限去访问该共享内存区;
ownerProcId&-&The&id&of&the&processor&which&owns&the&region管理该内存区的处理器ID,该处理器具有创建HeapMemMP的权限,而其他处理器只有使用的权限;
cacheEnable&-&Whether&the&region&is&cacheable是否为该共享内存区创建cache;
cacheLineSize&-&The&cache&line&size&&cache的大小;
createHeap&-&Whether&a&heap&is&created&for&the&region.是否使用Heap(堆)管理该内存区域;
静态配置方法e.g.:DVRRDK_xx.xx.xx.xx\dvr_rdk\mcfw\src_bios6\cfg\ti816x\SYSLINK_common.cfg
/*&Set&Shared&Region&variables&by&picking&up&the&information&from&Platform
&*&memory&map
var&sr0MemSection&&&&&&&&&&&=&Program.cpu.memoryMap['SR0'];&//此处值都是有config_xxx.bld文件读取的,下同
var&sr1MemSection&&&&&&&&&&&=&Program.cpu.memoryMap['SR1'];
var&sr2MemSection&&&&&&&&&&&=&Program.cpu.memoryMap['SR2_FRAME_BUFFER_MEM'];
var&sr3MemSection&&&&&&&&&&&=&Program.cpu.memoryMap['SR3_FRAME_BUFFER_EXTRA'];
&*&&Need&to&define&the&shared&region.&The&IPC&modules&use&this
&*&&to&make&portable&pointers.&All&processors&need&to&add&this
&*&&call&with&their&base&address&of&the&shared&memory&region.
&*&&If&the&processor&cannot&access&the&memory,&do&not&add&it.
&&&&&&&&This&section&is&the&SR0&section&of&syslink&and&is
&&&&&&&&used&for&MsgQ's&that&are&present&on&different&processors.
&&&&&&&&A8&&-&NON-CACHED
&&&&&&&&M3&&-&NON-CACHED
&&&&&&&&DSP&-&NON-CACHED
SharedRegion.setEntryMeta(&0,
&&&&&&base:&&&&&&&&sr0MemSection.base,
&&&&&&len:&&&&&&&&&sr0MemSection.len,
&&&&&&name:&&&&&&&&sr0MemSection.name,
&&&&&&isValid:&&&&&true,
&&&&&&ownerProcId:&srOwnerProcId,
&&&&&&cacheEnable:&false,
&&&&&&cacheLineSize:&128,
&&&&&&createHeap:&&true&
注:在IPC中所给的例子除了设置以上需要设置SharedRegion.cacheLineSize、SharedRegion.numEnties、SharedRegion.translate等,如果不知道入口地址,可以将isValid设置成false,在runtime时通过计算来得到,然后是由SharedRegion_getEntry()来设置。
var&SharedRegion&=&xdc.useModule('ti.sdo.ipc.SharedRegion');
SharedRegion.cacheLineSize&=&32;//cache行缓冲字节数,暂时还没明白这个设置于上面例子中cacheLineSize:&128有何区别,如何不指定则使用默认值;
SharedRegion.numEntries&=&4;//总的共享内存区个数(入口数);
SharedRegion.translate&=&//是否需要做地址转换,如果设置为false,因为不需要做地址转换,则对总体性能有所提升;
动态配置方法e.g.:DVRRDK_xx.xx.xx.xx\dvr_rdk\mcfw\src_bios6\utils\src\utils_mem.c
SharedRegion_Entry&srE
Int&&&&&&&&&srStatus&=&SharedRegion_S_SUCCESS;
UInt32&&&&&srId;
SharedRegion_entryInit(&srEntry);
SharedRegion_getEntry(srId[i],&&srEntry);
&Vps_printf&(&&%d:&MEM:&Shared&Region&%d:&Base&=&0x%08x,&Length&=&0x%08x&(%d&MB)&\n&,\
&&&&&&&&&&&&&&&&&&&&Utils_getCurTimeInMsec(),&srId[i],srEntry.base,srEntry.len,&srEntry.len/());
if&((FALSE&==&srEntry.isValid)&&(0&!=&srEntry.len))
&&&&&&&&&&&&srEntry.isValid&&&&&=&TRUE;
&&&&&&&&&&&&do&{
&&&&&&&&&&&&&&&&srStatus&=&SharedRegion_setEntry(srId[i],&&srEntry);
&&&&&&&&&&&&&&&&if&(srStatus&!=&SharedRegion_S_SUCCESS)&{
&&&&&&&&&&&&&&&&&&&&Vps_printf(&&%d:&MEM:&ERROR:&SharedRegion_setEntry&(%d,&0x%08x)&FAILED&!!!&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&(status=%d)&\n&,&Utils_getCurTimeInMsec(),&srId[i],&&srEntry,&srStatus);
&&&&&&&&&&&&&&&&&&&&Task_sleep(1000);
&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&}&while&(srStatus&!=&SharedRegion_S_SUCCESS);
&if&(srEntry.len)
&&&&&&&&gUtils_heapMemHandle[i]&=&SharedRegion_getHeap(srId[i]);
&&&&&&&&UTILS_assert(gUtils_heapMemHandle[i]&!=&NULL);
&&&&&&&&gUtils_memClearBuf[i]&=&FALSE;
注:通常来说动态创建入口的方式不常用,一般只是用来更改原有配置。另外,如果想完全重新配置一个共享内存,可以在每个处理器上调用SharedRegion_clear()来清除该共享内存的入口信息。
SharedRegion中的内存可以使用Memory_alloc(IHeap_Handle&heap,&SizeT&size,&SizeT&align,&Ptr&eb)来动态分配。IHeap_Handle获取方式如上例中gUtils_heapMemHandle[i]&=&SharedRegion_getHeap(srId[i]);
SharedRegion_clearEntry()
ShareRegion_entryInit()
SharedRegion_getCacheLineSize()
SharedRegion_getEntry()、SharedRegion_setEntry()
SharedRegion_getHeap()
SharedRegion_getId
SharedRegion_getIdByName()
SharedRegion_getNumRegions()
SharedRegion_getPtr()
SharedRegion_getSRPtr()
SharegRegion_isCacheEnabled()
SharedRegion_translateEnabled()
SharedRegion_inValidSRPtr()
List模块封装了双向循环链表的操作API,SYS/BIOS侧见“ti.sdo.utils.List”实现,HLOS侧见:
“DVRRDK_xx.xx.xx.xx\ti_tools\syslink\syslink_x_xx_xx_xx\packages\ti\syslink\utils\hlos\List.c”中实现。提供的API也就是通常用于操作链表的一些api接口,这里就不多介绍了。
用于打印相关调试时使用,有点像CCS下的探针,在SysLink中根据所带的参数分了7种不同的探针,不多介绍,具体实现参考&“DVRRDK_xx.xx.xx.xx\ti_tools\syslink\syslink_x_xx_xx_xx\packages\ti\syslink\utils\common\Trace.c”
在调试TI的各种开发包(如HDVPSS&ISS等)时非常方便,加载syslink.ko时需要附加参数TRACE,如:
insmod&syslink.ko&TRACE=1
insmod&syslink.ko&TRACE=1&TRACEFAILURE=1&TRACECLASS=3
insmod&syslink.ko&TRACE=1&TRACEFAILURE=1&TRACEENTER=1&TRACECLASS=3
MultiProc模块用于多核处理器中唯一的标识处理器(多处理器ID管理,如果你看了fwload的程序,肯定注意到其中调用MultiProc_getId获取ProcId用于ProcMgr_open的参数),在使用该模块前,需要在IPC环境中使用*.cfg脚本来配置多处理器环境。
如:在DVRRDK_xx.xx.xx.xx\dvr_rdk\mcfw\src_bios6\cfg\ti816x\SYSLINK_c6xdsp.cfg中
/*******************************************************************************
*&SysLink&&SysMgr&initializations&-&IPC&is&a&part&of&sysLink
*******************************************************************************/
var&MultiProc&&&&&&&&&&&&&&=&xdc.useModule('ti.sdo.utils.MultiProc');
var&Notify&&&&&&&&&&&&&&&&&=&xdc.useModule('ti.sdo.ipc.Notify');
/*&The&DSP&is&processor&id&0&and&there&are&3&other&cores&*/
MultiProc.setConfig(&DSP&,&[&DSP&,&&VIDEO-M3&,&&VPSS-M3&,&&HOST&]);
除了上述的静态设置方法外,还可以主处理器起来后(此时从处理器均未startup,不能强制配置其Proc_ID)通过GPIO、Nand、EEPROM等手段动态获取,使用MultiProc_setLocalId()&API设置,但cfg文件配置还是需要的,只不过此时相应处理器名称设置为NULL。
MultiProc模块提供的API主要有:
获取/设置多核系统的基ID:
MultiProc_getBaseIdOfCluster()/MultiProc_setBaseIdOfCluster();
通过处理器名字检索其ID:MultiProc_getId();
通过处理器ID检索其名字:MultiProc_getName();
查询多核系统的处理器数:
MultiProc_getNumProcessors()/MultiProc_getNumProcsInCluster();
返回当前处理器的ID:MultiProc_self();
设置处理器ID:MultiProc_setLocalId();
NameServer直译为名称服务器,大致看了下实现过程,有点像简单的网络中的DNS(Domain&Name&Server),即可以根据名字查找某对象(DNS中可以根据网址查找到对应网址的IP)。
NameServer的实现原理:基于链表的结构,并且可以根据设置决定是否对value域进行Hash编码(查了下资料,这个可能是从java中吸其精华而改造来的),其基本数据结构如下:
typedef&struct&NameServer_TableEntry_tag&{
&&&&List_Elem&&&&&&&&&&&&&&&&&
&&&&/*&List&element&*/
&&&&UInt32&&&&&&&&&&&&&&&&&&&&
&&&&/*&Hash&value&*/
&&&&String&&&&&&&&&&&&&&&&&&&&
&&&&/*&Name&portion&of&the&name/value&pair.&*/
&&&&UInt&&&&&&&&&&&&&&&&&&&&&&
&&&&/*&Length&of&the&value&field.&*/
&&&&Ptr&&&&&&&&&&&&&&&&&&&&&&&
&&&&/*&Value&portion&of&the&name/value&entry.&*/
&&&&Bool&&&&&&&&&&&&&&&&&&&&&&
&&&&/*&Does&the&hash&collides?&*/
&&&&struct&NameServer_TableEntry_tag&*&
&&&&/*&Pointer&to&the&next&entry,&used&incase&of&collision&only&*/
}&NameServer_TableE
NameServer提供了以下API:
NameServer_Params_init()
NameServer_create()/NameServer_delete()
NameServer_getHandle()
NameServer_add()
NameServer_addUInt32()
NameServer_get()/NameServer_getUInt32
NameServer_getLocal()/NameServer_getLocalUInt32()
NameServer_removeEntry()/NameServer_remove()
在SysLink中很多组件都用到了NameServer,如FrameQ、HeapMemMP、GateMP、ListMP、MessageQ、RingIO等等。
=======================================================================================================
转载请注明原文地址:http://blog.csdn.net/crushonme/article/details/
部分模块由于没有用过,也没看到SysLink包中提供例子,所以只是简略介绍。
文中给出的API并不会给出相关解释,请参考SysLink&API&Reference&Documentation
注:这部分涉及到cfg脚本(XDC)配置,用得比较少,涉及到很多TI及其他公司封装的java类库,后续等俺学习了再详细说明。
有任何问题和意见可以在评论区给我留言,我们一起探讨和学习
=======================================================================================================
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:11673次
排名:千里之外
原创:48篇
(1)(1)(10)(6)(13)(15)(6)(1)(1)

我要回帖

更多关于 ipc几种通信方式 的文章

 

随机推荐