硬件系统测试通过后,接下来的目标就是图像处理系统软件的实现。图像处理系统软件包括DSP外部设备驱动程序的开发与调试、嵌入式操作系统在DSP上的移植、应用图像处理算法在DSP上的移植和优化。系统软件的性能关系到整个系统的性能,在系统软件的设计和实现中,要满足系统的实时性和可靠性要求。
本节主要从系统软件的两个重大性能要求出发,深入探讨算法在DSP上移植和优化方法、系统软件核心层的设计和实现问题。
7.3.1 操作系统
该图像处理系统采用DSP/BIOS作为其操作系统。因为该系统中有许多任务和中断,只依靠主循环加中断的简单前后台式系统形式无法满足要求。在主循环加中断模式的前后台系统中,各模块任务以顺序结构组织在一起,各模块之间的调用和切换都是由各模块自身提供的代码来完成的,这样使得应用程序各模块之间处于一种紧耦合状态,如果添加新的功能模块,不但因为要修改相关模块的调用代码而难于升级维护,并且新增模块也会明显影响到原有系统的时间响应特性。
DSP/BIOS是TI为其DSP产品开发的嵌入式微内核操作系统,其出现提供了另外一种组织应用程序各功能模块的机制,它通过可配置、可裁减的内核服务使各功能模块在系统调度器的安排下按照优先级的高低分时复用CPU资源,这种机制带来了应用程序可维护性的提高,而且也为能够提供更高级、方便的调试手段打下了基础。DSP/BIOS在整个目标模板上主要起到三方面的作用:其一是实现主机调试环境对目标模板上运行的应用程序的实时监察和控制,其二是实现各线程之间的调度和线程间的通信,其三是使用DSP/BIOS可以对片上外围硬件进行可视化配置。使用DSP/BIOS对整个系统的配置如表7-3所示:
表7-3 DSP/BIOS对系统的配置
系统在上电重启后,需要对电路板上的DSP和其相关外部设备初始化,初始化由TI DSP DM642完成,初始化函数为_dm642_init,该函数的流程图如图7-20所示。此函数主要完成初始化DSP的任务,使DSP能构正常的启动,是系统软件运行的基础,一切的系统软件和应用算法都在DSP正常运行后启动。
图7-20 _dm642_init函数流程图
7.3.2 系统核心层的设计和实现
DSP/BIOS是一种多任务的实时操作系统(RTOS),主要用于TI C5000、C6000系列DSP。DSP/BIOS主要是为需要实时调度和同步以及主机/目标系统通信和实时检测的应用而设计。DSP/BIOS组件包括抢先式多任务内核、硬件抽象层、实时分析工具和配置工具。
7.3.2.1 任务的分配与调度
DSP/BIOS的内核是一个支持抢占式的基于优先级的多任务内核,任务调度过程是首先从就绪表中找到那个进入就绪态且优先级最高的任务,若是当前正在运行的任务,则不需要进行调度,否则进入任务切换。任务切换由以下两个部分完成,将挂起任务的微处理器寄存器推入该任务的堆栈,然后将较高优先级的任务的寄存器从该较高优先级的任务堆栈中恢复到寄存器中。
DSP/BIOS支持以下四种任务类型,每种任务都有不同的执行和抢占特性。
(1)硬件中断(HWI):用于响应外部异步事件。当一个硬件中断发生时执行HWI函数来执行关键任务。HWI函数在DSP/BIOS应用程序中的优先级最高,应该用于发生频率在200 k Hz左右的事件。
(2)软件中断(SWI):与硬件中断相对,SWI是通过调用SWI函数触发的,它的优先级处于HWI和TSK之间。SWI和HWI一样,也是必须执行到完成,一般用于执行期限(deadlines)在100 ms以上的事件。
(3)任务(TSK):任务的优先级高于后台线程,低于软件中断。任务与软件中断不同的地方在于运行过程中可以被挂起。DSP/BIOS提供了一些任务间同步和通信的机制,包括队列、信号灯和邮箱。
(4)后台线程(IDL):后台线程在DSP/BIOS应用中的优先级最低。在main函数返回后,系统为每个DSP/BIOS模块调用startup例程,然后开始空闲循环的执行。除非有高优先级的线程抢占,空闲循环将一直循环执行。
根据该系统的应用特点,在系统建立如下几个任务:接收图像、算法处理、传输结果和更新FLASH。其中接收图像又细分为收图到内存任务和搬移图像到SDRAM任务。另外还有一些附加的小任务,在操作系统到点后定时运行,统计一些基本信息。
整个系统的程序流程图如图7-21所示。系统复位后,进入main函数中进行应用程序相应需要的初始化工作,先是由DSP/BIOS调用芯片整体初始化函数,然后配置和初始化FPGA、CCD、USB等外围器件。系统完成上述工作后,就由操作系统进行任务调度。DSP/BIOS中的每个任务都是永久循环。一般来说该任务完成一个循环后,会因为等待某种标志或资源而主动让出CPU控制权。此外,优先级高于该任务的任务或者中断也会抢占该任务的CPU资源。
图7-21 系统的程序流程图(胡佳,2007年)
图中TSK1属于硬件中断(HWI),当FPGA的FIFO缓存满一行(1024)图像时,给DSP发出外部硬件中断,DSP在中断服务程序里设置相应的寄存器,启动DMA,使能TSK2;TSK2属于软件中断(SWI),它由TSK1触发,主要将L2 SRAM里的一行图像搬移到较大存储空间的SDRAM里;TSK3属于任务(TSK),其优先级设定为10,该任务是系统的核心,对接收来的图像进行处理,得到正确的结果,该任务是一个无穷循环;TSK4也属于任务(TSK),其优先级设定为11,高于TSK3,当TSK3处理出结果时,TSK4立即将处理结果信息传输给上位机;TSK5属于后台线程(IDL),大部分时间该任务都不会被调用执行,当且仅当系统有更新需要的时候,才需要调用这个任务。
7.3.2.2 并发处理
并发处理主要解决进程同步和进程通信的问题,因为一个具体的算法被分解成了不同的任务,并且在不同的CPU执行时隙上运行,显然这是一组需要相互合作的并发进程,因为进程之间需要交换信息,而各个进程又以各自独立的、不可预知的速度进行,所以,当一个进程需要另一个或几个进程的信息时,只要那个或那些进程没有发出,这个进程就必须等待,直到所需的信息收到为止,否则就会出现错误。显然,多个任务之间的并发处理不仅仅在于完成任务本身,还必须考虑某些资源的共享问题。
根据应用,系统(胡佳,2007年)内核中,通过信号量来满足同步的需要;通过消息机制提供通信的服务,该系统提供了邮箱和消息队列两种消息服务。利用邮箱和消息队列机制,在单处理器上的各任务之间可以通过指针传递而不是值传递来完成图像数据和任务间消息等大量数据的通信。
图7-22所示为系统中各任务的执行图,从图中可以看出多任务并行执行的好处。
图7-22 各任务的执行图
因为算法任务Detect_alg的检测时间大于图像帧产生的周期,所以在前后台式系统中,算法任务Detect_alg会一直执行下去,这样必然导致要丢弃一帧图像;但在多任务系统中,各个任务是按优先级分时执行的,当第二帧图像到来时,即使此时算法任务Detect_alg仍在执行,但由于硬件中断的优先级较高,系统会将第二帧图像收取存储后,继续算法的执行,由于算法对第二帧图像的处理转入了跟踪状态,算法的运行时间大大降低,完全可以在25 ms内运行完毕,能够满足实时性要求,并且不会丢帧。
7.3.2.3 存储空间分配与管理
比较各种类型的存储器,从访问速度来看,片内L1 SRAM的访问速度最快,其次是片内L2 SRAM,然后是片外SDRAM,再次是片外异步存储器(如FLASH等)。从存储器容量来看,片外的SDRAM存储容量最大,其次是片外异步存储器,再次是L2 SRAM,L1 SRAM容量最小。由于异步存储器FLASH访问速度比较慢,在图像处理算法实时性要求高的情况下,不适合存储程序或数据,所以不考虑该空间的使用。
本系统使用片外SDRAM保存整帧图像数据以及不频繁调用的程序,片内SRAM保存需要频繁访问的少量数据和频繁调用的程序。结合我们的图像处理算法会常用到128×128大小的图像宏块的特点,每次先将一组宏块的数据从速度较慢的外部SDRAM中读入速度较快的内部存储器再进行处理,处理完成后把结果送到外部存储器或共享内存中。这些数据传送由EDMA完成,这样在传送数据的时候不会中断CPU执行算法程序。
外部存储器主要用到的是SDRAM,它不但存取速度较快,可以存放数据和程序,而且容量大,能够满足视频处理过程中对存储空间的要求。表7-4是SDRAM空间的分配:
表7-4 SDRAM空间分配
L2SRAM用于存储编码中频繁调用的程序模块(如图像处理算法、USB传输函数等)和频繁访问的数据(如图像数据宏块等)。因为内部的SRAM较小,不能放下所有的数据。L2SRAM空间分配见表7-5。
表7-5 L2SDRAM空间分配
在进行解码过程中还要用EDMA将SDRAM中的数据搬进搬出。本系统在此将SRAM分出一些Ping-Pong缓冲区,一部分用于CPU进行计算,一部分与外部SDRAM进行数据传输,以保证CPU无须耗费时间在等待数据的搬运完毕上。
在ANSIC上可以使用malloc()和free()两个函数动态的分配内存和释放内存。但是由于内存分配算法的原因,多次使用这两个函数会导致内存的碎片,并且执行的时间是不确定的,它们可能会引入错误的时间延时,由此破坏任务的时间约束。在系统中,根据实时图像处理系统的应用,对内存施行静态的分配。
7.3.2.4 高速缓存的一致性
应用程序必须保证高速缓存的一致性,驱动程序不需要担当此责任,因为数据一般都是由EDMA在片上快L2 SRAM和片外慢SDRAM之间搬运以适应CPU。此外,算法中还可以使用Ping-Pong缓冲来并行EDMA的传输和CPU的运算,这样可以隐藏大部分数据搬运的开销。程序员在软件设计上可以将帧缓冲对齐到高速缓存边界线上来避免使用Cache flush和clean函数。假如不能对齐的话,设计者就必须使用flush和clean来保证高速缓冲的数据一致性。因为EDMA可以直接通过EMIF来存取外部存储器,而CPU须要先过缓冲才能存取数据。
7.3.3 算法的移植和优化
7.3.3.1 DSP代码编写注意点
TI的DSP平台与一般的台式机编程有许多的不同之处,编程的时候有以下方面需要注意:对标准C的支持。CCS的编译器仅仅支持标准C。有一些非标准C的函数需要软件开发人员换成DSP支持的函数库。有些函数例如malloc、fileopen等等都不一定在CCS编译环境中得到支持,需要用特定的函数和方法来替代。
DSP对不同的存储单元的访问速度是有区别的,对片内寄存器的访问速度最快,对片内RAM的访问速度比片外RAM的访问速度快。因此,合理地配置和使用存储空间对系统整体效率影响很大。应该尽可能地把访问比较频繁的常数表和代码段装入片内RAM。同时,还要考虑存储bank的冲突。由于TMS320 C64x使用交叉存储方案,将存储器分成4个bank,每个bank都是单口存储区,因此每个周期只允许一次访问,在一个周期内对一个bank进行两次访问将产生存储器阻塞。存储器阻塞导致所有流水线操作停止一个周期。解决的办法是对代码段进行修改,将要同时读和写的两块数据分开在不同的bank存放。
7.3.3.2 C语言优化
C语言优化可做的工作很多,使用到的大概有以下几项:
(1)使用L2 Cache。DM642的片内存储结构最大特点就是具有2级缓存结构,其结构如图7-23所示。DM642的L2 SRAM可存放数据和程序,可以配置成片内SRAM和4路组Cache。通过配置一定大小的片内SRAM可以将关键代码和常用数据放到片内,减少存取时间。L2 Cache只能与片外存储器相映射,L2 SRAM只能与L1 Cache相映射。
图7-23 DM642的二级缓存结构
(2)合理安排分支。对于DSP的流水线而言,程序分支的跳转带来的性能损耗是很大的,因此编写程序时要尽量避免分支时产生跳转。分支的情况包括if-else、条件运算符(?:)、while循环和for循环等。对于条件语句,可以用计算方法代替判断,如图7-23所示。
运算虽然增加了,但是避免了跳转延时,而且编译器会将临近的语句插入到计算之中并行处理,实际效果比使用if-else语句快。而对于循环,特别是多重循环则需要合理安排循环的层次和任务,对于耗时多的循环体最好改写为汇编。
(3)使用内联(inline)函数。内联函数虽然会增加代码的大小,但是可以省去函数调用时跳转和参数传递所产生的延时。使用内联函数既保持了程序的清晰结构,又能保证速度,是一个比较好的方法。
(4)使用CCS提供的库函数。适用的库函数的效率往往是比较高的,使用库函数既加快了开发速度又可保证软件效率。
(5)为了提高算法的实现效率,减少运算的实际开销,把需运行时计算的参数做成查找表或常数数值,从而将运行时的计算转化为编译时的计算。
(6)浮点数定点化。为了方便,C语言中一般既有整型数,又有浮点数。由于使用的定点芯片,所以有必要把所有的浮点运算改为定点运算。
(7)使用数据打包技术,对短字长的数据使用宽长度的存储器访问,例如使用字访问2个16位数据,将其分别放在32位寄存器的高16位和低16位字段,这样可以使程序读取数据的速率提高一倍,从而大大提高执行效率。
(8)使用移位指令替代乘除操作。移位指令只有一个时钟周期,比之乘除运算则可以节约许多时钟周期。
7.3.3.3 软件流水与汇编
在优化的后面阶段,某些关键函数的运算时间依旧很长,使得系统整体性能难以达到要求,为充分发挥流水线的能力,可将关键函数改写为汇编语言,并合理安排指令并行运行。软件流水是一种用于安排循环内的指令运行方式,使循环的多次迭代能够并行执行的一种技术。图7-24是一个用来解释循环流水技术的示意图。
图7-24 用软件流水来执行循环程序
设n为迭代次数,m为每次迭代的指令条数,k为可并行执行的最大指令条数,为简单起见,假定每条指令均为单周期指令,且m可以被k整除,设C㊣为执行软件流水的循环所需时钟周期数,则有:
设C′㊣为执行非软件流水的循环所需时钟周期数,则有:
显然,当m>>k时,有C′/C≈k,也就是说实现软件流水的循环执行时间是未实现软件流水的循环的1/k㊣。以上讨论的是理想情况,实际运用中,由于部分指令是多周期的,以及存在存储器冲突等因素很难达到这种理想情况。通过实验分析,对于C6000而言,经充分优化实现软件流水的程序执行时间约为未优化程序执行时间的1/30,未优化的程序的执行速度仅略快于Pentium MMX 200,根本不能体现DSP的高速特性,这更加说明了实现软件流水的重要性。进行汇编软件流水时,首先将函数改写为线性汇编(即每周期只执行一条指令,没有并行指令),然后将各指令使用到的CPU单元列表,经过适当调整指令顺序和寄存器使用,可以将相邻的使用不同CPU单元的指令并行执行。
7.3.3.4 实际优化过程
(1)首先在C语言程序的基础上,将算法移植到DSP上运行,在未作任何优化的条件下,测得DSP上运算一帧图像所消耗的时间为210 ms。
(2)对算法进行算法级优化,对于点目标检测算法,由于在图像预处理部分,有个图像缩小1/4的运算是由DSP来完成的,占用了较多CPU时间,这里我们利用EDMA对其二维采样,完成同样的操作,不占用CPU的时间,DSP可以同时进行其他的运算,大大减少了运算时间。在优化前,点目标检测的时间为245 ms,优化后,点目标检测的时间减为178.7 ms。对于畸变校正算法,将浮点运算改为查找表的方式进行,例如以前对畸变进行校正是利用畸变参数和多项式进行计算得到结果,消耗时间较长,所以将畸变点对在开机时都计算好存在SDRAM里,在需要校正时,查找SDRAM里的表即可。优化前,校正算法的时间为5 ms,优化后的时间仅为0.8 ms,几乎可以忽略不计。
(3)在算法所在的文件内使用-o3优化,-o3是文件级优化,使用此选项使编译器自行对算法进行优化,优化效果较好。经测试,使用-o3优化后的算法总运行时间为112.2 ms。
(4)使用CACHE缓存技术,将L2 SRAM的一半128 kB作为L2 Cache使用,将SDRAM里存储原始图像的部分映射到Cache里,提高了L2 Cache的命中率,大大提高了访问外部存储器数据的能力,使得算法的运算速度得到提高,消耗时间减小。经测试,使用Cache缓存技术后,处理一帧图像的算法运行时间为85.6 ms。
(5)TI公司为其DSP提供大量的用于图像和信号处理的函数库,这些函数专门针对DSP的体系结构特点进行了相应的优化,使用这些函数库不仅可以达到减少算法的运行时间,而且可以大大节省开发时间。针对我们算法的特点,将均值滤波函数AverageFilter()使用图像处理库里的函数代替。经测试,优化后的一帧图像处理时间为62.3 ms。
(6)经测试发现,影响算法运行时间的两个函数为小图粗搜索函数mImgSearch()和原始图精确定位函数OriImgSearch()。这两个函数因为要频繁的访问图像数据,进行大量的计算,又有很多层的循环,因此函数消耗的时间很长。使用软件流水技术安排循环内的指令运行方式,使循环的多次迭代能够并行执行。在算法中,mImgSearch()和OriImgSearch()函数内有四重循环,由于编译器仅对内部循环执行软件流水,因此,为了提高性能,应尽可能创造一个比较大的内循环。本节中,使用循环展开,使得这两个函数只有两层循环。
将除法运算改为乘法和移位运算,即m>>n㊣的形式。其中的重点在于,既要保证改过的运算的准确度,又要不让乘法运算溢出。以除以3的运算为例,在算法中,像素的灰度的最大值是255(FFH),所以只要将乘法运算的结果向右移14位就可以得到结果,所以乘法系数m㊣的值为214/3=5461。优化后,算法的运行时间减少为48.5 ms。
(7)在算法性能仍旧不能满足要求的状态下,决定使用汇编语言改写上述两个运算时间较长的函数,优化过程中,使用数据打包指令,每个指令读取64位的数据。相关图和模迭代编排表是非常有效的工具。相关图可以帮助我们消除相关性,也与模迭代编排表一同帮助我们安排指令顺序,以达到更高的并行效率。
经过如此优化后,算法处理一帧图像的时间为24.6 ms,能够满足实际应用的需求。综上所述,将上述采取的优化措施和优化结果制成表格(如表7-6所示):
表7-6 优化结果统计表
续表7-6
从表7-6可以看出,使用了优化手段后,算法的时间性能得到了很大提高,特别要利用DSP硬件结构上的特点,利用DMA和硬件乘加器优化存储器访问和卷积运算,效果很好。最后,算法处理一帧图像的时间满足了实际应用的需求。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。