您的位置 首页 新品

Keil C51里关于仓库指针的处理

KeilC是非常优秀的C51编译器,可能是最好的C51编译器,提供各种优化模式,对变量的优化和地址安排做得非常好。这是用C语言写代码的好处之…

Keil C是十分优异的C51编译器,或许是最好的C51编译器,供给各种优化形式,对变量的优化和地址组织做得十分好。这是用C言语写代码的优点之一,假如用汇编写,得费一大番功夫给各个变量组织内存物理地址,还得时间记住哪些地址的内存单元是现已分配了,新添加的变量就不能占用那些现已分配了的单元,避免产生内存交叠抵触和溢出。我一向十分信任Keil C51的编译成果,在我的形象里,它对内存的分配是完美的,只需代码用它编译时没有陈述任何warning和error,代码运转时不或许内存抵触或溢出的现象。
但,今日产生的作业证明我错了。
手头上有个产品的代码,代码量很大。程序跑起来的作用不大好,因而计划把代码优化一下。代码量越大,通常可优化的当地也越多。对8051来说,拜访芯片内部的data区(0~7FH)内存速度是最快的,直接拜访,一条指令就能读写,而idata区(80H~FFH)尽管仍是内存区,但因为地址分配上跟特别寄存器SFR重合,只能直接地址拜访,两条指令才干读写,速度稍慢点,而外存xdata区(0~7FFFH)有必要运用DPTR指针才干拜访,速度是最慢的。很显着,优化的准则便是尽量把频频读写的变量优先组织在data区,然后是idata区,最终才是xdata区。
当我做完变量手艺优化作业后,把编译形式设为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文件,该文件包含了很多的内存分配信息,十分具体,包含哪个变量被编译器分配到哪个内存地址,占用多少个字节,哪些变量是局部变量,能够重复运用……这个M51文件里都有具体的列表。
从列表里的变量分配地址一路看下来,都没错,边看还边惊叹编译器对变量的分配组织十分准确,但看到最终一个仓库指针的组织时,总算发现问题所在了,它是这样组织的:
TYPE BASE LENGTH RELOCATION SEGMENT NAME
———————————————————————————————-
IDATA 0080H 0034H UNIT _IDATA_GROUP_
IDATA 00B4H 0022H UNIT ?ID?IPPHONE_MAIN
IDATA 00D6H 001FH UNIT ?ID?DNS_NICRCV?IPPHONE_DNS
IDATA 00F5H 0004H UNIT ?ID?DISP
IDATA 00F9H 0001H UNIT ?STACK
这上面标有STACK的段便是仓库分配,上面的数据标明,SP仓库指针组织在F9H这个地址,仓库空间是1个字节!外表看没有溢出,但我的程序里运用了中止服务,进入中止服务时,至少需求8个字节的仓库空间(保存R0~R7寄存器)来进行维护现场,8051运用的是递增压栈的规划,仓库指针往往被组织在内存空间的后边可用部分,每压栈一个字节,SP指针往上加1,进中止服务时,至少压栈8个字节,F9H+8,超出了FFH,仓库指针不能超过FFH,也便是说仓库溢出了!本来这便是导致程序不断重启的原因,不是变量内存溢出,而是仓库溢出!
而当我把那个数组指定为xdata类型后,因为该数组不再占用idata区,所以IDATA一会儿多了16个字节的可用空间,从头编译后的M51这样组织:
IDATA 0080H 0024H UNIT _IDATA_GROUP_
IDATA 00A4H 0022H UNIT ?ID?IPPHONE_MAIN
IDATA 00C6H 001FH UNIT ?ID?DNS_NICRCV?IPPHONE_DNS
IDATA 00E5H 0004H UNIT ?ID?DISP
IDATA 00E9H 0001H UNIT ?STACK
从这组数据来看,SP指针组织到在E9H这个地址,仓库空间有FFH-E9H+1=23个字节,关于程序来说现已够用,因而程序运转正常。
屡次调整变量类型的编译成果标明,C51关于仓库空间需求巨细不作核算,任何代码都只是按仓库空间只需1个字节需求来分配(在我眼里看来这显着是蛮干,稍复杂点的子程序调用都不或许只需1个字节就能完结现场维护),因为仓库只能分配在data区和idata区,因而当一个程序为了优化而data区占用太多时,尽管编译器能编译成功,但往往SP仓库指针被分配在data区的最终面,很简单形成仓库空间不行而溢出。为稳妥起见,最好保证编译后的SP值组织在F0H之前,那样至少有16个字节的仓库空间,才干最大极限保证程序不会跑飞。
看样子不能太信任Keil C51,今后编译完后,还得检查一下M51才干保证程序的质量,不知道这个是不是Keil C51的bug。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/xinpin/268349.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部