微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > keilc51中如何看堆栈的分配情况

keilc51中如何看堆栈的分配情况

时间:11-29 来源:互联网 点击:
Keil C是非常优秀的C51编译器,可能是最好的C51编译器,提供各种优化模式,对变量的优化和地址安排做得非常好。这是用C语言写代码的好处之一,如果用汇编写,得费一大番功夫给各个变量安排内存物理地址,还得时刻记住哪些地址的内存单元是已经分配了,新增加的变量就不能占用那些已经分配了的单元,以免产生内存交叠冲突和溢出。我一直非常信赖Keil C51的编译结果,在我的印象里,它对内存的分配是完美的,只要代码用它编译时没有报告任何warning和error,代码运行时不可能内存冲突或溢出的现象。

但,今天发生的事情证明我错了。

手头上有个产品的代码,代码量很大。程序跑起来的效果不大好,因此打算把代码优化一下。代码量越大,通常可优化的地方也越多。对8051来说,访问芯片内部的data区(0~7FH)内存速度是最快的,直接访问,一条指令就能读写,而idata区(80H~FFH)虽然还是内存区,但由于地址分配上跟特殊寄存器SFR重合,只能间接地址访问,两条指令才能读写,速度稍慢点,而外存xdata区(0~7FFFH)必须使用DPTR指针才能访问,速度是最慢的。很明显,优化的原则就是尽量把频繁读写的变量优先安排在data区,然后是idata区,最后才是xdata区。




8051在物理上有4个存储器空间

片内ROM和片外ROM。片内RAM和片外RAM。

片外程序存储器ROM地址空间为64kB,片外数据存储器RAM也有64kB的寻址区,在地址上是与ROM重迭的。

8051单片机通过不同信号来选通ROM或RAM。当从外部ROM中取指令时,采用选通信号PSEN,而从外部RAM中读写数据时

则采用读RD和写WR信号或来选通,因此不会因地址重迭而发生混乱。

片内数据存储器RAM

片内RAM有256个字节,其中00H~7FH地址空间是直接寻址区,该区域内从00H~1FH地址为工作寄存器区,

安排了4组工作寄存器,每组都为R0~R7,在某一时刻,CPU只能使用其中任意一组工作寄存器,由程序状态字

PSW中RS0和RS1的状态决定。

片内RAM的20H~2FH地址单元为位寻址区,其中每个字节的每一位都规定了位地址。每个地址单元除了可进行字

节操作之外,还可进行位操作。

片内RAM的80H~FFH地址空间是特殊功能寄存器SFR区,对于51子系列在该区域内安排了21个特殊功能寄存器,对于52子系列

则在该区域内安排了26个特殊功能寄器,同时扩展了128个字节的间接寻址片内RAM,地址也为80~FFH,与SFR区地址重迭.

当我做完变量手工优化工作后,把编译模式设为SMALL,这样C51编译器会自动把那些我没手工指定存放区的变量优先安排进data区,如果超出有效地址范围,它会报错,因此我大可以放心。按下rebuild all按钮后,编译器提示:

Program Size: data=236.2 xdata=19321 code=43372

"ipphone_main" - 0 Error(s), 0Warning(s).

编译器提示的data区包括了idata在内,按以往的经验来看,data区有256个byte,程序才使用了236.2个,还剩下19个,没有溢出,而xdata有32k,现在才使用了19k,远没有溢出,编译结果一切很正常。

把代码烧录进芯片跑起来后,结果出人意料,从现象来看,上电约1秒后就自动重启,重启后过1秒又重启,非常有规律的重启。

我没有怀疑是编译器的原因,当时第一念头是怀疑是看门狗,代码里上电后就打开了看门狗,可能某些子程序代码执行时间过长,看门狗复位了,于是在有怀疑的地方插入了喂狗代码,重新编译后再测试,依然自动重启。于是干脆就把看门狗的代码注释了,不使用看门狗,以为这回没问题了吧,结果出人意料,还是重启。

我仔细想了一下,能造成8051的重启的原因不多,一是看门狗引起的重启,这点可以排除;二是某些8051支持重启指令,我手头上用的这款虽然支持,但我没用过那指令,这点也可以排除;三是8051被强干扰,把取指寄存器PC的内容改变了,改成0,于是就重启了,这点也可以排除,因为如果现场有强干扰,没优化前也会重启才对。

由于没想出来是什么原因,于是开始折腾,把优化的变量一个个恢复成未恢复优化的状态,每恢复一步就重新测试一次。终于在恢复一个16字节的数组时发现程序正常了,仔细看了一下,那数组定义在xdata区的时候程序就完全正常,而定义在idata区的时候程序就复位了,虽然奇怪的是,定义在idata区时,编译器并没有报告内存溢出。跟踪汇编指令也没发现异常,无论定义在idata还是xdata,编译器为该数组分配的地址证明确实都是有效地址,确实没有溢出,编译器的安排还是正确。

虽然还没找到根源,但问题既然是出现在内存上,我于是决定查看当那个数组指定为idata类型时的内存分配。Keil C51在编译时会输出一个M51文件,

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top