|
以前一直用51的,后来由于需要用上AVR M8,最近一直为一件事困惑:编写的程序感觉比用51实现同样功能,所用程序空间要大。后来通过GCC编译出来的汇编看,其SRAM都用LDS等4字节指令,这样变量多时就要比2字节指令占用更多的程序空间。但AVR手册中说0x60-0xff的SRAM可用LD等指令,这个应该是2字节的啊。为什么GCC不会选择2字节的指令,这样可以省出很多空间来。
查了一些资料,可用register .... ams("r4") 这样的方法将一些变量整到通用寄存器,曾试过,一个UINT变量,程序中用两次,用这个方法限定后省了30BYTES的空间,相当可观。 但通用寄存器比较有限,几十个或近百个数组变量,就无法这样做了。但是0x60-0xff,有160个,对于一般程序将变量放到这边,再用如LD类的2字节指令,可省很多空间的。GCC默认都是使用LDS等4字节指令,有什么办法让GCC在这个范围内使用2字节指令?
同时问下,M88内部0x60-0xff已经是扩展IO寄存器了,但有很多是未用的,是否可以作为SRAM来用,GCC在编译M88时变量自动从0x100开始了。想把M88的变量定义到0x60-0xff的未用单元,再用2字节指令,这样可以省很多空间。望知情者不吝赐教!在此先谢过。
==================================================================================================
以上是一个网友的问题和疑惑,有一定的代表性,涉及到AVR本身的架构和内存空间等概念.因此我给出一些相关的解释如下:
说来话长了,让我慢慢道来.(个人观点,仅供参考)
1.首先要明确一个概念.51的指令是CSIC(复杂指令),AVR是RISC(精简指令).其有个区别是,CSIC的指令是不定长的,有1/2/3/4个字节的指令,而RISC是定长的,每个指令的长度相同.对于AVR讲,其大部分的指令是1个字(2字节),还有些指令是双字(4个字节).他们各自的优点和缺点这里不讨论,但要明白,所谓的"精简指令"不是指生成的代码短了,而是将一些去掉(精简)了一些复杂功能的指令,使指令系统比较简单.这样方便了内部的取指令操作,指令译码,使得单周期执行一条指令成为可能,另外提高了取指令的可靠性,同时速度也提高了.
2.那么如何看待生成代码的长短呢?这个不能拿采用某一条指令做对比的方法.因为AVR的一条指令最少是2字节,而51有单字节的指令.如果是实现一个简单的操作,51可能就是1个字节,而AVR就需要2个字节了.因此最好的比较是对比一组能完成特定算法或功能的指令段,而其指标也不是只看整个代码的长度,而是一个综合的评价.如在相同的系统时钟情况下,完成该算法的时间,以及代码长度等.下面是ATMAL公司早年在推广AVR时的给出的一个比较例子,尽管不一定全面,但可以作为参考.
***对几种常见单片机的一个简单性能对比实验
用高级语言C编写一段选取最大值的代码,从几种市场上常见单片机的运行情况对它们的性能做一个简单的比较。
/*一个小的C函数
/* Return the maximum value of a table of 16 integers */
int max(int *array)
{
char a;
int maximum=-32768;
for (a=0;a<16;a++)
if (array[a]>maximum)
maximum=array[a];
return (maximum);
}
AVR 汇编输出: Code Size: 46 Bytes, Execution time: 335 cycles
C51 汇编输出: Code Size: 112 Bytes, Execution time: 9384 cycles
HC11 汇编输出: Code Size: 57 Bytes, Execution time: 5244 cycles
PIC16C74 汇编输出: Code Size: 87 Bytes, Execution time: 2492 cycles
运行条件: (系统时钟频率)
AT90S8515 8MHz
80C51 24MHz
68HC11A8 12MHz
PIC16C74 20MHz
比较结果:
Code Size(Bytes) Function ExecutionTime(μS) Current Consumption(mA) Executions /S/Mw
AT90S8515(AVR) 46(1) 42(1) 11(1) 434(1)
80C51 112(2.4) 391(9) 16(1.5) 32(0.07)
68HC11 57(1.2) 437(10) 27(2.5) 17(0.04)
PIC16C74 87(1.9) 125(3) 13.5(1.2) 119(0.27)
比较分析:
8MHz AVR等于224MHz 80C51。
68HC11:代码效率高,但处理能力只有AVR的1/10,功耗高2.5倍。
PIC速度也比较快,但是在相同功耗下,AVR性能比其高3.5倍。
3.其实,对于一些简单的小型系统,用51实现生成代码是比AVR少,因为简单的小系统使用的指令多是最常用的,计算也不多,对于51讲,通常是1/2字节的指令,而AVR则肯定是2字节,还有4字节指令,如绝对转移指令等.因此,如果对指令代码长短非常敏感的话,设计小的比较简单的系统可能使用汇编最好,但前提条件是必须对AVR的汇编熟悉,否则或许还不如采用C.
4.现在看看AVR的内存结构.早期的AVR内存空间由三部分构成:前32个寄存器组,64个I/O寄存器,后面是RAM.对于32个寄存器和64个I/O寄存器,都有专用的大量的操作指令,均为1个字的长度.而AVR将他们整合在一个RAM空间中,也使得可以使用对RAM操作的指令对前96个寄存器操作,但这时的指令长度是2个字.这样的处理是方便C编译器,使得硬件结构更加贴近C语言的操作.后来,由于AVR内部的功能和接口越来越多和复杂,64个I/O寄存器不够用了,所以出现了某2个I/O寄存器对应一个地址的情况和对I/O空间进行扩展.扩展I/O寄存器空间就是把I/O寄存器映射到RAM的前面.可是问题来了,由于指令系统已经不能改动了,所以对于扩展的I/O空间的操作只能采用对RAM操作的指令(2个字).
The ATmega48/88/168 is a complex microcontroller with more peripheral units than can be supported within the 64 locations reserved in the Opcode for the IN and OUT instructions. For the Extended I/O space from 0x60 - 0xFF in SRAM, only the ST/STS/STD and LD/LDS/LDD instructions can be used.
5.再后来,AVR发现由于扩展的I/O空间对于不同的芯片,其大小不同,因此RAM开始的地址也不同,比较麻烦,所以发展到现在,AVR索性就把扩展I/O空间定义到0X00FFH,即前32个为寄存器组,后64个为I/O空间,再后面的160全部是I/O扩展空间.RAM统一从0100H开始.这样做的另外一个优点是,I/O寄存器的地址比较统一了,方便了不同AVR芯片的代码移植.从最新推出的芯片可以看到这样的变化.
6.实际上,并不是所有的160个I/O空间都被使用了,有些功能比较简单的芯片,其扩展空间是用不掉的,这些不使用的I/O空间就作为备用,实际上内部即没有寄存器,也没有RAM,是不能使用的.因此你从M48的I/O寄存器分配表中可以发现后面大批的地址是保留的.所谓保留,就是在本芯片中不使用.
7.最后如何优化代码空间呢?当然如果你的汇编能力强的话,采用汇编.但这实际是又走回老路了,是没有办法的办法了.因此可行的办法有:a/采用好的编译器,如IAR.b/将在系统中频繁操作的变量(或小的数组)定义成全局变量,分配在32个工作寄存器中.c/采用合适的优化选项,如采用代码优先而不是速度优先的优化方案.d/程序设计本身的优化,如尽量使用字节变量等短的数据类型等,这就是体现个人的水平了.
有许多人认为,会51就会AVR.我个人是不同意这种观点的.我曾经做这样的比喻,51相当与普通的桑车,而AVR是F1赛车.会开普桑,或许也能驾驶F1,但要把F1开到赛车场上,真正发挥它性能还是不行的.因为许多会51的工程师,其掌握的基础、理念、方法都是落后的,停止不前的,如果不认真继续的努力,是不能真正用好AVR的,更不要说32位的系统了。
还是拿开车说事。很多人习惯(或许也只有这样的水平)开自动波的车,但赛车都是手动波的。 |
|