<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > Keil C51里關(guān)于堆棧指針的處理

Keil C51里關(guān)于堆棧指針的處理

作者: 時(shí)間:2016-11-17 來(lái)源:網(wǎng)絡(luò ) 收藏
Keil C是非常優(yōu)秀的C51編譯器,可能是最好的C51編譯器,提供各種優(yōu)化模式,對變量的優(yōu)化和地址安排做得非常好。這是用C語(yǔ)言寫(xiě)代碼的好處之一,如果用匯編寫(xiě),得費一大番功夫給各個(gè)變量安排內存物理地址,還得時(shí)刻記住哪些地址的內存單元是已經(jīng)分配了,新增加的變量就不能占用那些已經(jīng)分配了的單元,以免產(chǎn)生內存交疊沖突和溢出。我一直非常信賴(lài)Keil C51的編譯結果,在我的印象里,它對內存的分配是完美的,只要代碼用它編譯時(shí)沒(méi)有報告任何warning和error,代碼運行時(shí)不可能內存沖突或溢出的現象。
但,今天發(fā)生的事情證明我錯了。
手頭上有個(gè)產(chǎn)品的代碼,代碼量很大。程序跑起來(lái)的效果不大好,因此打算把代碼優(yōu)化一下。代碼量越大,通??蓛?yōu)化的地方也越多。對8051來(lái)說(shuō),訪(fǎng)問(wèn)芯片內部的data區(0~7FH)內存速度是最快的,直接訪(fǎng)問(wèn),一條指令就能讀寫(xiě),而idata區(80H~FFH)雖然還是內存區,但由于地址分配上跟特殊寄存器SFR重合,只能間接地址訪(fǎng)問(wèn),兩條指令才能讀寫(xiě),速度稍慢點(diǎn),而外存xdata區(0~7FFFH)必須使用DPTR指針才能訪(fǎng)問(wèn),速度是最慢的。很明顯,優(yōu)化的原則就是盡量把頻繁讀寫(xiě)的變量?jì)?yōu)先安排在data區,然后是idata區,最后才是xdata區。
當我做完變量手工優(yōu)化工作后,把編譯模式設為SMALL,這樣C51編譯器會(huì )自動(dòng)把那些我沒(méi)手工指定存放區的變量?jì)?yōu)先安排進(jìn)data區,如果超出有效地址范圍,它會(huì )報錯,因此我大可以放心。按下rebuild all按鈕后,編譯器提示:
Program Size: data=236.2 xdata=19321 code=43372
"ipphone_main" - 0 Error(s), 0Warning(s).
編譯器提示的data區包括了idata在內,按以往的經(jīng)驗來(lái)看,data區有256個(gè)byte,程序才使用了236.2個(gè),還剩下19個(gè),沒(méi)有溢出,而xdata有32k,現在才使用了19k,遠沒(méi)有溢出,編譯結果一切很正常。
把代碼燒錄進(jìn)芯片跑起來(lái)后,結果出人意料,從現象來(lái)看,上電約1秒后就自動(dòng)重啟,重啟后過(guò)1秒又重啟,非常有規律的重啟。
我沒(méi)有懷疑是編譯器的原因,當時(shí)第一念頭是懷疑是看門(mén)狗,代碼里上電后就打開(kāi)了看門(mén)狗,可能某些子程序代碼執行時(shí)間過(guò)長(cháng),看門(mén)狗復位了,于是在有懷疑的地方插入了喂狗代碼,重新編譯后再測試,依然自動(dòng)重啟。于是干脆就把看門(mén)狗的代碼注釋了,不使用看門(mén)狗,以為這回沒(méi)問(wèn)題了吧,結果出人意料,還是重啟。
我仔細想了一下,能造成8051的重啟的原因不多,一是看門(mén)狗引起的重啟,這點(diǎn)可以排除;二是某些8051支持重啟指令,我手頭上用的這款雖然支持,但我沒(méi)用過(guò)那指令,這點(diǎn)也可以排除;三是8051被強干擾,把取指寄存器PC的內容改變了,改成0,于是就重啟了,這點(diǎn)也可以排除,因為如果現場(chǎng)有強干擾,沒(méi)優(yōu)化前也會(huì )重啟才對。
由于沒(méi)想出來(lái)是什么原因,于是開(kāi)始折騰,把優(yōu)化的變量一個(gè)個(gè)恢復成未恢復優(yōu)化的狀態(tài),每恢復一步就重新測試一次。終于在恢復一個(gè)16字節的數組時(shí)發(fā)現程序正常了,仔細看了一下,那數組定義在xdata區的時(shí)候程序就完全正常,而定義在idata區的時(shí)候程序就復位了,雖然奇怪的是,定義在idata區時(shí),編譯器并沒(méi)有報告內存溢出。跟蹤匯編指令也沒(méi)發(fā)現異常,無(wú)論定義在idata還是xdata,編譯器為該數組分配的地址證明確實(shí)都是有效地址,確實(shí)沒(méi)有溢出,編譯器的安排還是正確。
雖然還沒(méi)找到根源,但問(wèn)題既然是出現在內存上,我于是決定查看當那個(gè)數組指定為idata類(lèi)型時(shí)的內存分配。Keil C51在編譯時(shí)會(huì )輸出一個(gè)M51文件,該文件包含了大量的內存分配信息,非常詳細,包括哪個(gè)變量被編譯器分配到哪個(gè)內存地址,占用多少個(gè)字節,哪些變量是局部變量,可以重復利用……這個(gè)M51文件里都有詳細的列表。
從列表里的變量分配地址一路看下來(lái),都沒(méi)錯,邊看還邊驚嘆編譯器對變量的分配安排非常精確,但看到最后一個(gè)堆棧指針的安排時(shí),終于發(fā)現問(wèn)題所在了,它是這樣安排的:
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這個(gè)地址,堆??臻g是1個(gè)字節!表面看沒(méi)有溢出,但我的程序里使用了中斷服務(wù),進(jìn)入中斷服務(wù)時(shí),至少需要8個(gè)字節的堆??臻g(保存R0~R7寄存器)來(lái)進(jìn)行保護現場(chǎng),8051使用的是遞增壓棧的設計,堆棧指針往往被安排在內存空間的后面可用部分,每壓棧一個(gè)字節,SP指針往上加1,進(jìn)中斷服務(wù)時(shí),至少壓棧8個(gè)字節,F9H+8,超出了FFH,堆棧指針不能超過(guò)FFH,也就是說(shuō)堆棧溢出了!原來(lái)這就是導致程序不斷重啟的原因,不是變量?jì)却嬉绯?,而是堆棧溢出?br />而當我把那個(gè)數組指定為xdata類(lèi)型后,由于該數組不再占用idata區,于是IDATA一下子多了16個(gè)字節的可用空間,重新編譯后的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
從這組數據來(lái)看,SP指針安排到在E9H這個(gè)地址,堆??臻g有FFH-E9H+1=23個(gè)字節,對于程序來(lái)說(shuō)已經(jīng)夠用,因此程序運行正常。
多次調整變量類(lèi)型的編譯結果表明,C51對于堆??臻g需求大小不作計算,任何代碼都只是按堆??臻g只有1個(gè)字節需求來(lái)分配(在我眼里看來(lái)這明顯是胡來(lái),稍復雜點(diǎn)的子程序調用都不可能只要1個(gè)字節就能完成現場(chǎng)保護),由于堆棧只能分配在data區和idata區,因此當一個(gè)程序為了優(yōu)化而data區占用太多時(shí),雖然編譯器能編譯成功,但往往SP堆棧指針被分配在data區的最后面,很容易造成堆??臻g不夠而溢出。為保險起見(jiàn),最好保證編譯后的SP值安排在F0H之前,那樣至少有16個(gè)字節的堆??臻g,才能最大限度保證程序不會(huì )跑飛。
看樣子不能太相信Keil C51,以后編譯完后,還得查看一下M51才能確保程序的質(zhì)量,不知道這個(gè)算不算Keil C51的bug。


關(guān)鍵詞: KeilC51堆棧指

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>