=======================================================================================================
转载请注明原文地址:http://blog.csdn.net/crushonme/article/details/10287693
部分模块由于没有用过,也没看到SysLink包中提供例子,所以只是简略介绍。
文中给出的API并不会给出相关解释,请参考SysLink API Reference Documentation
注:这部分涉及到cfg脚本(XDC)配置,用得比较少,涉及到很多TI及其他公司封装的java类库,后续等俺学习了再详细说明。
=======================================================================================================
绪论:Davinci中的多核系统一般由GPP+DSP构成,也就是所谓的异构多核(同构是指内部核的结构是相同的,而异构是指内部的核结构是不同的),为了为异构多核处理器间提供高效的异构多核协作,需要建立异构多核间的通信机制。在TI提供的异构多核间通信组件SysLink中,核间通信机制为用户提供了多种实现方法。下面的内容将介绍SysLink架构、特性和相关的API。
关键词
缩写词 | 解释 |
HLOS | Higher Level Operating System |
RTOS | Real Time Operating System |
CCS | Code Composer Studio |
IPC | Inter-Processor Communication |
GPP | General Purpose Processor e.g.ARM |
DSP | Digital Signal Processor e.g. C64X |
CGTools | Code Gen Tools e.g.Compiler Linker Archiver |
SysLink工具包为异构多核之间的通信提供基础开发接口,使得多核系统之间更方便的交换信息。在多核异构系统中,每个核心运行的操作系统和所处地位各不相同。所运行的OS可以是HLOS,如Linux、WinCE等,也可以是RTOS如SYS/BIOS或者QNX。异构多核系统中主处理器(Host Processor)肩负着控制从处理器(Slave Processor)的责任。
广义上的SysLink包含了运行在HLOS上的SysLink和运行在RTOS上的IPC。其基本架构如下图所示:
SysLink工具包主要针对于嵌入式操作系统中的应用,主要由以下组件构成:
系统管理模块(IPC module)为方便多核管理提供了简单快捷的方法,此外也为系统资源(e.g.系统内存)的管理提供了接口。
IPC 模块提供的功能包括:
ProcMgr模块为从处理器提供了以下services:具体例子可以参见DVRRDK_xx.xx.xx.xx\dvr_rdk\mcfw\src_linux\utils\fw_load中firmware load的例子。
在SysLink系统中,为了方便管理,每个处理器都会被编码(即Processor ID);如图中所示,在该系统中使用了硬件抽象层来屏蔽底层硬件差异,这样做的好处就是为不同的底层硬件提供了通用的软件接口。
注:
SysLink下定义了以下几种通信机制:
Notify组件将硬件中断抽象成多组逻辑事件,是一种简单快捷的发送低于32bit信息的通信方式。
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()来检查该事件是否可用,即该中断号上的该事件号是否被注册。
Module | Event Ids |
FrameQBufMgr | 0 |
FrameQ | 1 |
MessageQ(TransportShm) | 2 |
RingIO | 3 |
NameServerRemoteNotify | 4 |
MessageQ,顾名思义,基于队列的消息传递,可不是MaggieQ噢,哈哈。
MessageQ有以下特点:
MessageQ组件常用在满足以下条件的场景中:
MessageQ组件提供了以下几个API:
ListMP实现了多宿主双向循环链表,即该双向循环链表为多个处理器共同拥有,可以由多个处理器共同维护,共同使用。
ListMP的实现区别于一般的双向循环链表,因此它不仅具有双向循环链表的特性外,还增添了其他的特性,比如以下几点:
ListMP组件常用于满足一下条件的场景中:
GateMP是针对于多处理器共享资源的一种保护机制,就如其名字一样,把共享资源比作房子,那么GateMP就是这个房子的门。GateMP组件实现了开关门的机制,用于保护共享资源一次只被一个处理器读写。根据SOC硬件资源配置的不同,GateMP的实现有所不同。对于硬件支持Hardware Spinlock的可以基于H/W spinlock来实现GateHwSpinlock;而对于没有该硬件资源的系统中,则使用软件方法(Peterson算法)来实现GatePeterson。
GateMP组件框架如下:
GateMP组件对用户提供了以下API接口:
注:如果某个处理器在想使用被某个GateMP保护的共享资源,那么该处理器会被阻塞,直到该资源被释放(即GateMP_leave())。
HeapMP主要包括HeapBufMP和HeapMemMP,用于共享内存区的堆内存配置和管理。
HeapMP具备以下几个特征:
HeapBufMP为用户提供了固定大小的缓冲池管理接口;
HeapBufMP组件为用户提供的API接口如下:
在SysLink包中并没有找到相关API,但SysLink UserGuide中有提到.
HepMultiBufMP为用户提供了可配置大小的缓冲池管理接口。
HeapMemMp为用户提供了基于堆的可变大小的内存管理机制。
HeapMemMp组件为用户提供的接口如下:
Utility Modules包括SharedRegion(IPC中属于类库ti.sdo.ipc.SharedRegion)、List、Trace、MultiProc、NameServer等,这些模块是上层组件实现的基础。在IPC包中,该组件对应于类库ti.sdo.utils.
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映射出的虚拟地址为0x80000000,而对于Proc[1..3]映射出的地址则是0x90000000;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需要设置以下几个:
静态配置方法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()来设置。
e.g.
var SharedRegion = xdc.useModule('ti.sdo.ipc.SharedRegion'); SharedRegion.cacheLineSize = 32;//cache行缓冲字节数,暂时还没明白这个设置于上面例子中cacheLineSize: 128有何区别,如何不指定则使用默认值; SharedRegion.numEntries = 4;//总的共享内存区个数(入口数); SharedRegion.translate = true;//是否需要做地址转换,如果设置为false,因为不需要做地址转换,则对总体性能有所提升;
动态配置方法e.g.:DVRRDK_xx.xx.xx.xx\dvr_rdk\mcfw\src_bios6\utils\src\utils_mem.c
SharedRegion_Entry srEntry; 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/(1024*1024)); 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]);
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主要有:
typedef struct NameServer_TableEntry_tag { List_Elem elem; /* List element */ UInt32 hash; /* Hash value */ String name; /* Name portion of the name/value pair. */ UInt len; /* Length of the value field. */ Ptr value; /* Value portion of the name/value entry. */ Bool collide; /* Does the hash collides? */ struct NameServer_TableEntry_tag * next; /* Pointer to the next entry, used incase of collision only */ } NameServer_TableEntry;
NameServer提供了以下API:
在SysLink中很多组件都用到了NameServer,如FrameQ、HeapMemMP、GateMP、ListMP、MessageQ。RingIO等等。
=======================================================================================================
转载请注明原文地址:http://blog.csdn.net/crushonme/article/details/10287693
部分模块由于没有用过,也没看到SysLink包中提供例子,所以只是简略介绍。
文中给出的API并不会给出相关解释,请参考SysLink API Reference Documentation
注:这部分涉及到cfg脚本(XDC)配置,用得比较少,涉及到很多TI及其他公司封装的java类库,后续等俺学习了再详细说明。
有任何问题和意见可以在评论区给我留言,我们一起探讨和学习
=======================================================================================================