原文地址:http://hi.baidu.com/shmily_soc/item/f87532fa07a4d5ea1a111f52
标签: CE是一系列APIs,供用户用于实例化和运行xDAIS算法。一个VISA接口被当做与xDM-兼容的xDAIS算法交互的接口。介绍各个整合器及之间联系,并讲到trace调试方法。这里的记录适合于以下情况:嵌入式操作系统应用开发,且将达芬奇的DSP侧当成一个黑盒处理,通过API表示。个人觉得这种应用开发将会越来越重要,因为很多codec已经免费开放了,研发人员需要做的就是根据上层应用的需要将codec lib做CE封装,包括多个codecs的封装调用。
1.1 CE含义:
CE的APIs对于以下条件都是相同的: 算法运行在本地端(GPP)或运行在远端(DSP); 系统可以是GPP+DSP, DSP-Only, GPP-Only; 所有被支持的GPPs和DSPs;所有受支持的操作系统,如Linux,PrOS,VxWorks,DSP/BIOS,WinCE.
xDM(xDAIS-DM)是xDAIS标准针对数字媒体的扩展部分,总是与xDAIS接口兼容。xDM接口将codec算法分为4类:video,image,speech,audio(VISA),针对每个codec类都有一系列APIs,由此,不需要改动应用程序源码,即可以将MP3 codec代替WMA codec,仅需要将配置改动。
CE还提供APIs,用于访问内存,查询CPU使用率统计以及支持的trace信息等。
CE安装后主目录下的packages里面是整个CE包,其下ti.sdo.ce里面包含所有VISA APIs及用于远程调用的stubs和skeletons,osal目录下包含OS抽象层。主目录下的xdoc里面的是针对packages里面内容的各部分说明文档。
1.2 算法创建
算法创建负责创建xDAIS算法并提供必要的封装,使得CE可以调用和配置算法。如果codec是xDM兼容的,CE的VISA APIs不需要额外支持即可以用于远程执行,但如果non-xDM兼容,要想实现远程调用的话,算法创建就需要提供codec engine skeletons和stubs(这就是我们的工作)。算法创建使用xDAIS和XDC工具,通过这些工具,生成一个codec library。同时创建一个XDC module,以实现ti.sdo.ce.ICodec的接口。算法创建器传递一个released的codec package到server整合器,这个package包含了一个模块,该模块实现上面说到的ti.sdo.ce.ICodec接口,并包含算法实现的libraries。
1.3 server 整合器
要支持远程codecs,就必须要用到server整合器,若仅GPP-Only或DSP-Only,就用不到这一块了。server整合和配置各种组件(如DSP/BIOS,FC,DSP/BIOS Link diriver,codecs,Codec Engine)来容纳codecs,并产生一个可执行文件即Codec Server。对于server的配置已经看过很多次了,tcf文件配置DSP/BIOS(参考DSP/BIOS Tconf User's Guide_SPRU007和C6000 DSP/BIOS API Reference_SPRU403),如果应用需要,可以在tcf文件中添加自己的non-Codec Engine条目,其它的就是配置剩余组件,其中一个简单的main()程序用以实现最小程度的初始化工作。然后server整合器将生成的可执行文件传递给下面要讲的Engine整合器.
1.4 Engine 整合器
Engine整合器定义各种Engine配置,包括Engines名字,codecs和他们在每个Engine中的名字,每个codec是本地还是在远端,决定每个codec应该整合到哪一组(利于支持资源共享),对远程调用的codec,确定Codec Server镜像的名字,等等,以上工作通过XDC配置脚本*.cfg完成。Engine整合器接收Codec Server名字,及它在Server整合器中包含的一系列codecs。对于DSP-Only或GPP-only平台,Codec Server没有启用,所有codecs配置成本地调用("local: true"). 完了Engine整合器将Engine配置文件传递给上层应用程序。
1.5 上层应用程序
应用程序使用CE APIs(Engine_,VISA,等等)创建/删除已配置好的Engine instances,创建/删除codecs并与之交互,为codecs获取合适的buffers。负责处理I/O操作,包括文件读取和驱动交互(buffer 管理),并负责编译应用程序代码,链接合适的内容,生成一个可执行的镜像文件。生成可执行文件的过程高度依赖于应用程序的操作系统,比如,如果应用程序跑在DSP上,使用DSP/BIOS,则需要一个.tcf文件来配置DSP/BIOS内核,如果跑在Linux上,则应用程序不需要配置操作系统。
2. 使用CE APIs
这里只提供了APIs的概览,及如何使用他们,对于详细的调用语法,参考安装文献CE_INSTALL_DIR/docs/html
2.1 Core Engine APIs
Core Engine有一个核心模块即Engine,应用程序使用这个模块打开和关闭Engine instances。多线程应用的话,必须要么顺序访问(不能同时)一个共享Engine instance,要么为每个线程创建一个独立的Engine instance。注意:Engine 句柄不受线程保护,使用Engine instance的每个线程应该单独执行Engine_open()并使用自己所有的Engine句柄,这样在多线程环境下,可保护每个Engine instance不会被其他线程访问。
...
2.2 算法控制MOD_control()并用算法处理数据MOD_process()
MOD_create()创建算法实例后,可通过MOD_control()函数控制和查询算法, CE定义了很多个宏,根据返回值可查询宏的意义。相关参数传递见相关文档。
2.3 配置优先级决定最重要的算法
这一点之前没太注意过,应该很有用,比如,一个应用程序可能希望按不同优先级远程运行多个codec instances,例如希望运行两个同样的audio code,一个优先级4,一个优先级5,此时server配置中可以这样写:
Server.algs = [{name: "audenc_copy" , mod: AUDENC_COPY, threadAttrs:{stackMemId:0,priority:Server.MINPRI+3}},
{name: "audenc_copy_2", mod: AUDENC_COPY, threadAttrs:{stackMemId:0,priority:Server.MINPRI+4}}
...
]
这样配置后编译server会报错,因我这两个同样的codec产生UUIDs是由mod(AUDENC_COPY)决定,而非由name决定,因此他们的UUID是相同的。由ARM应用程序传递到DSP server实例化codec的是UUID,而非name,这样server就无法将这两个codec区别开来。解决的办法是在算法创建时以优先级来区别,通过name参数传递到MOD_create() API,格式如下:
Engine_Handle ce;
AUDENC_Handle enc;
AUDENC_Handle enc_high; // 从这里也可以看出,同一个句柄类型可以定义多个句柄名,比如同样的视频解码VIDDEC_Handle,可以定义一个MP4的,还可 // 以定义一个h264的,这就解决了之前我们在多解码器封装时用enc的句柄来指向mpeg4解码器的矛盾
ce = Engine_open("audio_copy", NULL, NULL);
enc = AUDENC_create(ce, "audenc_copy", NULL);
enc_high = AUDENC_create(ce, "audenc_copy : 5", NULL); // 附加: 5来强调优先级,以表明是不同的codec.(以类似的方式可以指定codec buffers布置)
3. Server APIs
运行在GPP端的应用程序可以通过下述server APIs来DSP Server相关信息并控制DSP Server。而且,可以使得应用程序获得在DSP Server上配置的内存堆栈信息,当前堆栈的使用情况,并可以对算法堆栈基址和大小做重新配置,相关APIs有以下这些:
Engine_getServer();
Server_getNumMemSegs();
Server_getMemStat();
Server_redefineHeap);
Server_restoreHeap();
3.1 获取Server句柄
要访问引擎的DSP Server,应用程序首先得通过API获得一个Server句柄,如:
static string engineName = "auddec";
Engine_handle ce;
Server_Handle server;
Engine_Error err;
ce = Engine_open(engineName, NULL, &err); // 先打开引擎
server = Engine_getServer(ce); // 如果返回为NULL,则ce没有Server----是不是可以用来判断当前是否为remote调用?
3.2 下面以例子说明以上几个函数的用法:
Server_Handle server = NULL;
Server_Status status;
Engine_Handle ce = NULL;
XDAS_Int8 *buf;
Uint32 base;
String engineName = "audio_copy";
String decoderName = "auddec_copy";
String encoderName = "audenc_copy";
// 打开引擎并获取server句柄,engine_open()将会load并start DSP
ce = Engine_open(engineName, NULL, NULL);
server = Engine_getServer(ce);
// 在连续物理内存上分配一块连续内存, 可以0字节对齐,通常8字节对齐
buf = (XDAS)Int8 *)Memory_contigAlloc(BUFSIZE, ALIGNMENT);
// 转换虚地址到物理地址:因为上面GPP程序分配连续内存的函数返回的地址是一个虚地址,故先得调用本函数将之转换为DSP地址才能传入下面函数
base = Memory_getBufferPhysicalAddress(buf, BUFSIZE, NULL);
// 重新分配算法heap,这块heap必须是物理连续的,而且当前在该heap上没有分配内存
status = Server_redefineHeap(server, "DDRALGHEAP", base, BUFSIZE);
// ...创建、运行codecs
// ...删除codecs
// 重新分配算法heap到其原始状态
status = server_restoreHeap(server, "DDRALGHEAP");
// 释放buffer
Memory_contigFree(buf, BUFSIZE);
// 注: DSP Server可以配置一块内存专门用于算法heap(如tcf中配置的DDRALGHEAP通常有128M之多)。在某些情况下,DSP Server可能只配置了一个较小的算法heap(DDRALGHEAP),而GPP应用程序在运行时希望能有一块更大的连续内存供DSP Server用于算法heap(DDRALGHEAP). 这时,如果有一块之前连续分配的内存DSP没有使用到,则可以收回这块内存,交给DSP Server供做算法Heap用,用完后恢复过来。通常应该是用于多codecs间切换。
3.3 连续物理地址创建的问题
在ARM端,因为有Linux或类似OS通过内存管理单元MMU维护了一张表,实现虚地址与物理地址之间的匹配对应关系,所以GPP可以处理非连续的buffers,但在DSP端是没有维护这样一张表的,故传给DSP端的地址都必须是实际物理地址,并作cache对齐。
有一种情况特别需要注意:开始创建A,B,C三个codec instances,给他们各自分配了所需连续的物理空间,codecs能正常运行,然后当删除codec A和codec C,然后再次创建codec C,此时A可能就无法创建了,因为此时给C分配的连续地址占据了原先给A分配的空间剩下的可能就没有足够大的连续空间供A使用了,导致A无法创建。
4. DSP的实时性问题
GPP应用程序负责处理所有多线程和实时性问题,例如,频率高但持续时间短的处理(如audio)被Linux OS调度在一个较高的优先级上,相对时间长的处理(如video)优先级较低。每秒钟内,app可以使用CE run或control一个算法达7000次之多,对于每秒30~50帧的视频处理,这看似非常足够了,但事实上因为各种限制,达不到这个切换频率的。
4.1 多处理-单处理性能比较
VISA APIs被调用后会等待函数返回,由此,多线程就派到用场了,当等待DSP处理算法时,这个等待时间可以交给其他线程来处理其他的事情,对于GPP多线程与CE的联合管理见相关文档。
5. CE debugging (trace) 相关
CE模块在app端和server端都提供了非常丰富的trace信息,若任何object---codec或一个CE,或本地或远端,在app端创建失败,按照下面介绍实现各种trace方法做基本调试。
5.1 从ARM端实现ARM+DSP系统的调试
在run app前设置下CE_DEBUG的环境变量值,其中1为级别最低的调试信息输出,包括ARM和DSP端的warnings和errors,2中等级别,3为最高级别。
:~# CE_DEBUG=1/2/3 .app.out [any app args...] >> log.txt
5.2 DSP-only系统的CE调试
对DSP-only系统,假定从CCS调试应用程序,显示CE trace信息,可以在C code中打开tracing。做法如下:首先保证添加了两个头文件<stdio.h><ti/sdo/ce/trace/gt.h>,然后紧跟在CERuntime_init()函数后添加下面语句:
GT_setprintf( (GT_PrintFxn)printf );
GT_set( "*+67"); // 表示打开多少trace,这里输出warnings and errors。如果希望打开所有trace,则使用下面一句:
---->GT_set( "*+01234567, GT_prefix=1235, GT_time=0" ); // 这句等效于setenv CE_TRACE "*=01234567,GT_prefix=1235,GT_time=", 其中time不看
6. 软件trace调试
TraceUtil模块可以帮助在CE应用中实现软件trcaing,用于调试和收集实时数据;另外,类似SoC Analyzer等工具可以帮助显示trace数据,而TraceUtil可以用于简化以上工具的使用。
6.1 配置TraceUtil模块
要使能TraceUtil module,在GPP端配置脚本文件cfg中任何位置处添加下行:
var TraceUtil = xdc.useModule('ti.sdo.ce.utils.trace.TraceUtil');
缺省设置使得GPP应用程序打印所有GPP侧的errors和warnings到标准输出,收集DSP侧errors和warning到标准输出,不会使能或扑捉任何DSP/BIOS logging。但可以通过设置trace属性值(NO_TRACING,DEFAULT_TRACING,SOCRATES_TRACING,FULL_TRACING)来修改输出内容,比如:
TraceUtil.attrs = TraceUtil.SOCRATES_TRACING; // 使能SOC分析器和DSP/BIOS logging,GPP侧输出到/tmp/cearmlog.txt,DSP侧输出到/tmp/cedsp0log.txt,DSP/BIOS logging输出到/tmp/bilslog.dat。此属性值关闭了所有tracing。如打开tracing,则:
TraceUtil.attrs = TraceUtil.FULL_TRACING; <ti.sdo.ce.utils.trace.TraceUtil>
6.2 app C code中支持TraceUtil
先得添加头文件<ti/sdo.ce/utils/trace/TraceUtil.h>,并在CERuntime_init()后添加以下行代码:
#include <ti/sdo.ce/utils/trace/TraceUtil.h>
TraceUtil_start(engineName);
....
TraceUtil_stop(); // app结束时调用
6.3 DSP Server的配置
如果前面在GPP侧使用了DSP/BIOS logging,则必须在DSP Server image中也要使能DSP/BIOS logging,配置脚本中添加:
var LogServer = xdc.useModule('ti.sdo.ce.bioslog.LogServer');
并通过设置环境变量CE_TRACE的值来改变输出模式,详见文档58。
7. 下面提供一个实际的reusable example脚本配置示例供参考。