引言
根据处理器芯片的指令及其实现形式,我们可以把处理器芯片分为复杂指令系统(CISC)和精简指令系统(RISC),前者追求单条指令的强大功能以简化编程;后者强调指令的简化以提高硬件效率。由于RISC具有指令长度一致、单周期执行时间、易于并行和流水线处理等优点,绝大多数的DSP处理芯片都采用了RISC。另外,根据计算机的存储器结构及其总线连接形式,计算机系统可以被分为冯诺依曼结构和哈佛结构,前者共用数据存储空间和程序存储空间,共用存储器总线;后者具有分离的数据和程序空间及分离的访问总线。由于哈佛结构在指令执行时,取指和取数可以并行,因此具有更高的执行效率,所以大多数的DSP芯片都采用了哈佛结构。
ADSP2106x就是一款采用超级哈佛结构和RISC的DSP处理芯片,其强大的浮点、定点运算功能和大容量的片内存储器,使其可以胜任苛刻的实时信号处理任务;而它丰富的外部接口和10个通道的DMA可以使所处理数据的畅通无阻[1];再加上片内的仲裁逻辑,6个ADSP2106x和一个主机可以很容易连在一起构成一个并行处理系统。利用ADSP2106x可以开发出功能很强的信号处理系统[2]。
虽然ADSP2106x芯片本身提供了优异的性能,但该性能的发挥离不开软件编程的支持。比如,ADSP2106x的峰值运算速度可达120MFLOPS(主频40MHz),即在一个时钟周期内可以完成一次乘法、一次加法和一次减法,但这三个并行运算指令是需要合理安排才能实现的。另外,由于芯片内部采用了超级哈佛结构,因此可以在一定条件下同时存取两个数据,但这也需要合理安排数据在数据存储器和程序存储器中的放置,才能使并行的存取指令有效。
本文主要介绍ADSP2106x中并行指令的一些应用技巧,重点针对并行运算指令和数据存取指令。通过这些技巧的应用,可以提高编程效率,充分发挥硬件潜力,同时对ADSP2106x的内部结构有更为深入的了解。
ADSP2106x中的运算处理单元
ADSP2106x中的核心处理部分包含了三个运算单元:ALU、乘法器和移位器,它们与寄存器组间的连接关系如图1所示[3]。三个运算单元的功能如下:
(1)ALU单元:定点、浮点加减法和求平均;逻辑运算;求绝对值、最大值、最小值、限幅、比较;定点<-->浮点转换。
(2)乘法器:浮点乘法;定点乘法及乘法累加。
(3)移位器:移位操作;位操作;位场(bit field)提取和存储。
ADSP2106x中三个运算处理单元的数据通道只与寄存器组相连,而不能直接从存储器中存取操作数,这是典型的RISC结构,这种结构往往需要大量的寄存器来存储和交换中间结果,ADSP2106x中具有32个寄存器,其中16个工作于前台,16个工作于后台。乘法器从两个寄存器输入操作数,把结果存入另一个寄存器;移位器从3个寄存器输入数据,把一个结果存入另一个寄存器;ALU从两个寄存器输入操作数,把两个运算结果分别存入另两个寄存器。
寄存器可以在一个时钟周期内从数据存储器和程序存储器中各存取1个数据,这正是ADSP2106x超级哈佛结构的优势。另外,运算单元的运算与寄存器的存取可以并行不悖,但在编程时,这种并行往往伴随一个流水线的过程。
ADSP2106x中的乘法器和ALU还具有并行运算的能力;在一个时钟周期内,乘法器可以完成一次乘法,ALU可以同时完成一次加法和一次减法,这使ADSP2106x在40MHz的主频下达到了120MFLOPS峰值运算速度。
下面针对这种并行运算来考虑软件编程时应注意的问题。
并行运算指令的基本格式
ADSP2106x中并行运算指令的基本格式如下图2,这里我们以浮点运算为例,如果是定点运算,只需把所有的前缀“F”换为“R”即可。
并行指令中各操作的顺序必须符合图中的要求,并以逗号隔开,否则编译时会出错。
DM地址产生寄存器的范围为0~7, PM地址产生寄存器的范围为8~15;即
I0≤ Ia ≤ I7, M0≤ Mb ≤ M7; I8≤ Ic≤ I15, M8≤ Md ≤ M15。并且很重要的是:DM(Ia,Mb)实际指向的内存地址范围必须在数据存储器中,对于ADSP21060,在32位数据长度下,其数据存储器的地址范围为0x30000~0x3FFFF; PM(Ic,Md)实际指向的内存地址范围也必须在数据存储器中。否则虽然编译时能通过,但运行时达不到并行的效果,而且很可能出错,此种出错具有一定的不确定性,在程序调试时不易发现,潜在的危害很大。
在乘法器和ALU并行时,对它们从寄存器中获得操作数有严格的要求。16个寄存器被分成4组,F0~F3为第一组,F4~F7为第二组,F8~F11为第三组,F12~F15为第四组。当乘法器和ALU运算并行时,乘法器的两个操作数必须分别取自于第一组和第二组;ALU的两个操作数必须分别取自于第三组和第四组。
在上面并行处理中,任一寄存器都可以既被读又被写;在指令执行时遵循先读后写的原则,即在前半个时钟周期里数据从某个寄存器中被读出,在后半个时钟周期里运算结果又被回写到该寄存器中。对这一点的深刻理解有助于在编程时采用流水线的步骤。
并行处理时的流水线步骤
在采用ADSP2106x并行指令编程时,由于数据在内存到运算单元之间的流通必须以寄存器为中介,因此编程时需要采用流水线的步骤。我们以下面例子为通用格式来表示该流水线步骤。
假设内存中有N个数据:xn, 0≤n≤N-1;对其经过某种运算处理后,得到N个处理结果:yn,0≤n≤N-1,并把yn写回内存。如果我们不采用并行处理,则处理步骤如下:
For n=0 to N-1
Fx ← 内存(xn);
Fy = 运算(Fx);
Fy → 内存(yn);
End
上述处理共需要3*N个时钟周期(不考虑循环的初始化)。如果我们采用下面的并行处理,且把数据从内存?寄存器?运算单元?寄存器?内存的流通步骤进行下面的流水线化,
Fx ← 内存(x0);
Fy = 运算(Fx), Fx ← 内存(x1); /*进入循环的准备操作 */
For n=2 to N-1
Fy = 运算(Fx), Fx ← 内存(xn), Fy → 内存(yn); /*并行处理循环体 */
End
Fy = 运算(Fx), Fy → 内存(yN-2);
Fy ? 内存(yN-1); /*退出循环后的回写操作 */
则总处理时间缩短为N+2个时钟周期。此时,为了使循环体中并行指令能够实现,需要在进入循环体之前完成数据预取的准备操作,在循环体退出后完成运算结果的回写操作;同时要求xn和yn分处于程序存储器和数据存储器中。
该例子虽然简单,但基本上表达了并行处理时的通用流水线步骤。下面部分,我们将以两个具体的例子来说明并行处理指令的应用。
两个实例
实例1为求两个数组内积。设xn, yn, 0≤n≤N-1为两个数组,它们的内积定义为:。为了在运算中能够同时获得xn和yn,我们需要把xn和yn分别安排在程序存储器和数据存储器中,经过运算后,内积结果在寄存器f8中。
f8=0; /*对结果寄存器清零 */ i0=x; m0=1; /*把数组xn的首地址赋给i0 */ i8=y; m8=1; /*把数组yn的首地址赋给i8 */ f0=dm(i0,m0), f4=pm(i8,m8); f12=f0*f4, f0=dm(i0,m0), f4=pm(i8,m8); /*为进入循环作准备*/ lcntr=N-1, do loop until lce; loop: f12=f0*f4, f8=f8+f12, f0=dm(i0,m0), f4=pm(i8,m8); /*并行处理循环体*/ f12=f0*f4, f8=f8+f12; f8=f8+f12; /*退出循环的残余操作,内积结果在f8中 */
第二个例子是复数乘法运算,一次复数乘法需要4次实数乘法、一次实数加法和一次实数减法,因此至少需要四条指令才能完成一次复乘法,在这四条指令中,还要完成四个操作数的读入和两个结果的回写。这里,我们假设有两个复数组:xn=xrn+j*xin与yn=yrn+j*yin,0(n(N-1;二者相乘后得到zn=zrn+j*zin=(xrn*yrn-xin*yin)+j*(xrn*yin+xin*yrn),0(n(N-1。xr、yr及zi被安排在数据存储器中,xi、yi及zr被安排在程序存储器中。具体程序如下,
i0=xr; i1=yr; i3=zi; i8=xi; i9=yi; i10=zr; m0=1; m8=1; /*对地址产生寄存器赋初值 */ f0=dm(i0,m0), f4=pm(i9,m8); /* f0= xr0, f4= yi0*/ f5=dm(i1,m0), f1=pm(i8,m8); /* f5=yr0, f1=xi0 */ f8=f0*f5; /* f8= xr0* yr0 */ f12=f1*f4; /* f12= xi0* yi0 */ f9=f0*f4, f2=f8-f12, f0=dm(i0,m0), f4=pm(i9,m8); /* f9= xr0* yi0, zr0=f2= xr0* yr0- xi0* yi0 */ /* f0= xr1, f4= yi1*/ f13=f1*f5, f5=dm(i1,m0), f1=pm(i8,m8); /* f13= xi0* yr0 , f5=yr1, f1=xi1 */ lcntr=N-2, do CMTI until lce; f8=f0*f5, f3=f9+f13; /* f8= xr* yr , zi=f3=xr* yi+ xi* yr */ f12=f1*f4, dm(i3,m0)=f3, pm(i10,m8)=f2; /* f12= xi* yi, 存zi与zr */ f9=f0*f4, f2=f8-f12, f0=dm(i0,m0), f4=pm(i9,m8); /* f9= xr* yi, zr=f2= xr* yr- xi* yi */ /* f0= xr, f4= yi */ CMTI: f13=f1*f5, f5=dm(i1,m0), f1=pm(i8,m8); /* f13= xi* yr , f5=yr, f1=xi */ f8=f0*f5, f3=f9+f13; f12=f1*f4, dm(i3,m0)=f3, pm(i10,m8)=f2; f9=f0*f4, f2=f8-f12; f13=f1*f5; f3=f9+f13; dm(i3,m0)=f3, pm(i10,m8)=f2;
结语
本文对ADSP2106x芯片内部运算处理单元的结构进行了分析,并在此基础上总结了并行处理指令的一般格式和具体应用中的流水线步骤,最后给出数组内积和复数组相乘的两个典型例子。
|