1. 程式人生 > >keil優化等級設定說明

keil優化等級設定說明

優化級別說明(僅供參考):
則其中的 Code Optimization    欄就是用來設定C51的優化級別。共有9個優化級別(書上這麼寫的),高優化級別中包含了前面所有的優化級別。現將各個級別說明如下:

0級優化:
1、 常數摺疊:只要有可能,編譯器就執行將表示式化為常數數字的計算,其中包括執行地址的計算。
2、 簡單訪問優化:對8051系統的內部資料和位地址進行訪問優化。
3、 跳轉優化:編譯器總是將跳轉延至最終目標上,因此跳轉到跳轉之間的命令被刪除。
1級優化:
1、 死碼消除:無用的程式碼段被消除。
2、 跳轉否決:根據一個測試回溯,條件跳轉被仔細檢查,以決定是否能夠簡化或刪除。
2級優化:
1、 資料覆蓋:適於靜態覆蓋的資料和位段被鑑別並標記出來。連線定位器BL51通過對全域性資料流的分析,選擇可靜態覆蓋的段。

3級優化:
1、“窺孔”優化:將冗餘的MOV命令去掉,包括不必要的從儲存器裝入物件及裝入常數的操作。另外如果能節省儲存空間或者程式執行時間,複雜操作將由簡單操作所代替。
4級優化:
1、 暫存器變數:使自動變數和函式引數儘可能位於工作暫存器中,只要有可能,將不為這些變數保留資料儲存器空間。
2、擴充套件訪問優化:來自IDATA、XDATA、PDATA和CODE區域的變數直接包含在操作之中,因此大多數時候沒有必要將其裝入中間暫存器。
3、區域性公共子式消除:如果表示式中有一個重複執行的計算,第一次計算的結果被儲存,只要有可能,將被用作後續的計算,因此可從程式碼中消除繁雜的計算。
4、 CASE/SWITCH語句優化:將CASE/SWITCH語句作為跳轉表或跳轉串優化。

5級優化:
1、 全域性公共子式消除:只要有可能,函式內部相同的子表示式只計算一次。中間結果存入一個暫存器以代替新的計算。
2、 簡單迴圈優化:以常量佔據一段記憶體的迴圈再執行時被優化。
6級優化:
1、 迴路迴圈:如果程式程式碼能更快更有效地執行,程式迴路將進行迴圈。
7級優化:
1、 擴充套件入口優化:在適合時對暫存器變數使用DPTR資料指標,指標和陣列訪問被優化以減小程式程式碼和提高執行速度。
8級優化:
1、 公共尾部合併:對同一個函式有多處呼叫時,一些設定程式碼可被重複使用,從而減小程式程式碼長度。
9級優化:
1、 公共子程式塊:檢測重複使用的指令序列,並將它們轉換為子程式。C51甚至會重新安排程式碼以獲得更多的重複使用指令序列。

當然,優化級別並非越高越好,應該根據具體要求適當選擇。 




Keil C51匯流排外設操作問題的深入分析
    閱讀了《微控制器與嵌入式系統應用》2005年第10期雜誌《經驗交流》欄目的一篇文章《Keil C51對同一埠的連續讀取方法》(原文)後,筆者認為該文並未就此問題進行深入準確的分析 文章中提到的兩種解決方法並不直接和簡單。筆者認為這並非是Keil C51中不能處理對一個埠進行連續讀寫的問題,而是對Kei1 C51的使用不夠熟悉和設計不夠細緻的問題,因此特撰寫本文。
    本文中對原文提到的問題,提出了三種不同於原文的解決方法。每種方法都比原文中提到的方法更直接和簡單,設計也更規範。(無意批評,請原文作者見諒)

1 問題回顧和分析
    原文中提到:在實際工作中遇到對同一埠反覆連續讀取,Keil C51編譯並未達到預期的結果。原文作者對C編譯出來的彙編程式進行分析發現,對同一埠的第二次讀取語句並未被編譯。但可惜原文作者並未分析沒有被編譯的原因,而是匆忙地採用一些不太規範的方法試驗出了兩種解決辦法。
    對此問題,翻閱Keil C51的手冊很容易發現:KeilC51的編譯器有一個優化設定,不同的優化設定,會產生不同的編譯結果。一般情況預設編譯優化設定被設定為8級優化,實際最高可設定為9級優化:

1. Dead code elimination。
2.Data overlaying。
3.Peephole optimization。
4.Register variables。
5.Common subexpression elimination。
6.Loop rotation。
7.Extended Index Access Optimizing。
8.Reuse Common Entry Code。
9.Common Block Subroutines。
    而以上的問題,正是由於Keil C51編譯優化產生的。因為在原文程式中將外設地址直接按如下定義:
