高分求助在线等:将2个汇编程序合成一个
AD转换的汇编程序如下:位定义SOBITP1.0;数据输入CSBITP1.1;从机选择SCKBITP1.2;时钟数据字节定义DATAHDATA30H;读取数据高位DATA...
AD转换的汇编程序如下:
位定义
SO BIT P1.0;数据输入
CS BIT P1.1;从机选择
SCK BIT P1.2;时钟
数据字节定义
DATAH DATA 30H;读取数据高位
DATAL DATA 31H;读取数据低位
TDATAH DATA 32H;温度高位
TDATAL DATA 33H;温度低位
读取温度子程序
READT: CLR CS;停止转换并输出数据
CLR SCK;时钟变低
MOV R2,#08H
READH: MOV C,SO;读取D15~D8高8位数据
RLC A
SETB SCK
NOP
CLR SCK
DJNZ R2,READH
MOV DATAH,A;将读取的高8位数据保存
MOV R2,#08H
READL: MOV C,SO;读取D7~D0低8位数据
RLC A
SETB SCK
NOP
CLR SCK
DJNZ R2,READL
MOV DATAL,A;将读取的低8位数据保存
SETB CS;启动另一次转换过程
RET
数据转换子程序,将读得的16位数据转换为12位温度值,去掉无用的位
D16T12:MOV A,DATAL
RLC A;循环左移一位,以去掉D15伪志位
MOV DATAL,A
MOV A,DATAH
RLC A
SWAP A;将DATAH中的数据高低4位互换
MOV B,A;数据暂存于B中
MOV A,#0FH;得到温度值的D11~D8位,并将D15~D12位置0
MOV TDATAH,A;转换后的数据送温度高位
MOV A,B;取出温度值的D7~D4位
ANL A,#0F0H
MOV B,A;暂存于B中
MOV A,DATAL取出温度值的D3~D0
ANL A,#0F0H
SWAP A
ORL A,B;合并成低位字节
MOV TDATAL,A转换后的数据送温度低位
RET
这段程序为AD转换程序,数据存放于温度高位与低位中
显示程序源代码如下:
ORG 0000H
ajmp start
org 00a0h
start:MOV SP,#50H
LCALL MAX0;调用初始化7219函数
LOOP:LCALL DISP;调用显示函数
SJMP LOOP 跳转到loop
MAX0:MOV A,#0BH;设置扫描位数
MOV R2,#03H ;显示4位
LCALL YW
MOV A,#0AH;设置亮度
MOV R2,#05H
LCALL YW
MOV A,#09H;设置译码格式
MOV R2,#0FFH ;设置BCD码
LCALL YW
MOV A,#0CH;设置正常工作
MOV R2,#01H
LCALL YW
RET
DISP:MOV R0,#40H;要显示的数据存储地址
MOV R1,#01H;数码管的地址寄存器01h~08h
MOV R3,#08H
LOOP1:MOV A,@R0
MOV R2,A
MOV A,R1
LCALL YW
INC R0
INC R1
DJNZ R3,LOOP1
RET
YW:LCALL SEND;调用发送函数,一位一位的从din送入7219
MOV A,R2
LCALL SEND;调用发送子程序
CLR P1.4;load 信号上升沿对其锁存
NOP
NOP
SETB P2.2;复位
RET
SEND:MOV R4,#08H;对其字节向左循环移8位
LOOP2:CLR P2.1;clk上升沿对每一位数据锁存
RLC A
MOV P2.0,C;将为累加器内数据传送到P2.0
NOP
NOP
NOP
SETB P2.1
DJNZ R4,LOOP2;不为0转移
RET
这段程序为显示程序,需要从AD程序保存的温度值读出并送至显示,请帮忙把以上两个程序合成一个。编译成功再加分
请在程序后边加注释,要不我真的看不懂。。。。 一楼的朋友,解释一下为什么显示然后才进行AD转换?这样怎么可以把要转换的数据显示呢?好像顺序颠倒了呀 展开
位定义
SO BIT P1.0;数据输入
CS BIT P1.1;从机选择
SCK BIT P1.2;时钟
数据字节定义
DATAH DATA 30H;读取数据高位
DATAL DATA 31H;读取数据低位
TDATAH DATA 32H;温度高位
TDATAL DATA 33H;温度低位
读取温度子程序
READT: CLR CS;停止转换并输出数据
CLR SCK;时钟变低
MOV R2,#08H
READH: MOV C,SO;读取D15~D8高8位数据
RLC A
SETB SCK
NOP
CLR SCK
DJNZ R2,READH
MOV DATAH,A;将读取的高8位数据保存
MOV R2,#08H
READL: MOV C,SO;读取D7~D0低8位数据
RLC A
SETB SCK
NOP
CLR SCK
DJNZ R2,READL
MOV DATAL,A;将读取的低8位数据保存
SETB CS;启动另一次转换过程
RET
数据转换子程序,将读得的16位数据转换为12位温度值,去掉无用的位
D16T12:MOV A,DATAL
RLC A;循环左移一位,以去掉D15伪志位
MOV DATAL,A
MOV A,DATAH
RLC A
SWAP A;将DATAH中的数据高低4位互换
MOV B,A;数据暂存于B中
MOV A,#0FH;得到温度值的D11~D8位,并将D15~D12位置0
MOV TDATAH,A;转换后的数据送温度高位
MOV A,B;取出温度值的D7~D4位
ANL A,#0F0H
MOV B,A;暂存于B中
MOV A,DATAL取出温度值的D3~D0
ANL A,#0F0H
SWAP A
ORL A,B;合并成低位字节
MOV TDATAL,A转换后的数据送温度低位
RET
这段程序为AD转换程序,数据存放于温度高位与低位中
显示程序源代码如下:
ORG 0000H
ajmp start
org 00a0h
start:MOV SP,#50H
LCALL MAX0;调用初始化7219函数
LOOP:LCALL DISP;调用显示函数
SJMP LOOP 跳转到loop
MAX0:MOV A,#0BH;设置扫描位数
MOV R2,#03H ;显示4位
LCALL YW
MOV A,#0AH;设置亮度
MOV R2,#05H
LCALL YW
MOV A,#09H;设置译码格式
MOV R2,#0FFH ;设置BCD码
LCALL YW
MOV A,#0CH;设置正常工作
MOV R2,#01H
LCALL YW
RET
DISP:MOV R0,#40H;要显示的数据存储地址
MOV R1,#01H;数码管的地址寄存器01h~08h
MOV R3,#08H
LOOP1:MOV A,@R0
MOV R2,A
MOV A,R1
LCALL YW
INC R0
INC R1
DJNZ R3,LOOP1
RET
YW:LCALL SEND;调用发送函数,一位一位的从din送入7219
MOV A,R2
LCALL SEND;调用发送子程序
CLR P1.4;load 信号上升沿对其锁存
NOP
NOP
SETB P2.2;复位
RET
SEND:MOV R4,#08H;对其字节向左循环移8位
LOOP2:CLR P2.1;clk上升沿对每一位数据锁存
RLC A
MOV P2.0,C;将为累加器内数据传送到P2.0
NOP
NOP
NOP
SETB P2.1
DJNZ R4,LOOP2;不为0转移
RET
这段程序为显示程序,需要从AD程序保存的温度值读出并送至显示,请帮忙把以上两个程序合成一个。编译成功再加分
请在程序后边加注释,要不我真的看不懂。。。。 一楼的朋友,解释一下为什么显示然后才进行AD转换?这样怎么可以把要转换的数据显示呢?好像顺序颠倒了呀 展开
2个回答
展开全部
在编写和调试C6000程序时,为了使C6000代码获得最好的性能,我们需要按照软件编程的3个阶段进行,每个阶段完成的任务如下[4]:
第一阶段:开始可以不考虑C6000的有关知识,完全根据任务编写C语言程序。在CCS环境下用C6000的代码产生工具,编译产生在C6000内运行的代码,证明其功能正确。然后再用CCS的调试工具,如debug和profiler等,分析确定代码可能存在的、影响性能的低效率段。为进一步改进代码性能,需要进入第二阶段。
第二阶段:利用内联函数、CCS编译选项和其他具体优化方法改进C语言程序。重复第一阶段,检查所产生的C6000代码性能。如果产生的代码仍不能达到所期望的性能,则进入第三阶段。
第三阶段:从C语言程序中抽出对性能影响很大的程序段,用线性汇编重新编写,再用汇编优化器优化,链接,直到达到所期望的性能要求。
具体到G.729A标准编解码器的实时要求,第三阶段是工作的重点,而且线性汇编的重新编写要求对程序代码和DSP的特性有充分的了解。
3. G.729A代码的剖析
CCS集成开发环境为软件开发人员提供了高效的开发、调试工具。特别是它提供了评价器( profiler)的优化工具,通过收集在指定代码区间程序执行的统计性能,分析确定程序中各个段、各个子函数所花费的处理器时间,从而把程序的优化集中在对程序性能影响最大的代码段上去[5]。其两种不同的测试方法是:
(1) 在需要测定复杂度的程序段的开头和结尾处设定两个断点,打开时钟窗口,运行程序。在第一个断点处执行停止,这时双击时钟窗口使之清0,接着继续执行程序,在第二个断点处停止,这时,时钟窗口显示的值便是该段代码的复杂度。这在测试程序中一个函数的复杂度是非常有用的。
(2) 先打开统计窗口,在需要测试的程序段头尾设置统计点((Probe Point)。程序运行结束后,统计窗口内该程序段后面的统计值便是该代码段的复杂度。这种方法较简单,统计点自动收集统计信息,无需手工干涉,这在测定程序多段代码的复杂度是非常有用。
4. 线性汇编的优化
线性汇编是TI提供的一种汇编语言,其指令系统和汇编语言的指令系统完全相同,但在编写时不需要指定寄存器和操作单元,也不需要考虑延时的问题,因此编写线性汇编相对要容易一些 [6]。
经过第一阶段和第二阶段的优化后,音频编码程序在DM642上的运行状况有了很大改善,但是经测试仍然没有到达实时效果,而高级语言的效率几乎发挥到了极致,测试的速度达到了36.5帧/s,是未优化之前的10倍。这时,我们采用线性汇编语言重新编写C代码的低效率段程序,进一步提高程序的执行效率和充分利用DM642的硬件资源,最终按设计要求在DM642实时实现G.729A编码。在前面的DSP开发流程已经提过,DSP开发的最后一个手段是用汇编重写C代码,它是唯一可以既提高程序执行速度又可以减少程序体积的方法。由于针对并行处理器编写汇编的难度很大,一般采取的是混合编程的方法,即程序的主要部分用C代码,部分耗时较大的函数可以用线性汇编改写。
在编写线性汇编优化代码的过程中,为了提高代码执行效率,我们需要遵循以下原则[7]:
(1)写并行代码:通过使用汇编指令并行执行的方法减少循环内的执行周期数,优化线性汇编代码。这里的关键问题是弄清指令相关性,只有不相关的指令才能并行执行。辨别指令是否相关,可以使用相关图。
(2)处理跳转指令和转移指令:汇编程序的一大特点就是频繁地跳转,当满足不同的条件时,要求程序进行不同的操作,或跳到相应的位置。对于“大于”、“大于等于”、“小于”、“小于等于”等较为接近的逻辑判断和处理,应慎重对待,否则将产生逻辑性错误,并且很难调试。当发生溢出需进行相应处理时,这种现象尤为突出。
(3)尽量减少循环体内的指令数:G.729A的算法实现,有许多是在循环内部完成的,有些地方如固定码本搜索过程中,为了确定四个非0脉冲的位置和幅度,还用到了多重循环。在循环内部,特别是在嵌套较深的循环内部,减少一条指令可以大大降低程序的操作次数。例如,对于一个每重循环8次的四重嵌套循环,在最内层循环每减少一条指令,整个程序可以少执行84=4096语句。因此在设计程序时,能够放在循环体外执行的语句,尽量放在循环体外执行。
(4)展开程序体:在一定条件下,尽量展开程序,以减少子程序的调用和返回次数,牺牲空间换取时间。
G.729A算法中的LPC模块、LSP量化及激励码本搜索耗时最多,为进一步提高代码效率,对相关计算、FIR滤波等部分函数用线性汇编语言进行了改写,并用画相关图等方法有针对性的进行优化。经汇编优化器优化后,代码效率比C语言直接编译有明显提高。
5. 优化工作的创新点
在对G.729A的优化中,本文在前人研究成果的基础上,针对TMS320DM642 DSP系列芯片提出了一些有价值的新方法。这些创新点在不同程度上提高了代码的优化速度和执行效率,在语音编解码的DSP实时实现中起到了关键性作用。下面,以举例的方式阐明一些经典的方法。
5.1 绘制分析图,掌握函数结构
对于一个语句较多、结构复杂的函数,为了充分了解其逻辑结构和语句的相关性,我们通常采用画分析图的方法。分析图的形式比较灵活,可以根据具体的情况选用不同的制图工具。在编写线性汇编的时候,需要考虑存取数组中的元素,数据打包操作和数据相关性等问题,分析图有助于正确处理这些问题。
在对函数Cor_h_X( )优化过程中,我们遇到了一定的困难,原因在于其中有一个双层的循环体,内层的次数与外层有关,外层的循环次数为40,并且循环内部的语句有先后的相关性。这样的结构如果用循环展开的方法将会用到大量的寄存器,数目超出了64个,需要开辟额外的内存空间去存放临时变量,而读写内存会消耗较多的时间,因此这样执行效率不会有充分的提高。对此,我们利用分析图描述了函数中关键代码的数组X[ ],h[ ]的使用情况,如图1所示:
图1 cor_h_X( )函数分析图(部分)
图1直观地反映了数组16位h[ ]和16位X[ ]之间的乘加关系,从函数cor_h_X( )中可知,两个数组的乘积之和要对应的保存在临时数组32位Y[ ]中。通过研究此分析图,我们发现h[ ]与X[ ]中的一些元素进行乘积和处理之后就不再被使用,那么存储这些元素的寄存器可以存放中间结果(Y[]的元素),这样就可节省寄存器的使用个数,免去了开辟内存空间和中间变量的存取指令。
对于函数cor_h_X( ),利用上述思想编写线性汇编,只需要定义57个寄存器就可以完成所用的操作,存取指令从1760条优化到30条,仅为原来的1/60。同时执行速度从390072个时钟减少到35871个,降为原来的1/10。
绘制的分析图可以包含相关图,相关表等,使资源安排更加合理。该方法在其他函数的改写中也多次使用到。
5.2 功能相似的函数或代码段合并为一个函数
线性汇编在提高代码效率的同时也成倍的增加了代码尺寸,以上述cor_h_X( )为例,它在该写后代码尺寸从660条增大到7776条(该数据由CCS剖析工具分析所得)。在工程应用中,对于有限的内存程序区,我们会适当减少程序占用的空间。合并功能相似的函数可以达到这一要求。
在LSP量化处理中,源代码中给出了2个LSP选择函数:Lsp_select_1( )和Lsp_select_2( ),而我们发现它们具有相同的功能和相似的结构,因此,在对两者的线性汇编改写中,我们只需编写一个函数(命名为Lsp_select)即可实现LSP量化处理中这两个模块的功能。
另外,在对于一些数组拷贝,数组初始化的代码,我们同样可以用此方法,编写一个函数实现,这样可以在提高执行效率的同时,减少程序占用的内存空间。
5.3 多个循环合并为一个循环
C代码改写线性汇编的时候,我们常常会发现,只要作一些调整,两个或多个循环完成的操作完全可以由一个循环来完成。以LPC子模块240点加窗语音的自相关计算Autocorr()函数为例,经过优化改写的C代码(部分)如下:
for(i=0; i<L_WINDOW; i++) //第一个循环体
y[i] = (_smpy(x[i], hamwindow[i])+0x00008000L)>>16;
sum = 1; //避免为0的情况
for(i=0; i<L_WINDOW; i++) //第二个循环体
sum = _sadd(sum,_smpy(y[i], y[i]));
这段代码包含了两个for循环,在CCS中直接编译运行并行度很差,利用线性汇编重写代码。我们发现两个循环体的循环次数均为60(L_WINDOW=60),所处理的数组不同,并且两个循环没有相关性,可以把第一和第二个循环合并成一个循环。前者的功能是对语音信号进行加窗;后者是实现乘累加(Mac)。两者合并后采用线性汇编编写,其代码如下:
mvk 60,i //设置循环次数
loop1: lddw *ham++,hamih:hamil //hamwindow[]指针
lddw *x++,xih:xil //x[]指针
smpy2 hamil,xil,yi1:yi0 //两对16位操作数相承,并行执行
smpy2 hamih,xih,yi3:yi2
sadd yi0,con0x8000,yi0
sadd yi1,con0x8000,yi1
sadd yi2,con0x8000,yi2
sadd yi3,con0x8000,yi3
packh2 yi1,yi0,yl //数据打包技术
packh2 yi3,yi2,yh
stdw yh:yl,*y++ //双字存取,提高执行效率
smpy2 yl,yl,yi1:yi0
sadd sum0,yi1,sum0
sadd sum0,yi0,sum0
smpy2 yh,yh,yi3:yi2
sadd sum0,yi3,sum0
sadd sum0,yi2,sum0
add i,-1,i
[i] b loop1 //把第一和第二个循环合成一个大循环,减少转移次数
产生的汇编代码并行流水性能大大增加,耗费的时钟周期数从1310000减少到15000,少于改编前的1/8。
6. 结束语
关于编解码器执行的时钟周期,在线性汇编改写前后,文件版本通过CCS的profiler剖析工具得知:每10帧(100MS)从159700000降至68500000,仅为原来的42%。硬件版本进行测试得:编解码的帧数提高到了88帧/s以上,鉴于编码、解码的时间比例为5:1,所以,本系统编码已经达到100帧/s,完全符合实时通信的要求。
第一阶段:开始可以不考虑C6000的有关知识,完全根据任务编写C语言程序。在CCS环境下用C6000的代码产生工具,编译产生在C6000内运行的代码,证明其功能正确。然后再用CCS的调试工具,如debug和profiler等,分析确定代码可能存在的、影响性能的低效率段。为进一步改进代码性能,需要进入第二阶段。
第二阶段:利用内联函数、CCS编译选项和其他具体优化方法改进C语言程序。重复第一阶段,检查所产生的C6000代码性能。如果产生的代码仍不能达到所期望的性能,则进入第三阶段。
第三阶段:从C语言程序中抽出对性能影响很大的程序段,用线性汇编重新编写,再用汇编优化器优化,链接,直到达到所期望的性能要求。
具体到G.729A标准编解码器的实时要求,第三阶段是工作的重点,而且线性汇编的重新编写要求对程序代码和DSP的特性有充分的了解。
3. G.729A代码的剖析
CCS集成开发环境为软件开发人员提供了高效的开发、调试工具。特别是它提供了评价器( profiler)的优化工具,通过收集在指定代码区间程序执行的统计性能,分析确定程序中各个段、各个子函数所花费的处理器时间,从而把程序的优化集中在对程序性能影响最大的代码段上去[5]。其两种不同的测试方法是:
(1) 在需要测定复杂度的程序段的开头和结尾处设定两个断点,打开时钟窗口,运行程序。在第一个断点处执行停止,这时双击时钟窗口使之清0,接着继续执行程序,在第二个断点处停止,这时,时钟窗口显示的值便是该段代码的复杂度。这在测试程序中一个函数的复杂度是非常有用的。
(2) 先打开统计窗口,在需要测试的程序段头尾设置统计点((Probe Point)。程序运行结束后,统计窗口内该程序段后面的统计值便是该代码段的复杂度。这种方法较简单,统计点自动收集统计信息,无需手工干涉,这在测定程序多段代码的复杂度是非常有用。
4. 线性汇编的优化
线性汇编是TI提供的一种汇编语言,其指令系统和汇编语言的指令系统完全相同,但在编写时不需要指定寄存器和操作单元,也不需要考虑延时的问题,因此编写线性汇编相对要容易一些 [6]。
经过第一阶段和第二阶段的优化后,音频编码程序在DM642上的运行状况有了很大改善,但是经测试仍然没有到达实时效果,而高级语言的效率几乎发挥到了极致,测试的速度达到了36.5帧/s,是未优化之前的10倍。这时,我们采用线性汇编语言重新编写C代码的低效率段程序,进一步提高程序的执行效率和充分利用DM642的硬件资源,最终按设计要求在DM642实时实现G.729A编码。在前面的DSP开发流程已经提过,DSP开发的最后一个手段是用汇编重写C代码,它是唯一可以既提高程序执行速度又可以减少程序体积的方法。由于针对并行处理器编写汇编的难度很大,一般采取的是混合编程的方法,即程序的主要部分用C代码,部分耗时较大的函数可以用线性汇编改写。
在编写线性汇编优化代码的过程中,为了提高代码执行效率,我们需要遵循以下原则[7]:
(1)写并行代码:通过使用汇编指令并行执行的方法减少循环内的执行周期数,优化线性汇编代码。这里的关键问题是弄清指令相关性,只有不相关的指令才能并行执行。辨别指令是否相关,可以使用相关图。
(2)处理跳转指令和转移指令:汇编程序的一大特点就是频繁地跳转,当满足不同的条件时,要求程序进行不同的操作,或跳到相应的位置。对于“大于”、“大于等于”、“小于”、“小于等于”等较为接近的逻辑判断和处理,应慎重对待,否则将产生逻辑性错误,并且很难调试。当发生溢出需进行相应处理时,这种现象尤为突出。
(3)尽量减少循环体内的指令数:G.729A的算法实现,有许多是在循环内部完成的,有些地方如固定码本搜索过程中,为了确定四个非0脉冲的位置和幅度,还用到了多重循环。在循环内部,特别是在嵌套较深的循环内部,减少一条指令可以大大降低程序的操作次数。例如,对于一个每重循环8次的四重嵌套循环,在最内层循环每减少一条指令,整个程序可以少执行84=4096语句。因此在设计程序时,能够放在循环体外执行的语句,尽量放在循环体外执行。
(4)展开程序体:在一定条件下,尽量展开程序,以减少子程序的调用和返回次数,牺牲空间换取时间。
G.729A算法中的LPC模块、LSP量化及激励码本搜索耗时最多,为进一步提高代码效率,对相关计算、FIR滤波等部分函数用线性汇编语言进行了改写,并用画相关图等方法有针对性的进行优化。经汇编优化器优化后,代码效率比C语言直接编译有明显提高。
5. 优化工作的创新点
在对G.729A的优化中,本文在前人研究成果的基础上,针对TMS320DM642 DSP系列芯片提出了一些有价值的新方法。这些创新点在不同程度上提高了代码的优化速度和执行效率,在语音编解码的DSP实时实现中起到了关键性作用。下面,以举例的方式阐明一些经典的方法。
5.1 绘制分析图,掌握函数结构
对于一个语句较多、结构复杂的函数,为了充分了解其逻辑结构和语句的相关性,我们通常采用画分析图的方法。分析图的形式比较灵活,可以根据具体的情况选用不同的制图工具。在编写线性汇编的时候,需要考虑存取数组中的元素,数据打包操作和数据相关性等问题,分析图有助于正确处理这些问题。
在对函数Cor_h_X( )优化过程中,我们遇到了一定的困难,原因在于其中有一个双层的循环体,内层的次数与外层有关,外层的循环次数为40,并且循环内部的语句有先后的相关性。这样的结构如果用循环展开的方法将会用到大量的寄存器,数目超出了64个,需要开辟额外的内存空间去存放临时变量,而读写内存会消耗较多的时间,因此这样执行效率不会有充分的提高。对此,我们利用分析图描述了函数中关键代码的数组X[ ],h[ ]的使用情况,如图1所示:
图1 cor_h_X( )函数分析图(部分)
图1直观地反映了数组16位h[ ]和16位X[ ]之间的乘加关系,从函数cor_h_X( )中可知,两个数组的乘积之和要对应的保存在临时数组32位Y[ ]中。通过研究此分析图,我们发现h[ ]与X[ ]中的一些元素进行乘积和处理之后就不再被使用,那么存储这些元素的寄存器可以存放中间结果(Y[]的元素),这样就可节省寄存器的使用个数,免去了开辟内存空间和中间变量的存取指令。
对于函数cor_h_X( ),利用上述思想编写线性汇编,只需要定义57个寄存器就可以完成所用的操作,存取指令从1760条优化到30条,仅为原来的1/60。同时执行速度从390072个时钟减少到35871个,降为原来的1/10。
绘制的分析图可以包含相关图,相关表等,使资源安排更加合理。该方法在其他函数的改写中也多次使用到。
5.2 功能相似的函数或代码段合并为一个函数
线性汇编在提高代码效率的同时也成倍的增加了代码尺寸,以上述cor_h_X( )为例,它在该写后代码尺寸从660条增大到7776条(该数据由CCS剖析工具分析所得)。在工程应用中,对于有限的内存程序区,我们会适当减少程序占用的空间。合并功能相似的函数可以达到这一要求。
在LSP量化处理中,源代码中给出了2个LSP选择函数:Lsp_select_1( )和Lsp_select_2( ),而我们发现它们具有相同的功能和相似的结构,因此,在对两者的线性汇编改写中,我们只需编写一个函数(命名为Lsp_select)即可实现LSP量化处理中这两个模块的功能。
另外,在对于一些数组拷贝,数组初始化的代码,我们同样可以用此方法,编写一个函数实现,这样可以在提高执行效率的同时,减少程序占用的内存空间。
5.3 多个循环合并为一个循环
C代码改写线性汇编的时候,我们常常会发现,只要作一些调整,两个或多个循环完成的操作完全可以由一个循环来完成。以LPC子模块240点加窗语音的自相关计算Autocorr()函数为例,经过优化改写的C代码(部分)如下:
for(i=0; i<L_WINDOW; i++) //第一个循环体
y[i] = (_smpy(x[i], hamwindow[i])+0x00008000L)>>16;
sum = 1; //避免为0的情况
for(i=0; i<L_WINDOW; i++) //第二个循环体
sum = _sadd(sum,_smpy(y[i], y[i]));
这段代码包含了两个for循环,在CCS中直接编译运行并行度很差,利用线性汇编重写代码。我们发现两个循环体的循环次数均为60(L_WINDOW=60),所处理的数组不同,并且两个循环没有相关性,可以把第一和第二个循环合并成一个循环。前者的功能是对语音信号进行加窗;后者是实现乘累加(Mac)。两者合并后采用线性汇编编写,其代码如下:
mvk 60,i //设置循环次数
loop1: lddw *ham++,hamih:hamil //hamwindow[]指针
lddw *x++,xih:xil //x[]指针
smpy2 hamil,xil,yi1:yi0 //两对16位操作数相承,并行执行
smpy2 hamih,xih,yi3:yi2
sadd yi0,con0x8000,yi0
sadd yi1,con0x8000,yi1
sadd yi2,con0x8000,yi2
sadd yi3,con0x8000,yi3
packh2 yi1,yi0,yl //数据打包技术
packh2 yi3,yi2,yh
stdw yh:yl,*y++ //双字存取,提高执行效率
smpy2 yl,yl,yi1:yi0
sadd sum0,yi1,sum0
sadd sum0,yi0,sum0
smpy2 yh,yh,yi3:yi2
sadd sum0,yi3,sum0
sadd sum0,yi2,sum0
add i,-1,i
[i] b loop1 //把第一和第二个循环合成一个大循环,减少转移次数
产生的汇编代码并行流水性能大大增加,耗费的时钟周期数从1310000减少到15000,少于改编前的1/8。
6. 结束语
关于编解码器执行的时钟周期,在线性汇编改写前后,文件版本通过CCS的profiler剖析工具得知:每10帧(100MS)从159700000降至68500000,仅为原来的42%。硬件版本进行测试得:编解码的帧数提高到了88帧/s以上,鉴于编码、解码的时间比例为5:1,所以,本系统编码已经达到100帧/s,完全符合实时通信的要求。
展开全部
编译成功:
/************************
zhaoyongke
2009-5-27
13:28:30
************************/
SO BIT P1.0
CS BIT P1.1
SCK BIT P1.2
DATAH DATA 30H
DATAL DATA 31H
TDATAH DATA 32H
TDATAL DATA 33H
ORG 0000H
ajmp start
org 00a0h
start:MOV SP,#50H
LCALL MAX0;调用初始化7219函数
LOOP:LCALL DISP;调用显示函数
SJMP LOOP
MAX0:MOV A,#0BH;设置扫描位数
MOV R2,#03H ;显示4位
LCALL YW
MOV A,#0AH;设置亮度
MOV R2,#05H
LCALL YW
MOV A,#09H;设置译码格式
MOV R2,#0FFH ;设置BCD码
LCALL YW
MOV A,#0CH;设置正常工作
MOV R2,#01H
LCALL YW
RET
DISP:MOV R0,#40H;要显示的数据存储地址
MOV R1,#01H;数码管的地址寄存器01h~08h
MOV R3,#08H
LOOP1:MOV A,@R0
MOV R2,A
MOV A,R1
LCALL YW
INC R0
INC R1
DJNZ R3,LOOP1
RET
YW:LCALL SEND;调用发送函数,一位一位的从din送入7219
MOV A,R2
LCALL SEND
CLR P1.4;load 信号上升沿对其锁存
NOP
NOP
SETB P2.2;复位
RET
SEND:MOV R4,#08H;对其字节向左循环移8位
LOOP2:CLR P2.1;clk上升沿对每一位数据锁存
RLC A
MOV P2.0,C
NOP
NOP
NOP
SETB P2.1
DJNZ R4,LOOP2
RET
READT: CLR CS
CLR SCK
MOV R2,#08H
READH: MOV C,SO
RLC A
SETB SCK
NOP
CLR SCK
DJNZ R2,READH
MOV DATAH,A
MOV R2,#08H
READL: MOV C,SO
RLC A
SETB SCK
NOP
CLR SCK
DJNZ R2,READL
MOV DATAL,A
SETB CS
RET
D16T12:MOV A,DATAL
RLC A
MOV DATAL,A
MOV A,DATAH
RLC A
SWAP A
MOV B,A
MOV A,#0FH
MOV TDATAH,A
MOV A,B
ANL A,#0F0H
MOV B,A
MOV A,DATAL
ANL A,#0F0H
SWAP A
ORL A,B
MOV TDATAL,A
RET
end
/************************
zhaoyongke
2009-5-27
13:28:30
************************/
SO BIT P1.0
CS BIT P1.1
SCK BIT P1.2
DATAH DATA 30H
DATAL DATA 31H
TDATAH DATA 32H
TDATAL DATA 33H
ORG 0000H
ajmp start
org 00a0h
start:MOV SP,#50H
LCALL MAX0;调用初始化7219函数
LOOP:LCALL DISP;调用显示函数
SJMP LOOP
MAX0:MOV A,#0BH;设置扫描位数
MOV R2,#03H ;显示4位
LCALL YW
MOV A,#0AH;设置亮度
MOV R2,#05H
LCALL YW
MOV A,#09H;设置译码格式
MOV R2,#0FFH ;设置BCD码
LCALL YW
MOV A,#0CH;设置正常工作
MOV R2,#01H
LCALL YW
RET
DISP:MOV R0,#40H;要显示的数据存储地址
MOV R1,#01H;数码管的地址寄存器01h~08h
MOV R3,#08H
LOOP1:MOV A,@R0
MOV R2,A
MOV A,R1
LCALL YW
INC R0
INC R1
DJNZ R3,LOOP1
RET
YW:LCALL SEND;调用发送函数,一位一位的从din送入7219
MOV A,R2
LCALL SEND
CLR P1.4;load 信号上升沿对其锁存
NOP
NOP
SETB P2.2;复位
RET
SEND:MOV R4,#08H;对其字节向左循环移8位
LOOP2:CLR P2.1;clk上升沿对每一位数据锁存
RLC A
MOV P2.0,C
NOP
NOP
NOP
SETB P2.1
DJNZ R4,LOOP2
RET
READT: CLR CS
CLR SCK
MOV R2,#08H
READH: MOV C,SO
RLC A
SETB SCK
NOP
CLR SCK
DJNZ R2,READH
MOV DATAH,A
MOV R2,#08H
READL: MOV C,SO
RLC A
SETB SCK
NOP
CLR SCK
DJNZ R2,READL
MOV DATAL,A
SETB CS
RET
D16T12:MOV A,DATAL
RLC A
MOV DATAL,A
MOV A,DATAH
RLC A
SWAP A
MOV B,A
MOV A,#0FH
MOV TDATAH,A
MOV A,B
ANL A,#0F0H
MOV B,A
MOV A,DATAL
ANL A,#0F0H
SWAP A
ORL A,B
MOV TDATAL,A
RET
end
参考资料: http://league.ysu.edu.cn/wuxie/
本回答被提问者采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询