本文分析了TMS320C3x采用C语言编程的优缺点及其适用场合,介绍了它的分支、寄存器和存储器三种流水线冲突类型及其避免的方法以及代码和数据的存储器存放原则。提供了一些经并行阵列运行证明其极为有效的高效编程技巧。
对许多实时信号处理系统,为减小系统的硬件规模,要求核心代码具有很高的效率,如实时图像处理、雷达信号阵列处理等需高速运算的场合,往往在实现功能代码后,还得花费几倍甚至几十倍的时间进行优化。TI(Texas Instruments)的TMS320C3x浮点系列DSP,由于其具有32位浮点精度,性价比高,国内应用极为广泛,它包括C30、C31、C32和C33。C3×具有适应高速信号处理的结构和功能,根据其特点对代码进行优化,同时配合改进算法的某些处理步骤,从而使系统的效率大大提高。
1 C语言的使用
C3x的设计考虑了与高级语言的兼容问题,其配备有优化的C语言编译器,能将符合ANSI标准的C语言编译为C3x汇编代码。尽管C语言编译器具有优化功能,但其效率却很低,因为它没能充分利用DSP特有的硬件资源。
(1)没能利用C3×众多的通用寄存器。
TMS320C3x共有28个寄存器,其中进行浮点数操作的是8个扩展精度为40bit的寄存器(RO~R7),8个用以地址寻址和运算的辅助寄存器( ARO~AR7)。而C语言编译器只使用了其中的RO~R3和ARO~AR3,造成了寄存器资源的浪费。在需要较多变量的运算场合时,使用过少的寄存器会造成寄存器瓶颈效应,降低了效率。
(2)没能利用C3x的特殊寄存器。C3x除了两组通用寄存器外,还有许多特殊寄存器,如用于变址寻址的变址寄存器IRO、IR1;用于循环寻址的BK(块大小寄存器)、RS(重复起始地址寄存器)、RE(重复终止地址寄存器)等,这些寄存器在必要时可以作为通用寄存器使用,这对于提高如FIR滤波、IIR滤波等的运算速度具有很大作用,但经C语言编译器生成的代码却无法利用这些特殊寄存器。
(3)没能利用C3×的特殊指令。C3x具有功能强大的指令集,其中有许多独特的指令,如位反转寻址指令(特别适用于FFT变换寻址)、单周期延迟分支指令以及单周期完成乘加的并行指令,而C语言编译器生成的代码却无法使用这些指令。更为严重的是,C3x可以实现单周期的浮点或定点乘,但在C语言编译器产生的代码中,却要调用函数来实现,由于调用函数涉及分支跳转会引起流水线刷新,从而使代码效率大大降低。
基于以上原因,高效的DSP系统的核心代码,一般不使用C语言编程。经过实际测试表明,用C语言编程和手写汇编语言编程之间的效率相差几倍到几十倍不等,而对于数学运算密集的代码,如FFT运算、IIR滤波等,其效率可相差几十倍之多,故一般不宜采用C语言编写C3×的数值处理程序,但可用其编写一些系统控制初始化软件或用于系统功能的先期仿真等。因为此类软件执行的频率不高,实时性要求也不高,运算也大多以简单逻辑判断为主,但为了便于调试和维护,要求程序的可读性强,所以这部分采用C语言编程比较合适,可降低软件开发的难度。
2. 汇编语言的使用
使用汇编语言能充分利用C3x的硬件资源和其强大的指令系统编制出数字信号处理的高效率代码。C3x由于采用寄存器阵列结构,而不是传统的寄存器加累加器形式,因而具有较多的编程技巧可以使用。
2.1 C3x的流水线结构
C3×一条指令的执行包括取指、译码、读、执行四个时钟周期,由于上述4个步骤由不同的单元执行,因而能实现并行操作,从而实现单指令周期。但流水线结构使用不当会造成流水线冲突,从而削弱CPU的处理能力。
2.2流水线冲突
在程序执行过程中,最糟糕的莫过于发生流水线冲突。冲突时,一条指令执行将占用四个CLK(时钟周期)。C3x的流水线冲突可归为3类:分支冲突、寄存器冲突和存储器冲突。分支冲突发生于标准分支BR、Bcond、CALL、IDLE、RPTB、RPTS、RETI、RETS等指令以及中断和复位。寄存器冲突包含了用于寻址目的寄存器的读和写,这些冲突发生于相应的寄存器没有准备好或已被使用,从而使CPU处于等待状态。在C3x的冲突中,最严重的就是存储器流水线冲突,当系统的存储器安排不当时,将会使系统效率严重下降。当两个CPU数据访问内部RAM块,而又有必要从同一块取程序;或当一个外部端口正启动CPU数据访问,而同时必须从同一端口取程序;或需要在外部总线上的多周期CPU数据访问或DMA数据访问时,都会发生存储器冲突。
2.3冲突的解决
对于分支冲突,C3x提供了延时跳转和调用指令,因而使用延时指令能避免流水线的冲突,实现单周期分支,但要注意的是延时指令后的三条指令是在分支前执行的。对于寄存器冲突,通过合理安排寄存器组的使用,如在使用变址寻址或其他操作中需要用到如AR寄存器时,尽量少对该类寄存器进行算术操作,而将其用作变址寻址的地址寄存器,同时应使各寄存器的利用率尽量均衡。对于存储器冲突的解决要根据具体的应用场合而定。C3x物理空间很大,首先应合理地安排各存储器的物理空间。
C3x共有3套并行总线,即程序总线、数据总线和DMA总线,允许并行的程序取、数据访问和DMA访问。因此,合理地安排存储器的配置,能最高限度地发挥C3×的数据吞吐能力。
片内高速RAM支持在一个时钟周期内的两次读或写操作,这是片外高速RAM所不能及的,因而将核心代码置于片内,可大幅度提高运行速度。同时又可给DMA让出外部基本总线,避免DMA冲突。对于软件规模大、核心代码较多的系统,当C3×片内存储器不够时,可采用动态加载的方法,片内存储器采用时分复用。对于CPU访问频繁的数据表,如FFT的正弦、余弦表、窗系数等和系统堆栈等也应放在片内。总之,能放在片内的决不要放在片外。对于采用较多乘加并行指令的代码,数据和代码应放在片内不同的块中,如采用C30或C31.可以将代码放在809800H块中,而将数据放在809COOH块中。例如对于128点复数FFT,数据和代码置于不同块中比置于同一块中可节省大约1 000个CLK的时间。
3.编程的其他技巧
除了避免CPU产生上述冲突外,采取其他一些方法也可提高C3×的代码效率:
(1)借用某些特殊寄存器作数学运算或数据暂存。当某些特殊场合通用寄存器(RO~R7,ARO~AR7)不够时,可借用其他空闲的寄存器,如BK、RE、RS、RC或IR等,特别是当DSP同时还在进行DMA操作时,使用寄存器操作可以减少CPU对外部总线的访问,特别是对于C31或C32而言更为重要,因为它们没有扩展总线,但在使用时应注意避免发生寄存器冲突。
(2)利用存储器暂存。C3x具有一系列并行指令,如乘加、取存、存取、二次取、二次存等,但C3x的并行指令比普通指令多许多限制条件,如乘法指令,其目的寄存器只能为RO和R1;而加法指令,其目的寄存器只能为R2和R3,如:
MPYF *ARO,* ARl,RO
‖ ADDF RO,R7,R2
是正确的,但:
MPYF *ARO,* AR2,R4
‖ ADDF R4,R7,R2
是错误的,尽管不采用并行符号“‖”时两者结果相同。因而在高密度运算中,如四阶椭圆滤波,其目的寄存器会产生瓶颈效应,造成系统效率下降。当多条并行指令一起执行时,往往由于前一条指令目的寄存器中的结果后续指令还要使用,因而必须保留,但后续指令就会因无空余的目的寄存器而不能采用并行执行。在这种情况下,可以将寄存器内容暂存于片内存储器,然后再利用寄生的取数指令将暂存数从存储器取回寄存器。如:
上述代码表面上看起来多了许多环节,但由于寄存器RO的存取过程是寄生于别的指令,无任何多余开销,却使后面的乘加并行指令能够使用RO寄存器,故节省了指令周期。需要说明的是,之所以不用堆栈来保存,是因为C3x不存在任何能与堆栈操作并行的指令,因而不采用压栈的方法保留寄存器。
(3)少用函数调用。采用结构化程序设计给软件维护和调试带来了方便,其代价是增加了指令周期,因为每一个函数调用和返回都将使C3x产生一次流水线刷新,因而对于存储空间足够的情况下,应多使用宏而少使用函数进行编程。
(4)改变{七算流程。为了提高效率,可采用几种处理穿插在一起进行,以充分利用C3x的并行指令,有时候这是一种最有效的方法。如在信号处理中需加窗和计算信号功率,这两种运算可穿插在一起,比单独加窗后再计算信号功率所需指令少了许多,因为利用C3×的众多寄存器可以省去多次数据存取。
4结论
利用上述技巧来优化软件虽然耗费了巨大的人力,但大幅度提高了系统的性价比,同时由于减小了系统的硬件规模,使系统的可靠性大为增加,由于软件的复制成本低廉,批量生产后效益将十分显着。
本文关键字:暂无联系方式DSP/FPGA技术,单片机-工控设备 - DSP/FPGA技术