1、1,DSP技术及应用,Digital Signal Processor数字信号处理器,第五章 汇编语言编程举例,第一节汇编语言编程的基本方法 第二节 用DSP实现FIR和IIR滤波器第三节 DSP的浮点运算方法,第一节 汇编语言编程的基本方法,1堆栈的使用,1.压入数据时,堆栈从高地址向低地址增长。2.压栈时指针先减,SP-1,再压入数据;3.出栈时,先弹出数据后,再SP+1。4.如要用堆栈,必须先设置,后使用。,要点,例5-1 设计一存储空间为100个单元的堆栈。size .set 100 ;设置堆栈空间的 ;大小为100stack .usect “STK”,size ;设置堆栈段的首地址
2、;和堆栈空间 STM #stack+size,SP ;将栈底地址指针送 ; SP,对其初始化,传送速度比加载和存储指令要快;传送数据不需要通过累加器;可以寻址程序存储器;与RPT指令相结合(重复时,这些指令都变成单周期指令),可以实现数据块传送。,2 数据块传送,特点,(1)数据存储器数据存储器 这类指令有:MVDK Smem,dmad 指令的字数/执行周期 2/2MVKD dmad,Smem;Smem=dmad 2/2MVDD Xmem,Ymem ;Ymem=Xmem 1/1(2)程序存储器数据存储器 这类指令有:MVPD pmad,Smem ;Smem=pmad 2/3MVDP Smem,
3、pmad ;pmad=Smem 2/4pmad为16位立即数程序存储器地址;dmad为16位立即数数据存储器地址;Smem为数据存储器地址;Xmem、Ymem为双操作数数据存储器地址,Xmem从DB数据总线上读出。Ymem从CB数据总线上读出。,(3)数据存储器MMR 这类指令有:MVDM dmad,MMR ;指令的字数/执行周期 2/2MVMD MMR,dmad ;dmad=MMR 2/2MVMM mmrx,mmry ;mmry=mmrx 1/1(4)程序存储器(Acc)数据存储器 包括:READA Smem ;Smem=prog(A) 1/5WRITA Smem ;prog(A)= Sme
4、m 1/5 mmrx,mmry为AR0AR7或SP; MMR为任何一个存储器映象寄存器;,例5-6 将数组x5 初始化为1,2,3,4,5。.data ;定义初始化数据段起始地址TBL: .word 1,2,3,4,5 ;为标号地址TBL ;开始的5个单元赋初值 .sect “.vectors” ;定义自定义段,并获 ;得该段起始地址 B START;无条件转移到标号为START的地址 .bss x,5 ;为数组x分配5个存储单元 .text ;定义代码段起始地址START:STM #x,AR5 ;将x的首地址存入AR5 RPT #4 ;设置重复执行5次下条指令 MVPD TBL,*AR5+
5、;将TBL开始的5个值传给x,(1)程序存储器数据存储器,例5-7 将数据存储器中的数组x10复制到数组y10。 .title “cjy1.asm” ;为汇编源程序取名 .mmregs ;定义存储器映象寄存器STACK .usect “STACK”,30H;设置堆栈 .bss x,10 ;为数组x分配10个存储单元 .bss y,10 ;为数组y分配10个存储单元 .datatable:.word 1,2,3,4,5,6,7,8,9,10 .def start ;定义标号start .text,(2)数据存储器数据存储器,start:STM #0,SWWSR ;复位SWWSR STM #STA
6、CK+30H,SP;初始化堆指针 STM #x,AR1 ;将目的地首地址赋给AR1 RPT #19 ;设定重复传送的次数为20次 MVPD table,*AR1+ ;程序存储器传送到数 ;据存储器 STM #x,AR2 ;将x的首地址存入AR2 STM #y,AR3 ;将y的首地址存入AR3 RPT #19 ;设置重复执行20次下条指令 MVDD *AR2+,*AR3+;将地址x开始的20个值 ;复制到地址y开始的20个单元end: B end .end,例5-2 编写求解加、减法的程序,计算z=x+y-w。SUM1:LD x,A ;将x地址的内容送A ADD y,A ;将y地址的内容与A中x
7、值相加 SUB w,A ;将A中的内容与w 相减,得z STL A,z ;将A的的计算值存入z 地址中例5-3 写求解直线方程的程序,计算y=mx+b。 SUM2:LD m,T ;将m 地址的内容送T MPY x,A ;将x 地址的内容与T中的m相乘, ;结果送A ADD b,A ;将A中的mx与b 地址的内容相加, ;结果送A STL A,y ;将A的的计算结果存入y 地址中,3 加、减法和乘法运算,用间接寻址方式获得操作数,且辅助寄存器只用AR2AR5;占用程序空间小;运行速度快。,4双操作数乘法,特点,例5-8 编制求解 的程序。利用双操作数指令可以节省机器周期。迭代次数越多,节省的机器
8、周期数也越多。本例中,在每次循环中,双操作数指令都比单操作数指令少用一个周期,节省的总机器周期数=1T*N(迭代次数)=NT。,单操作数指令方案 双操作数指令方案 LD #0,B LD #0,B STM #a,AR2 STM #a,AR2 STM #x,AR3 STM #x,AR3 STM #19,BRC STM #19,BRC RPTB done-1 RPTB done-1 LD *AR2+,T;1T MPY *AR2+,*AR3+,A;1T MPY *AR3+,A;1T ADD A,B ;1T ADD A,B ;1Tdone:STH B,y done:STH B,y STL B,y+1 S
9、TL B,y+1,在单个周期内同时利用C总线和D总线,得到32位操作数。,5长字运算,特点,使用长操作数指令时,按指令中给出的地址存取的总是高16位操作数。这样,有两种数据排列方法:(1)偶地址排列法 指令中给出的地址为偶地址,存储器中低地址存放高16位操作数。如: DLD *AR3+,A执行前:A=00 0000 0000 执行后:A=00 6CAC BD90 AR3=0100 AR3=0102 (0100h)=6CAC(高字) (0100h)=6CAC (0101h)=BD90(低字) (0101h)=BD90,(2)奇地址排列法 指令中给出的地址为奇地址,存储器中低地址存放低16位操作数
10、。如: DLD *AR3+,A执行前:A=00 0000 0000 执行后:A=00 BD90 6CAC AR3=0101 AR3=0103 (0100h)=6CAC(低字) (0100h)=6CAC (0101h)=BD90(高字) (0101h)=BD90推荐采用偶地址排列法,将高16位操作数放在偶地址存储单元中。如: 程序存储器 .long 12345678 h ;偶地址:1234 ;奇地址:5678 数据存储器 .bss xhi, 2, 1, 1 ;偶地址:xhi ;奇地址:xlo 变量名称 字长 页邻接 偶地址排列法,例5-9 计算Z32=X32+Y32。标准运算 长字运算 LD x
11、hi,16,A DLD xhi,A ADDS xlo,A DADD yhi,A ADD yhi,16,A DST A,zhi ADDS ylo,A (3个字,3个T) STH A,Zhi STL A,Zlo(6个字,6个T),(1)并行运算指同时利用D总线和E总线。其中,D总线用来执行加载或算术运算,E总线用来存放先前的结果。(2)并行指令都是单字单周期指令。(3)并行运算时所存储的是前面的运算结果,存储之后再进行加载或算术运算。(4)并行指令都工作在累加器的高位。,6并行运算,特点,表5-1 并行指令举例,例5-10 编写计算z=x+y和f=d+e的程序段。在此程序段中用到了并行存储/加载指
12、令,即在同一机器周期内利用E总线存储和D总线加载。 数据存储器分配如图5-4所示。 .title “cjy3.asm” .mmregsSTACK .usect “STACK”,10H .bss x,3 ;为第一组变量 ;分配3个存储单元 .bss d,3 ;为第二组变量 ;分配3个存储单元 .def start .datatable: .word 0123H,1027H,0,1020H,0345H,0,.text start:STM #0,SWWSR STM #STACK+10H,SP STM #x,AR1 RPT #5 MVPD table,*AR1+ STM #x,AR5 ;将第一组变量的
13、首地址传给AR5 STM #d,AR2 ;将第二组变量的首地址传给AR2 LD #0,ASM ;设置ASM=0 LD *AR5+,16,A ;将x的值左移16位放入A的高端字 ADD *AR5+,16,A ;将y值左移16位与A的高端字x相加 ST A,*AR5 ;将A中的和值右移16位存入z中 LD *AR2+,B ;将d的值左移16位放入B的高端字 ADD *AR2+,16,B ;将e值左移16位与B的高端字d相加 STH B,*AR2 ;将B的高端字中的和值存入f中end: B end .end,9小数运算,整数运算的问题(1)两个16位整数相乘,乘积总是“向左增长”。这意味着多次相乘后
14、,乘积将会很快超出定点器件的数据范围。(2)保存32位乘积到存储器,要开销2个机器周期以及2个字的存储器单元。(3)由于乘法器都是16位相乘,因此很难在后续的递推运算中,将32位乘积作为乘法器的输入。小数运算的优点(1)乘积总是“向右增长”。这就味着超出定点器件数据范围的将是不太感兴趣的部分。(2)既可以存储32位乘积,也可以存储高16位乘积,这就允许用较少的资源保存结果。(3)可以用于递推运算。,小数运算与整数运算的比较,C54x采用2的补码表示小数,其最高位为符号位,数值范围从-11。一个16位2的补码小数(Q15格式)的每一位的权值为: MSB(最高位) LSB(最低位) -1. 1/2
15、 1/4 1/8 2-15一个十进制小数乘以32768之后再将其十进制整数部分转换成十六进制数,就能得到这个十进制小数的2的补码表示了。1 7FFFh0.5正数:乘以32768 4000h0 0000h-0.5 负数:其绝对值部分乘以32768,再取反加1 C000h-1 8000h,(1)小数的表示方法,在汇编语言中,是不能直接写入十进制小数的,可写为整数运算式。如果要定义一个系数0.707,可以写成:.word 32768*707/1000不能写成32768*0.707。,注意,Q格式表示法,在Q格式中,Q之后的数字(如Q15格式中的15)决定小数点右边有多少位二进制位,故Q15表示在小数
16、点后有15位小数。当用一个16位的字来表示Q15格式时,在MSB(最高位)的右边有一个小数点,而MSB表示符号位。所以Q15的表示数字可表示范围从+1(以+0.999997表示)到-1的值。,通过合适的Q格式,可以把数值根据所需的精确度做适当地转换,以便定点数的DSP也可以处理高精度的浮点数。下面以Q15为例,说明转换的过程。1) 先确定准备转换的十进制数值N,是在Q15格式的数值范围之间,即-1.000000N+0.999997。2)数值N乘以215,即N=N215=N327683)把步骤2)的结果加216,即N=N+216=N+65536。4)步骤3)的结果转换成十六进制,并把第17位舍弃
17、掉,得到的结果就是N的Q15转换值。,下面通过把-0.2345及+0.2345转换成Q15格式来说明转换方法。-0.2345的转换为:-0.234532768=-7684.1-7684-7684+65536=5785257852转换成十六进制数值为0E1FCh,所以结果为E1FCh。+0.2345的转换为:0.234532768=7684.176847684+65536=7332073320转换成十六进制数值为11E04h,并把第17位舍弃掉,结果为1E04h。,以字长为4位和8位累加器为例,先看一个小数乘法的例子。 0 1 0 0(0.5230.5=(4)10=(0100)2) 1 1 0
18、1(-0.37523(-0.375)=(-3)10 0 1 0 0 =(1101)补) 0 0 0 0 0 1 0 01 1 0 0 (-0100)1 1 1 0 1 0 0 (-0.1875=-12/26-12=(1110100)补),(2)小数乘法与冗余符号位,上述乘积是7位,当将其送到8位累加器时,为保持乘积的符号,必须进行符号位扩展,这样,累加器中的值为11110100(-0.09375=-12/27),出现了冗余符号位。原因是: S x x x (Q3) S y y y (Q3)S S z z z z z z (Q6格式)即两个带符号数相乘,得到的乘积带有2个符号位,造成错误的结果。
19、同样,对于两个十六位数相乘,乘积只有30位,在最高的两位也是符号位,同样会造成错误的结果。,解决冗余符号的办法是:在程序中设定状态寄存器ST1中的FRCT(小数方式)位1,在乘法器将结果传送至累加器时就能自动地左移1位,累加器中的结果为:zzzzzz0(Q7格式),即11101000(-0.1875=-24/27-24=(11101000)补),自动地消去了两个带符号数相乘时产生的冗余符号位。所以在小数乘法编程时,应当事先设置FRCT位:SSBX FRCT MPY *AR2,*AR3,ASTH A,Z这样,C54x就完成了Q15*Q15=Q15的小数乘法。,例5-13 编制计算 的程序段,其中
20、数据均为小数:a1=0.1,a2=0.2,a3=-0.3,a4=0.4,x1=0.8,x2=0.6,x3=-0.4,x4=-0.2。 .title “cjy4.asm” .mmregsSTACK .usect “STACK”,10H .bss a,4 ;为a分配4个存储单元 .bss x,4 ;为x分配4个存储单元 .bss y,1 ;为结果y分配1个存储单元 .def start .data ;定义数据代码段,table: .word 1*32768/10 ;在table开始的8个.word 2*32768/10 ;地址放数据 .word -3*32768/10 .word 4*32768/
21、10 .word 8*32768/10 .word 6*32768/10 .word -4*32768/10 .word -2*32768/10,.text ;定义可执行程序代码段start:SSBX FRCT ;设置FRCT位,表示进行小数乘 STM #x,AR1 ;将x的首地址传给AR1 RPT #7 ;重复8次下条指令 MVPD table,*AR1+ ;将程序空间8个数传给数据存储器 STM #x,AR2 ;将数据存储器第一个数x1的地址传给AR2 STM #a,AR3 ;将数据存储器第五个数a1的地址传给AR3 RPTZ A,#3 ;将A清零,重复4次下条指令 MAC *AR2+,*
22、AR3+,A ;执行乘法累加和,结果放在A中 STH A,y ;将A的高端字存入结果y,低端字省去end: B end ;原处循环等待 .end 结果y=0x1EB7。转换为十进制数:y=(1163+14162+11161+7160)/32768=0.24,10 除法运算,方法:减法指令加重复指令实现无符号运算,(1)当被除数除数 此时商为小数。,(2)当被除数除数时 商为整数。,第三节 用DSP实现FIR滤波器,1FIR滤波器基本概念,1. FIR滤波器没有反馈回路,因此它是无条件稳定系统,其单位冲激响应h(n)是一个有限长序列。2. FIR滤波算法实际上是一种乘法累加运算。,要点,2.用线
23、性缓冲区法实现z-1 的FIR滤波器设计,对于N级的FIR滤波器,在数据存储器中开辟一个称之为滑窗的N个单元的缓冲区,存放最新的N个输入样本;从最老的样本开始,每读一个样本后,将此样本向下移位,读完最后一个样本后,输入最新样本至缓冲区的顶部。用线性缓冲区实现z-1的优点是,新老数据在存储器中存放的位置直接明了。,特点,C54x片内没有I/O资源,CPU通过外部译码可以寻址64K的I/O单元。有两条实现输入和输出的指令:PORTR PA,Smem ;将为PA的端口内容送 ;数据存储器SmemPORTW Smem,PA ;将地址为Smem的数据 ;存储器内容送端口PA,数据的输入/输出,N=6的线
24、性缓冲区存储器图,使用存储器延时指令DELAY,可以将数据存储单元中的内容向较高地址的下一单元传送。实现z-1的运算指令为:DELAY Smem ;(Smem)Seme+1,即数据存储 ;器单元的内容送下一高地址单元DELAY *AR2 ;AR2指向源地址,即将AR2所指单 ;元内容复制到下一高地址单元中,存储器的延时操作,延时指令与其它指令的结合,LT+DELAYLTD指令 ;单数据存储器的值装入 ;T寄存器并送下一单元延时MAC+DELAYMACD指令 ;操作数与程序存储器值相乘 ;后累加并送下一单元延时,例5-25 编写N=5,y(n)=a0*x(n)+a1*x(n-1)+a2*x(n-
25、2)+a3*x(n-3)+a4*x(n-4)的计算程序。先将系数a0a4存放在数据存储器中,然后设置线性缓冲区,用以存放输入和输出数据。,(1)用线性缓冲区和直接寻址方法实现FIR,线性缓冲区安排,.title “FIR1.ASM” ;定义源程序名 .mmregs ;定义存储器映象寄存器 .def start ;定义语句标号start .bss y,1 ;为结果y预留1个单元的空间XN .usect “XN”,1 ;在自定义的未初始化段“XN”XNM1 .usect “XN”,1 ;中保留5个单元的空间XNM2 .usect “XN”,1XNM3 .usect “XN”,1 XNM4 .use
26、ct “XN”,1A0 .usect “A0”,1 ;在自定义的未初始化段“A0”A1 .usect “A0”,1 ;中保留5个单元的空间A2 .usect “A0”,1A3 .usect “A0”,1A4 .usect “A0”,1PA0 .set 0 ;定义PA0为输出端口PA1 .set 1 ;定义PA1为输入端口,.datatable: .word 1*32768/10 ;假定程序空间有五个参数 .word -3*32768/10 .word 5*32768/10 .word -3*32768/10 .word 1*32768/10 .textstart: .SSBX FRCT ;设置
27、进行小数相乘 STM #A0,AR1 ;将数据空间用于放参数的首地址送AR1 RPT #4 ;重复下条指令5次传送 MVPD table,*AR1+ ;传送程序空间的参数到数据空间 LD #XN,DP ;设置数据存储器页指针的起始位置 PORTR PA1,XN ;从数据输入端口I/O输入最新数据x(n)FIR1: LD XNM4,T ;x(n-4)T,MPY A4,A ;a4*x(n-4)ALTD XNM3 ;x(n-3)T,x(n-3)x(n-4)MAC A3,A ;A+a3*x(n-3)ALTD XNM2 ;x(n-2)T,x(n-2)x(n-3)MAC A2,A ;A+a2*x(n-2)
28、ALTD XNM1 ;x(n-1)T,x(n-1)x(n-2)MAC A1,A ;A+a1*x(n-1)ALTD XN ;x(n)T,x(n)x(n-1)MAC A0,A ;A+a0*x(n)ASTH A,y ;保存y(n)的高字节PORTW y,PA0 ;输出y(n)BD FIR1 ;执行完下条指令后循环PORTR PA1,XN ;输入x(n).end,vectors.objfir1.obj-o fir1.out-m fir1.map-e startMEMORY PAGE 0 : EPROM: org=01OOOH len=01000H VECS: org=03F80H len=00080H
29、 PAGE 1 : SPRAM: org=00060H len=00020H DARAM: org=00080H len=01380H SECTIONS .vectors: VECS PAGE 0 .text: EPROM PAGE 0 .data: EPROM PAGE 0 .bss: SPRAM PAGE 1 .XN: DARAM align(8) PAGE 1 .A0: DARAM align(8) PAGE 1 ,(2)用线性缓冲区和间接寻址方法实现FIR,例5-26 编写y(n)=a0*x(n)+ a1*x(n-1)+ a2*x(n-2)+ a3*x(n-3)+ a4*x(n-4)的
30、计算程序,其中N=5。,将系数a0a4存放在数据存储器中,并设置线性缓冲区存放输入数据。利用AR1和AR2分别作为间接寻址线性缓冲区和系数区的辅助寄存器。,.title “FIR2.ASM” ;定义源程序名 .mmregs ;定义存储器映象寄存器 .def start ;定义语句标号start .bss y,1 ;为结果y预留1个单元的空间x .usect “x”,5 ;在自定义的未初始化段“x”中保留5个单元的空间a .usect “a”,5 ;在自定义的未初始化段“a”中保留5个单元的空间PA0 .set 0 ;定义PA0为输出端口PA1 .set 1 ;定义PA1为输入端口 .datat
31、able: .word 2*32768/10 ;假定程序空间有五个参数 .word -3*32768/10 .word 4*32768/10 .word -3*32768/10 .word 2*32768/10,.textstart: STM #a,AR2 ;将数据空间用于放参数 ;的首地址送AR2 RPT #4 ;重复下条指令5次传送 MVPD table,*AR2+ ;传送程序空间的参数到数据空间 STM #x+4,AR1 ;AR1指向x(n-4) STM #a+4,AR2 ;AR2指向a4 STM #4,AR0 ;指针复位值4AR0 SSBX FRCT ;小数相乘 LD #x,DP ;设
32、置数据存储器页指针 ;的起始位置 PORTR PA1,x ;从端口PA1输入最新值x(n),FIR2: LD *AR1-,T ;x(n-4)T MPY *AR2-,A ;a4*x(n-4)A LTD *AR1- ;x(n-3)T,x(n-3)x(n-4) MAC *AR2-,A ;A+a3*x(n-3)A LTD *AR1- ;x(n-2)T,x(n-2)x(n-3) MAC *AR2-,A ;A+a2*x(n-2)A LTD *AR1- ;x(n-1)T,x(n-1)x(n-2) MAC *AR2-,A ;A+a1*x(n-1)A LTD *AR1 ;x(n)T, x(n)x(n-1) MA
33、C *AR2+0,A ;A+a0*x(n)A ,AR2复原,指向a4 STH A,y ;保存运算结果的高位字到y(n) PORTW y(n),PA0 ;将结果y(n)输出到端口PA0 BD FIR2 ;执行完下条指令后,从FIR2开始循环 PORTR PA1,*AR1+0;输入新值x(n),AR1复原指向x+4 .end,(3) 用线性缓冲区和带移位双操作数寻址方法实现 FIR,例5-27 编写y(n)=a0*x(n)+ a1*x(n-1)+ a2*x(n-2)+ a3*x(n-3)+ a4*x(n-4)的计算程序,其中N=5。,与前面的编程不同,本例中,系数a0a4存放在程序存储器中,输入数
34、据存放在数据存储器的线性缓冲区中。乘法累加利用MACD指令,该指令完成数据存储器单元与程序存储器单元相乘,并累加、移位的功能。,.title “FIR3.ASM” ;定义源程序名 .mmregs ;定义存储器映象寄存器 .def start ;定义语句标号start .bss y,1 ;为结果y预留1个单元的空间x .usect “x”,6 ;在自定义的未初始化 ;段“x”中保留6个单元PA0 .set 0 ;定义PA0为输出端口PA1 .set 1 ;定义PA1为输入端口 .dataCOEF: .word 1*32768/10 ;假定程序空间有五个参数,a4 .word -4*32768/1
35、0 ;a3 .word 3*32768/10 ;a2 .word -4*32768/10 ;a1 .word 1*32768/10 ;a0,.textstart: SSBX FRCT ;小数乘法 STM #x+5,AR1 ;AR1指向x(n-4) STM #4,AR0 ;设置AR1复位值 LD #x+1,DP ;设置数据存储器页指针的起始位置 PORTR PA1,x+1 ;输入最新值x(n)FIR3: RPTZ A,#4 ;累加器A清0,设置重复下条指令5次 MACD *AR1-,COEF,A ;x(n-4)T,A= x(n-4)*a4 +A ;(PAR)+1PAR,x(n-4)x(n-5) STH A,*AR1 ;暂存结果到y(n) PORTW *AR1+,PA0;输出y(n)到PA0,AR1指向x(n) BD FIR3 ;执行下条指令后循环 PORTR PA1,*AR1+0;输入新数据到x(n),AR1指向x(n-4),