Davinci的异构多核间通信基础组件SysLink 2.0

=======================================================================================================

转载请注明原文地址: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。其基本架构如下图所示:

Davinci的异构多核间通信基础组件SysLink 2.0_第1张图片


SysLink工具包主要针对于嵌入式操作系统中的应用,主要由以下组件构成:

  • 系统管理(System Manager)
  • 处理器管理(Processor Manager——PM)
  • 核间通信(Inter-Processor Communication——IPC)
  • 其他模块(Utility Modules)

1、系统管理

        系统管理模块(IPC module)为方便多核管理提供了简单快捷的方法,此外也为系统资源(e.g.系统内存)的管理提供了接口。

        IPC 模块提供的功能包括:

  • SysLink系统初始化(syslink_setup()syslink_destroy())并为其他SysLink组件分配内存,包括IPC模块和PM模块(MemoryOS_setup()、Ipc_setup(&config))
  • 系统配置:任何系统级别的配置信息是由系统管理;

2、处理器管理

        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流程图:
Davinci的异构多核间通信基础组件SysLink 2.0_第2张图片
Processor Manager系统框架图如下

Davinci的异构多核间通信基础组件SysLink 2.0_第3张图片

        在SysLink系统中,为了方便管理,每个处理器都会被编码(即Processor ID);如图中所示,在该系统中使用了硬件抽象层来屏蔽底层硬件差异,这样做的好处就是为不同的底层硬件提供了通用的软件接口。

注:

  1. SysLink中从处理器Loader文件理论上支持多种格式,在SysLink Release版本中主要支持COFF和ELF。在TI的编译系统中,可以以可执行文件的后缀名来区别COFF文件和ELF文件,后缀名中带有‘e’的是ELF(如:xxx.xe64P),不带‘e’的是COFF文件(如:xxx.x64P)。
  2. 当前的ELF Loader只支持顺序加载,即只有当一个从处理器加载并启动后才能去加载下一个从处理器,不支持并行加载。

3、处理器间通信协议(Inter-Processor Communication Protocols)

        SysLink下定义了以下几种通信机制:

  • Notify
  • MessageQ
  • ListMp
  • GateMp
  • HeapBufMp
  • HeapMemMp
  • FrameQ(通常用于raw 视频数据)
  • RingIO(通常用于音频数据)
       这些通信机制的接口都有一下几个共同点:

  1. 所有IPC通信机制的接口都由系统规范化的命名;
  2. 在HLOS端,所有IPC 接口都有<Module>_setup() and <Module>_destroy() API用于初始化或者销毁相应的IPC Module;部分初始化还需要提供配置接口,<Module>_config();
  3. 所有的实例化都需要使用<Module>_create()来创建,使用<Module>_delete()来删除;
  4. 在更深层次使用IPC时需要用API <Module>_open()来获取handle,在结束使用IPC时需要用API <Module>_close()来回收handle;
  5. IPC的配置多数都是在SYS/BIOS下完成配置的,对于支持XDC配置的则可以使用静态配置方法;
  6. 每个IPC模块都支持Trace信息用于调试,而且支持不同的trace等级;
  7. 部分IPCs提供了专门的APIs来用于提取分析信息;

3.1、Notify

Notify组件将硬件中断抽象成多组逻辑事件,是一种简单快捷的发送低于32bit信息的通信方式。

Notify组件提供了以下接口:

  1. 初始化并配置Notify组件;Notify_attach();
  2. 注册/注销事件;Notify_registerEvent()/Notify_unregisterEvent()/Notify_registerEventSingle()/Notify_unregisterEventSingle()
  3. 发送带参数的事件给某处理器;Notify_sendEvent()
  4. 通过回调函数接收事件;Notify_FnNotifyCbck()
  5. 使能/禁用事件;Notify_diableEvent()/Notify_enableEvent()
  6. 其他逻辑接口;Notify_eventAvailable()/Notify_intLineRegistered()/Notify_numIntLines()/Notify_restore()
注:

  1. 同一个中断号可以注册多个事件,同一个事件可以有多个回调函数或者多个宿主(可以是处理器、线程或者任务),事件被触发后所有宿主都会被唤醒;
  2. 一个事件可以接收多个宿主发送来的通知(notification),事件所携带的参数最大支持32bit;
  3. 事件是有优先级的,EventId越小优先级越高,事件0的优先级最高,随着EventId增大优先级依次递减;当多个事件被触发,优先级最高的会最先响应;
  4. 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

3.2、MessageQ

MessageQ,顾名思义,基于队列的消息传递,可不是MaggieQ噢,哈哈。

MessageQ有以下特点:

  • 实现了处理期间变长消息的传递;
  • 消息的传递都是通过操作消息队列来实现的;
  • 每个消息队列可以有多个写者,但只能有一个读者;每个任务(task)可以对多个消息队列进行读写;
  • 一个宿主在准备接收消息时,必须先创建消息队列,而在发送消息前,需要打开预定的接收消息队列;

