|
RF5是德州仪器TI公司新近推出的DSP软件开发的起步代码参考框架,它以DSP/BIOS为基础,利用其中的数据处理元素和数据通信元素方便快捷地完成DSP软件的设计与开发。RF5是RF的最新版本,其区别于RF1和RF3的显著特点是其支持动态对象创建和支持线程(任务)挂起功能,因此适合系统较复杂的应用场合。
RF5 主要实现三个功能,存储管理,线程模型和通道封装,对于不同的应用,我们只需在这三个元素上做修改,而对于整个应用程序,不用从头设计,这样大大简化了开发者的开发难度,缩短了开发时间。
RF5适用于包含大量的算法,且要求多线程,多通道的应用,如图像处理,多媒体应用等,以Ti提供的实例mpeg2loopback为例,对RF5进行分析。
RF5包含的元素有:
1 线程(Thread):
RF5框架包含四个基本的数据处理元素,处在最顶层的是线程,线程总是顺序的执行所包含的通道,线程在一个比较高级的级别上把数据组织在一起,他们可以与别的线程,设备驱动以及别的类似结构进行通讯,在mpeg2lookback实例中,创建了三个线程分别是tskVideoInput, tskVideoOutput和tskProcess。每个线程都在不断的等待消息,处理数据,并将结果发送给其他的线程,同时有可能还要发送同步消息给其他线程以实现线程间的通讯,这里使用的机制是SCOM模块。
每个线程都是进行数据处理的一个单元,有的处理是简单的,有的处理是相对复杂的过程,简单的线程可以不包括任何的通道,而进行复杂数据处理的线程有可能包含多个的通道。
2 通道(Channel):
RF5提供了一种通道结构是为了更方便的封装算法,这可以理解通道为并行里的串行,因为线程的执行就是由通道的串行执行来完成的,一个通道包含一组核(Icell)。其主要任务就是依次顺序的执行所包含的核,主要执行的流程为:首先需要初始化通道模块,然后建立通道对象,注册该通道所包含的核对象,接着依次执行每个核,执行完成了后就销毁对象,最后退出。每个通道可以包含多个核,每个核都要进行初始化后再调用CHAN_regCell注册。
通道对象的结构如下:
typedef struct CHAN_Obj {
ICELL_Obj *cellSet; /* set of cells in the channel */
Uns cellCnt; /* number of cells in the cellSet */
CHAN_State state; /* state of the channel */
Bool (*chanControlCB)(CHAN_Handle chanHandle); /* optional control function */
} CHAN_Obj;
线程一般不定义通道对象,但是在CHAN_open()调用中初始化它们。CHAN_open()的最后一个参数是通道属性(CHAN_Attrs)结构体的地址。如果最后一个参数是NULL,那么CHAN_open()使用默认的参数。如果要想使用不同的参数,就要声明一个CHAN_Attrs的结构体,并需初始化为CHAN_ATTRS宏所定义的初值,然后根据需要可以修改其中相应的域的值。通常,其中的通道状态参数CHAN_State state域默认为CHAN_ACTIVE,通道控制回调函数参数域Bool (*chanControlCB)(CHAN_Handle chanHandle)默认为NULL。如果通道控制回调函数不是空,那么在任何的cell调用执行之前都会先调用此回调函数。
一个典型的设置:一个线程为每一个通道建立一个CHAN_Obj对象(或者一组类似的对象),并且为每一个cell建立一个ICELL_Obj对象(或者是与每个通道相对应的一组ICCE_Obj对象)。在线程初始化ICELL_Obj之后就会调用下面的函数:
备注其中的cell 指向cell 对象的指针, inputIcc/outputIcc是相应的cell的 ICC 对象,这个调用计算单元需要的空间,并分配给定的ICC对象给单元cell。
CHAN_regCell( cell, inputIcc, 1, outputIcc, 1 );
当所有的cells都已经创建并初始化之后,线程调用CHAN_open()函数来为每一个指定的通道(chanNum)传递cell对象(cellList)。这个函数创建所有的XDAIS算法,并且如果单元细胞定义了cellOpen函数,则会调用每一个单元细胞的cellOpen函数,.
CHAN_open( chanList[ chanNum ], cellList, numCells, NULL/* default attributes */ );
最后,在运行时,线程为每一个通道(chanNum)调用CHAN_execute函数开始执行:
CHAN_execute( chanList[ chanNum ], NULL /* arg to cells */ );
3 核(Icell):
核实际上就是ICELL接口对象,基于RF5的应用常常包含大量的算法和通道。为了便于算法集中到应用中,RF5提出了核的概念。一个核就是包含一种 XDAIS算法的容器,一个RF5通道对象可以包含多个核,也即是包含多个算法。通道通过核来调用算法,实际上,真正的数据处理是在XDAIS算法中进行的,核只是提供一个调用算法的接口,这大大简化了工作量,便于移植。
该接口包含一个重要的结构:ICELL_Fxns,该结构包含一组函数指针。通道通过调用这些函数来调用算法,其中包含一个关键的函数 cellExecute,这个函数的功能是调用XDAIS算法来执行,上面的通道执行函数CHAN_execute就包含了每个cellExecute的调用。
4 ICC模块
ICC模块是用来管理在核之间以及核与其他线程之间的数据通讯,我们知道线程间的数据传输是通过SCOM模块来实现的,每个ICC模块管理一个或者多个 ICC对象,每个核都有一组输入和输出ICC对象。这些对象是通过CHAN_regCell()来注册到相应的通道里。
5 同步通讯机制(SCOM)
ThrProcess中包含两个SCOM对象,RF5使用SCOM对象来实现线程间的通讯。SCOM消息是用户自定义的一个机构,一个线程通过调用 SCOM_putMsg()函数将SCOM消息放置到一个SCOM队列中,发送给其他的线程,或者通过调用SCOM_getMsg()函数从队列中获取消息。一般情况下,发送消息指明接收线程所要读取的数据缓冲区的地址(以指针形式),接收消息指明发送线程所要写入的数据缓冲区的地址。在 mape2loopback实例中,thrProcess要从thraVideoInput接收消息,并发送消息给thrVideoOutput输出图像。RF5使用SCOM来实现线程间的通讯:thrProcess拥有一些缓冲区,需要thrVideoInput写或thrVideoOutput读,所以thrProcess通过SCOM告诉thrVideoIput和thrVideoOutput线程数据缓冲区的地址,同时还要保证两个线程不会同时访问同一个缓冲区。thrProcess创建了两种消息以分别和两个线程进行通讯,scomMsgRx和scomMsgTx。scomMsgRx指定了被 thrVideoInput写的缓冲区地址,scomMsgTx指定了被thrVideoOutput读的缓冲区地址。
在实际的操作中,可以将SCOM看作是一种同步标记,它用来区分模块内存是否正在被其他线程所使用,这样就可以防止内存访问的冲突。整个系统中包含很多存储区,这些存储区很有可能在某一时刻正在被某一线程访问,为了保证在任意时刻只有一个线程访问某该存储区,当前正在访问这一内存块的线程通过发送SCOM消息给与这一内存块有关联的线程,告诉它们,“我正在访问呢,你等会再来吧”。当它访问完后,放弃了这一内存块的占有权,再通过SCOM消息告诉相关联的线程,“我用完了,你可以用了。”于是相关联的线程就可以访问了。
6 ALGRF模块—算法的实例化Algorithm Instantiation
ALGRF Module用DSP/BIOSMEM内存管理器来创建和删除XDAIS算法的模块。参考框架服务简化XDAIS部件的使用。所有符合XDAIS标准的算法都必须使用一个标准的接口——IALG接口。ALGRF使用算法的IALG来实现对XDAIS算法的实例化。任何符合XDAIS标准的算法都可以被 ALGRF所使用。
用户代码不必直接的调用ALGRF函数,这个工作由CHAN和其他的库函数来完成。例外的是cell wrappers中的ALGRF_activate/deactivate序列化:如果cell中的XDAIS算法执行 IALG_active/deactivate函数,细胞需要调用两个ALGRF函数来完成。
三个模块来简化IALG接口创建算法对象:RF5使用ALGRF模块来创建,配置,删除XDAIS算法实例;ALG模块使用CCStudio作为通用目的使用;并且不用DSP/BIOS MEM模块分配内存。ALGMIN是三个中的最小应用。
一般情况下,三个模块是相互包含的,但是只有一个能在应用程序中使用。ALGRF适于RF5的需要和别的RF级别,而不适合紧凑和底端的如RF1级别的系统。
ALGRF与ALG相比,有以下的优势:
1 更小的代码脚本(代码量):
作为一个通用的模块,ALG支持malloc/free 运行库和DSP/BIOS MEM_alloc/MEM_free动态内存分配方式,ALGRF只支持DSP/BIOS分配,这为设计者省了代码空间,另外ALGRF保证没有无用代码的存在。只有被调用的函数才被连接到执行程序中。
2 暂存区支持:
下面为API在ALGRF中介绍的实例:
ALGRF_Handle ALGRF_createScratchSupport(IALG_Fxns *fxns, IALG_Handle parent,
IALG_Params *params, Void *scratchBuf, Uns scratchSize)
除了当IALG_SCRATCH内存区域(内部数据缓冲区)被请求,该函数能根据算法请求分配内存。作为替代,scratchBuf和 scratchSize这两个参数表明这一缓冲区已经在应用中存在,而且可以被当前的算法重新使用。这就可以受约束的共享资源有限的内存区。
3 从DSP/BIOS堆标签中做提取:
ALGRF使用DSP/BIOS 的MEM模块动态分配内存。一个堆标记或内存段名可以传给
MEM_alloc()来表明分配到哪一个堆.如下:
/* Configure the ALGRF module to use:
* 1st argument - memory for internal heap
* 2nd argument - memory for external heap
*/
ALGRF_setup( INTERNALHEAP, EXTERNALHEAP );
这让我们可以指定算法的数据分配位置。例如,如果使用EXTERNALHEAP作为两个参数,那么算法的数据就被定位在外部存储器。
|
|