unsigned char xdata MAX197 _at_ 0x8000
    採用_at_將變數MAX197定義到外部擴充套件RAM 指定地址0x8000。因此,Keil C51優化編譯理所當然認為重複讀第二次是沒有用的,直接用第一次讀取的結果就可以了,因此編譯器跳過了第二條讀取語句。至此,問題就一目瞭然了。

2 解決方法
由以上分析很容易就能提出很好的解決辦法。
2.1 最簡單最直接的辦法
    程式一點都不用修改,將Keil C51的編譯優化選擇設定為0(不優化)就可以了。選擇project視窗的Target,然後開啟“Options for Target”設定對話方塊,選擇“C51”選項卡,將“Code Optimiztaion”中的“Level”選擇為“0:Costant folding”。再次編譯後,大家會發現編譯結果為:
CLR MAXHBEN
MOV DPTR,#MAX197
MOVX A,@DPTR
MOV R7,A
MOV down8,R7
SETB MAXHBEN
MOV DPTR,#MAX197
MOVX A,@DPTR
MOV R7,A
MOV up4,R7
兩次讀取操作都被編譯出來了。

2.2 最好的方法
    告訴Keil C51,這個地址不是一般的擴充套件RAM,而是連線的裝置,具有“揮發”特性,每次讀取都是有意義的。可以修改變數定義,增加“volatile”關鍵字說明其特徵:
unsigned char volatile xdata MAX197 _at_ 0x8000;
    也可以在程式中包含系統標頭檔案;“#include<absacc.h>”,然後在程式中修改變數,定義為直接地址:
#define MAX197 XBYTE
    這樣,Keil C51的設定仍然可以保留高階優化,且編譯結果中,同樣兩次讀取並不會被優化跳過。

2 3 硬體解決方法
    原文中將MAX197的資料直接連線到資料匯流排,而對地址匯流排並未使用,採用一根埠線選擇操作高低位元組。很簡單的修改方法就是使用一根地址線選擇操作高低位元組即可。比如:將P2.0(A8)連線到原來P1.0連線的HBEN腳(MAX197的5腳).在程式中分別定義高低位元組的操作地址:
unsigned char volatile xdata MAX197_L _at_ 0x8000;
unsigned char volatile xdata MAX197_H _at_ 0x8100;
將原來的程式:
MAXHBEN =0;
down8=MAX197;//讀取低8位
MAXHBEN =1;
up4=MAX197;//讀取高4位
改為以下兩句即可
down8= MAX197_L;//讀取低8位
up4=MAX197_H;//讀取高4位

3 小結
    Keil C51經過長期考驗和改進以及大量開發人員的實際使用,已經克服了絕大多數的問題,並且其編譯效率也非常高。對於一般的使用.很難再發現什麼問題。筆者曾經粗略研究過一下Keil C51優化編洋的結果.非常佩服Keil C51設計者的智慧,一些C程式編譯產生的彙編程式碼.甚至比一般程式設計師直接用匯編編寫的程式碼還要優秀和簡練 通過研讀Kell C51編譯產生的彙編程式碼.對提高組合語言編寫程式的水平都是很有幫助的。
    由本文中的問題可以看出:在設計中遇到問題時.一定不要被表面現象矇蔽,不要急於解決,應該認真分析,找出問題的原因.這樣才能從根本上徹底解決問題。

附表:Keil C51中的優化級別及優化作用 級別         說明
0         常數合併:編譯器預先計算結果,儘可能用常數代替表示式。包括執行地址計算。
優化簡單訪問:編譯器優化訪問8051系統的內部資料和位地址。
跳轉優化:編譯器總是擴充套件跳轉到最終目標,多級跳轉指令被刪除。
1         死程式碼刪除:沒用的程式碼段被刪除。
拒絕跳轉:嚴密的檢查條件跳轉,以確定是否可以倒置測試邏輯來改進或刪除。
2         資料覆蓋:適合靜態覆蓋的資料和位段被確定,並內部標識。BL51連線/定位器可以通過全域性資料流分析,選擇可被覆蓋的段。
3         窺孔優化:清除多餘的MOV指令。這包括不必要的從儲存區載入和常數載入操作。當儲存空間或執行時間可節省時,用簡單操作代替複雜操作。
4         暫存器變數:如有可能,自動變數和函式引數分配到暫存器上。為這些變數保留的儲存區就省略了。
優化擴充套件訪問:IDATA、XDATA、PDATA和CODE的變數直接包含在操作中。在多數時間沒必要使用中間暫存器。
區域性公共子表示式刪除:如果用一個表示式重複進行相同的計算,則儲存第一次計算結果,後面有可能就用這結果。多餘的計算就被刪除。
Case/Switch優化:包含SWITCH和CASE的程式碼優化為跳轉表或跳轉佇列。
5         全域性公共子表示式刪除:一個函式內相同的子表示式有可能就只計算一次。中間結果儲存在暫存器中,在一個新的計算中使用。
簡單迴圈優化:用一個常數填充儲存區的迴圈程式被修改和優化。
6         迴圈優化:如果結果程式程式碼更快和有效則程式對迴圈進行優化。
7         擴充套件索引訪問優化:適當時對暫存器變數用DPTR。對指標和陣列訪問進行執行速度和程式碼大小優化。
8         公共尾部合併:當一個函式有多個呼叫,一些設定程式碼可以複用,因此減少程式大小。
9         公共塊子程式:檢測迴圈指令序列,並轉換成子程式。Cx51甚至重排程式碼以得到更大的迴圈序列。