MessageQ组件常用在满足以下条件的场景中:

  1. 在消息传递中有多个写者,但仅有一个读者;
  2. 所需要传递的消息超过32bit,且长度可变;读写者的缓冲区大小相同;
  3. 处理期间需要频繁传递消息,在这种情况下,消息被依次放入队列,能保证不会丢消息;
  4. 消息队列为空时,调用MessageQ_get()获取消息时会被阻塞,直到消息队列被写入消息;
  5. 支持处理器间移动消息队列,在这种情况下,调用MessageQ_open()来定位队列位置,而消息传递部分代码不需要改动;

MessageQ组件提供了以下几个API:

  1. 消息队列初始化:MessageQ_Params_init()
  2. 消息队列创建/销毁:MessageQ_create()/MessageQ_delete(),create创建消息队列,并分配相应存储空间
  3. 消息队列打开/关闭:MessageQ_open()/MessageQ_close(),open时会返回远程处理器上的QueID的地址。
  4. 为消息队列分配堆内存:MessageQ_alloc()/MessageQ_free()
  5. 为消息队列注册/注销堆内存:MessageQ_registerHeap()/MessageQ_unregisterHeap()
  6. 向消息队列中放入/获取消息:MessageQ_put()/MessageQ_get()
  7. 其他逻辑API:
  • 获取消息队列ID:MessageQ_getQueueId()
  • 获取消息队列中消息数:MessageQ_count()
  • 在消息队列中嵌入消息:MessageQ_setReplyQueue()
  • 为消息队列解阻塞:MessageQ_unblock()
  • 为调试消息队列加入Trace:MessageQ_setMsgTrace()


3.3、ListMP

ListMP实现了多宿主双向循环链表,即该双向循环链表为多个处理器共同拥有,可以由多个处理器共同维护,共同使用。

ListMP的实现区别于一般的双向循环链表,因此它不仅具有双向循环链表的特性外,还增添了其他的特性,比如以下几点:

  • 实现了简单的多宿主协议,支持多个读写者(multi-reader、multi-writee);
  • 使用Gate作为内部保护机制,防止多个宿主处理器同时访问该链表;
ListMP的实现并未加入通知机制,如果需要的话,可以在外部封装是引入Notify机制来实现;使用ListMP机制来管理的buffers都需要从共享内存区分配,包括从堆内存分配的buffers以及动态分配的内存。

ListMP组件常用于满足一下条件的场景中:

  1. 需要被多个宿主访问并且需要频繁传递消息或者数据;
  2. 可用于无规则的消息传递,基于链表实现,因此读者可以遍历所有对象,并选出需要的对象进行处理;如果硬件支持快速队列,则无法完成队列遍历操作(WHY)
  3. 可以自定义消息优先级,同样是基于链表实现,读者可以随意的选择在链表头部还是链表的尾部来插入消息或者实现链表对象的位置调整,进而实现消息的优先级选择;如果硬件支持快速队列,则无法完成队列遍历操作(WHY);
  4. 无内置通知机制,可以灵活的外部通知机制来实现。譬如根据实际情况,选用Notify来实现,亦或是使用选用MessageQ则可以使用最少的中断资源实现性能优良的通知机制,缺点是需要额外的代码实现通知机制。
ListMP组件提供了以下API接口:

  1. ListMP参数初始化:ListMP_Params_init()
  2. ListMP创建/销毁:ListMP_create()/ListMP_delete()
  3. ListMP打开/关闭:ListMP_open()/ListMP_close()
  4. ListMP相关链表操作:
  • 判断链表空:ListMP_empty()
  • 获取保护锁:ListMP_getGate()
  • 获取链表头/表尾:ListMP_getHead()/ListMP_getTail()
  • 链表插入操作:ListMP_insert()
  • 获取链表上游元素/下游元素:ListMP_next()/ListMP_prev()
  • 插入元素到链表头/尾:ListMP_putHead()/ListMP_putTail()
  • 删除元素:ListMP_remove()


3.4、GateMP

GateMP是针对于多处理器共享资源的一种保护机制,就如其名字一样,把共享资源比作房子,那么GateMP就是这个房子的门。GateMP组件实现了开关门的机制,用于保护共享资源一次只被一个处理器读写。根据SOC硬件资源配置的不同,GateMP的实现有所不同。对于硬件支持Hardware Spinlock的可以基于H/W spinlock来实现GateHwSpinlock;而对于没有该硬件资源的系统中,则使用软件方法(Peterson算法)来实现GatePeterson。

GateMP组件框架如下:

Davinci的异构多核间通信基础组件SysLink 2.0_第4张图片

GateMP组件对用户提供了以下API接口:

  1. GateMP初始化:GateMP_Params_init();
  2. GateMP创建/删除:GateMP_create()/GateMP_delete();
  3. GateMP打开/关闭:GateMP_open()/GateMP_close();
  4. 进入/离开GateMP保护:GateMP_enter()/GateMP_leave();
  5. 获取当前GateMP的保护类型:GateMP_getLocalProtect()/GateMP_getRemoteProtect();

注:如果某个处理器在想使用被某个GateMP保护的共享资源,那么该处理器会被阻塞,直到该资源被释放(即GateMP_leave())。


3.5、HeapMP

HeapMP主要包括HeapBufMP和HeapMemMP,用于共享内存区的堆内存配置和管理。

HeapMP具备以下几个特征:

  • 支持多宿主,即无论是运行HLOS的主处理器还是运行SYS/BIOS的从处理器都可以配置和管理堆内存;
  • 可以将共享内存区配置成缓冲池(buffer pools);
  • 可以从共享内存区分配和释放缓冲区;

3.5.1、HeapBufMP

HeapBufMP为用户提供了固定大小的缓冲池管理接口;

HeapBufMP组件为用户提供的API接口如下:

  1. HeapBufMP创建/删除:HeapBufMP_create();HeapBufMP_delete();
  2. HeapBufMP打开/关闭:HeapBufMP_open();HeapBufMP_close();
  3. HeapBufMP参数初始化:HeapBufMP_Params_init();
  4. HeapBufMP分配/释放内存:HeapBufMP_alloc();HeapBufMP_free();
  5. HeapBufMP获取所有状态:HeapBufMP_getExtendedStats();HeapBufMP_getStats();


3.5.2、HeapMultiBufMP

在SysLink包中并没有找到相关API,但SysLink UserGuide中有提到.

HepMultiBufMP为用户提供了可配置大小的缓冲池管理接口。


3.5.3、HeapMemMP

HeapMemMp为用户提供了基于堆的可变大小的内存管理机制。

HeapMemMp组件为用户提供的接口如下:

  1. HeapMemMP参数初始化:HeapMemMP_Params_init();
  2. HeapMemMP创建/删除:HeapMemMP_create()/HeapMemMP_delete();
  3. HeapMemMP打开/关闭:HeapMemMP_open()/HeapMemMP_close();
  4. HeapMemMP分配/释放内存:HeapMemMP_alloc()/HeapMemMP_free();
  5. HeapMemMP获取内存状态:HeapMemMP_getExtendedStats()/HeamMemMP_getStats();
  6. HeapMemMP恢复内存初始状态:HeapMemMP_restore();

3.6、FrameQ

FrameQ是专门为传递视频帧而设计出来的组件。FrameQ的基本数据结构是可用于queue/dequeue数据的数据队列,封装了视频帧缓存指针、帧数据类型、帧宽、帧高、时间戳等信息。
对于FrameQ模块具有以下特征:
  • 支持多个读者,但写者唯一;
  • 可以分配和释放Frames;
  • 可以对指向同一块内存区多次分配和初始化成新的帧buffer;
  • FrameQ允许有多个队列,在多通道的运用中,视频帧会根据通道号被分配到相应的帧队列中;
FrameQ中用于buffer管理的模块称为FrameQBufMgr,该模块用于提供buffer管理接口和通知机制。

FrameQ提供以下API接口:
  1. FrameQ组件初始化/销毁:FrameQ_setup()/FrameQ_destroy();
  2. 创建/删除FrameQ实例:FrameQ_create()/FrameQ_delete();
  3. 打开/关闭FrameQ实例:FrameQ_open()/FrameQ_close();FrameQ_openByAddr();
  4. 为FrameQ实例分配/释放内存:FrameQ_alloc()/FrameQ_free();FrameQ_allocv/FrameQ_freev();
  5. 插入/释放FrameQ中帧:FrameQ_put()/FrameQ_get();FrameQ_putv()/FrameQ_getv();
  6. 复制给定的帧:FrameQ_dup();
  7. 注册/注销FrameQ通知:FrameQ_registerNotifier()/FrameQ_unregisterNotifier();
  8. 强制发送通知:FrameQ_sendNotify()
  9. 获取FrameQ中有效帧数/已被释放的帧数:FrameQ_getNumFrames()/FrameQ_getNumFreeFrames();                   FrameQ_getvNumFrames()/FrameQ_getvNumFreeFrames()
  10. FrameQ控制:FrameQ_control()
  11. 获取FrameQ的头指针:FrameQ_getExtendedHeaderPtr();
  12. 获取帧buffer/帧大小/帧数:FrameQ_getFrameBuffer()/FrameQ_getFrameBufSize()/FrameQ_getNumFrameBuffers();
  13. 获取空数据帧大小/位置:FrameQ_getFrameBufValidSize()/FrameQ_getFrameBufDataStartOffset();
  14. 设置空数据帧大小/位置:FrameQ_setFrameBufValidSize()/FrameQ_setFrameBufDataStartOffset();
  15. 获取FrameQ默认设置:FrameQ_getConfig();