優化論

談到優化,其實很多人都哭笑不得,因為在一個C51軟體工程師的生涯中,總要被KEIL的優化耍那麼一次到幾次。我被耍過,想必看著文章的你也被耍過,如果你回答說不,那隻能說你寫的C51程式不多!
看看KEILC的優化級別選項吧:


0-9共10個級別的優化,0是最低,9最高,一個普通的程式,設定最高級別和最低級別,編譯後代碼量有時會相差很遠,以DX板DEMO程式為例,0級優化後是14K的CODE,9級優化後是10K的CODE,前後相差了4K。可見這個差別是多麼的大。
事實上我們不需要知道對應的各個級別KEIL會如何優化你的程式或優化了些什麼,我們只需要以一種嚴謹的態度去編寫和對待你的程式就可以了。在我個人的觀念中,程式在9級優化後依然能保持完美無誤的執行,你才算瞭解KEIL的脾氣。
好了,還是說點正點的:
有些人習慣整體程式都選擇同一個優化級,事實上每個C檔案都可以有獨立的優化級別的:

在工作區右鍵選擇你的模組(.C)然後選取Options for File xxx就會出現如下介面:


在C51選項中就可以選擇優化級別和警告級別等東西了,被獨立設定過的C檔案會有特殊的標記的:


用以提醒你這個檔案的編譯處理並非預設設定!
如果你覺得模組優化都不夠細的話,你可以考慮區域性優化,也就是說對某個函式實行某個級別的優化。當你發現9級優化的時候某個函式總是變的不正常,但你又希望其它函式和程式段保持最高的簡潔度,那麼區域性優化可以說是相當有用的了。在KEIL手冊中有介紹這個功能:
#pragma OPTIMIZE(x) x就是你希望的優化級別,一般應用如下:

#pragma OPTIMIZE(6)
void FunA()
{
}
......
......

#pragma OPTIMIZE(9)
void FunB()
{
}

上面的意思就是說,在void FunA()到void FunB()之前的所有函式,包括FunA在內,都採用6級的優化,而從FunB開始直到之後,只要沒碰上#pragma OPTIMIZE,都採用9級優化了。
OPTIMIZE還可以多一個引數,就是speed和size,
用法: #pragma OPTIMIZE(9,speed)或#pragma OPTIMIZE(5,size)
對應的就是9級優化,以速度為主,或5級優化,以空間最小為主。

4.StartUp.a51
在之前第一節的建立工程中就曾經提到過StartUp.a51這個東西了,就是在工程初建的時候有個對話方塊用於選擇是否為工程新增這個a51檔案。


其實這個檔案給大家最最深刻的感覺就是:開機清空RAM。事實上它還有其他特別的用途的,例如初始化堆疊(很多人不知道KEILC一開始把堆疊設定為多少,事實上可以通過軟體模擬的時候從這個檔案找到答案),然後是再入函式的虛擬堆疊的設定,還有更高階一點的,BANK的初始化。
舊版本KEIL自動為每個工程預設新增相同的StartUp檔案,後期的KEIL就有了上圖的選擇,如果選擇新增,則會為每個工程新增一個獨立的StartUp。使用者可以通過手工改寫StartUp.a51實現某些必要的上電初始化。例如最通常的:取消微控制器開機清RAM功能!!
關於STARTUP的介紹,我建議大家看看以下的文章,它的解釋非常詳盡。

##################################

在實際使用時發現模擬時有寫程式是白色的無法進行斷點設定

搜尋到的答案是優化等級過高,一些普通的程式被優化。

只得把優化程式等級降低。