3.7、RingIO

RingIO是基于数据流的环形缓冲buffer,而且针对于音视频数据的特性做了优化。
RingIO支持一下特性:
  • 仅支持一个读者和一个写者;
  • 读写相对独立,可以在不同的进程或者处理器中同时进行读写操作;
RingIO为用户提供了以下接口:
  1. RingIO参数初始化:RingIO_Params_init();
  2. 创建/删除RingIO对象:RingIO_create()/RingIO_delete();
  3. 打开/关闭RingIO对象:RingIO_open()/RingIO_close();RingIO_openByAddr();
  4. 获取共享内存请求:RingIO_sharedMemReq();
  5. 注册/注销RingIO通知:RingIO_registerNotifier()/RingIO_unregisterNotifier();
  6. 强制发送RingIO通知:RingIO_sendNotify();
  7. 获取RingIO通知类型:RingIO_setNotifyType();
  8. 设置/获取水印标志/通知类型:RIngIO_setWaterMark()/RIngIO_getWaterMark()
  9. 获取/释放RingIO数据:RingIO_acquire()/RingIO_release();
  10. 设置/获取RingIO属性:RingIO_setvAttribute()/RingIO_getvAttribute();
  11. 设置/获取RingIO固定大小的属性:RingIO_setAttribute()/RingIO_getAttribute();
  12. 刷新RingIO的buffer:RingIO_flush();
  13. 获取有效/空buffer大小:RingIO_getValidSize()/RingIO_getEmptySize();
  14. 获取有效/空属性大小:RingIO_getValidAttrSize()/RingIO_getEmptyAttrSize();
  15. 获取用户需求buffer的大小/位置:RingIO_getAcquiredSize()/RingIO_getAcquiredOffset();


4、公共组件(基础组件)

Utility Modules包括SharedRegion(IPC中属于类库ti.sdo.ipc.SharedRegion)、ListTraceMultiProcNameServer等,这些模块是上层组件实现的基础。在IPC包中,该组件对应于类库ti.sdo.utils.


4.1、SharedRegion(非常重要,SysLink模块最基础的模块)

4.1.1、SharedRegion总览

SharedRegion顾名思义,是共享内存区的意思。SharedRegion模块负责管理共享内存区。在一个有共享内存的多核架构中,普遍会遇到共享内存映射虚拟地址转换问题,如下图所示:

Davinci的异构多核间通信基础组件SysLink 2.0_第5张图片

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。

4.1.2、SharedRegion配置方式举例

一般来说配置一个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()来设置。

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()来清除该共享内存的入口信息。

4.1.3、如何使用SharedRegion中的内存

SharedRegion中的内存可以使用Memory_alloc(IHeap_Handle heap, SizeT size, SizeT align, Ptr eb)来动态分配。IHeap_Handle获取方式如上例中gUtils_heapMemHandle[i] = SharedRegion_getHeap(srId[i]);


4.1.4、相关API

  • 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()

4.2、List

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接口,这里就不多介绍了。


4.3、Trace

用于打印相关调试是使用,有点像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

4.4、MultiProc

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主要有:

  1. 获取/设置多核系统的基ID:MultiProc_getBaseIdOfCluster()/MultiProc_setBaseIdOfCluster();
  2. 通过处理器名字检索其ID:MultiProc_getId();
  3. 通过处理器ID检索其名字:MultiProc_getName();
  4. 查询多核系统的处理器数:MultiProc_getNumProcessors()/MultiProc_getNumProcsInCluster();
  5. 返回当前处理器的ID:MultiProc_self();
  6. 设置处理器ID:MultiProc_setLocalId();

4.5、NameServer

NameServer直译为名称服务器,大致看了下实现过程,有点像简单的网络中的DNS(Domain Name Server),即可以根据名字查找某对象(DNS中可以根据网址查找到对应网址的IP)。
NameServer的实现原理:基于链表的结构,并且可以根据设置决定是否对value域进行Hash编码(查了下资料,这个可能是从java中吸其精华而改造来的),其基本数据结构如下:
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:

  • 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/10287693

部分模块由于没有用过,也没看到SysLink包中提供例子,所以只是简略介绍。

文中给出的API并不会给出相关解释,请参考SysLink API Reference Documentation

注:这部分涉及到cfg脚本(XDC)配置,用得比较少,涉及到很多TI及其他公司封装的java类库,后续等俺学习了再详细说明。

有任何问题和意见可以在评论区给我留言,我们一起探讨和学习

=======================================================================================================


你可能感兴趣的:(dm8168,SYSBIOS,DM81XX,SysLink,多核通信)