蘇延川
摘 要:為了使DSP程序同時具備可讀性好和效率高的優(yōu)點,提出一種采用C/C++和匯編語言混合編程的方法。該方法兼顧了兩種編程語言的優(yōu)點,已成為嵌入式 DSP系統(tǒng)的重要編程方法。現以ADSP-TS101的嵌入式系統(tǒng)為基礎,介紹混合編程的方法和函數調用的規(guī)則,然后闡述程序優(yōu)化的幾種方法和總體策略,最后給出一個混合編程的實例。采用這種方法編寫的程序能夠在保持高級語言自身優(yōu)點的情況下,使執(zhí)行效率提高5~10倍。
關鍵詞:ADSP-TS101;C/C++;匯編語言;混合編程;程序優(yōu)化
中圖分類號:TP312文獻標識碼:A
文章編號:1004-373X(2009)20-036-04
Mixed Programming and Optimized Method in Embedded System Based on ADSP-TS101
SU Yanchuan
(China Airborne Missile Academy,Luoyang,471009,China)
Abstract:In order to make DSP program has advantages of readability and high efficiency,mixed programming method which adopts C/C++ and assembler language is proposed.This method has advantages of two kinds of programming languages and it has already become the important programming method of embedded DSP system.Embedded system based on ADSP-TS101,the mixed programming method and the rule that function calls in are introduced.Some methods of optimized program and general policy are proposed,an example of mixed programming is given.The efficiency of program which uses the programming method can be improved by 5 to 10 times.
Keywords:ADSP-TS101;C/C++;assembler language;mixed programming;program optimization
0 引 言
隨著數據處理要求(如雷達、無線通信、圖像處理)的不斷提高,以及DSP的快速發(fā)展,目前許多嵌入式系統(tǒng)都以DSP為核心來構建。嵌入式DSP系統(tǒng)的軟件設計就是為了充分利用DSP的資源,完成復雜任務算法和高速數據的實時處理,保證系統(tǒng)的可靠性,并提高工作效率。
ADSP-TS101S是ADI公司專為大信號處理任務和通信應用而推出的一款高性能DSP芯片,在嵌入式信號處理中得到廣泛應用。DSP軟件設計可以采用C/C++語言、匯編語言或者二者混合編程。匯編語言編寫的程序代碼執(zhí)行效率高,但是程序開發(fā)難度大,而且可讀性、可移植性差;C/C++編寫的程序可讀性、移植性較好,但是執(zhí)行效率太低[1-3],在某些實時性要求高的場合這種方法所產生代碼的效率無法滿足設計要求。為了充分利用兩種語言的優(yōu)勢而采用二者混合編程的方法,這種方法既可充分利用芯片內的硬件資源,又可縮短其開發(fā)時間,維護升級也很方便。因此,在很多情況下采用混合編程方法能更好地達到設計要求,二者相互結合,可極大地提高DSP任務處理的能力,并可在有限的時間內完成設計任務。
1 混合編程方法及規(guī)則
1.1 混合編程方法
匯編語言和C/C++混合編程方法[2-4]通??煞譃橐韵聝煞N:
(1)獨立編寫C/C++程序和匯編程序。分開編譯或匯編形成各自的目標模塊,再用鏈接器將各個目標模塊鏈接起來,這是一種靈活性較大的方法。通常的做法是用C/C++編寫主程序框架,用匯編語言編寫調用次數較多或者執(zhí)行時間較長的子程序,這樣既能發(fā)揮C/C++程序可讀性好的優(yōu)點,又能充分體現匯編語言執(zhí)行效率高的長處,但它必須了解程序運行時的環(huán)境,維護各匯編模塊的入口和出口代碼,計算傳遞參數在堆棧中的位置,工作量稍大,不過能滿足軟件設計結構化的要求。
(2) 在C/C++程序中直接內聯(lián)匯編語句。這種方法適用于短小或者簡單的算法。混合編程的規(guī)則[5,6]是在C/C++環(huán)境下,由TigerSHARC 定義了一套嚴格的寄存器規(guī)則,它分為三類[2,3]:
第一類是保留寄存器。有j16~j25,k16~k25,xr24~xr31,yr24~yr31共40個,要使用這些寄存器,需要將其保存在實時堆棧中,在C可調用的匯編子程序中也必須保存和恢復這些寄存器。
第二類是堆棧專用寄存器。有k26,k27和j26,j27。這些寄存器專門用于堆棧保護,在使用高級語言編程時,不需要使用這四個寄存器。
第三類是高速暫存寄存器。它包括除了以上兩類寄存器以外的所有寄存器。用法和匯編中的普通寄存器是一樣的,使用前不需要保存寄存器內容。
在默認情況下,cjmp寄存器用作存放被調用函數的返回地址,但在嵌套調用中,這個值會被修改。為了保證安全返回,一般把返回地址存放在堆棧頂部(偏移地址為0的地方)。函數調用時,有時需要參數傳遞,通常,如果參數少于5個,則通過寄存器傳遞,如表1所示。
表1 參數字與寄存器的對應關系
參數字
使用的寄存器
參數為int或者指針參數為float或者雙字
參數字1j4xr4
參數字2j5xr5
參數字3j6xr6
參數字4j7xr7
如果在C/C++ 調用函數中做了正確的函數返回聲明,則被調用的匯編函數可使用寄存器j8,xr8 和xr9 返回有效值。j8用于返回整數或地址;xr9:8可提供雙字結果返回。若返回值大于2個字長,則必須為它們分配存儲空間,令j8為返回值,指向該空間的首地址即可。
在C/C++中聲明全局變量及函數,匯編中加“_”前綴才能使用;匯編中的對象必須用“_”前綴命名,并用.global 聲明全局變量,才可在C/C++中訪問到。
1.2 函數調用規(guī)則[7]
函數調用的標準運行模式為:
(1) 調用者將參數壓入堆棧,壓入時按照反序進行,即最右邊的參數位于堆棧的頂部;
(2) 調用函數;
(3) 調用結束時,調用者將參數彈出堆棧并返回。整個過程離不開堆棧操作。
ADSP-S101的堆棧是一個先入后出存儲區(qū),用堆棧指針(j/k27) 和幀指針(j/k26) 來管理堆棧。調用函數時,編譯器在運行棧中建立一個幀以存儲信息,當前函數幀稱為局部幀。j/k26指向當前函數局部幀的開始,即棧底。j/k27指向棧頂,工作方式是向低地址變化。每調用一次函數,就建立一個新幀。C環(huán)境利用局部幀來實現如下功能:
保護函數的返回地址及相關寄存器 把函數返回地址,保存在j27+0的位置(棧頂),同時設置j26為j27-0x40(棧底),得到長度為64的棧區(qū),并在棧區(qū)內保護相關寄存器。
分配局部變量 在局部變量賦初值的時候,系統(tǒng)在堆棧內給它分配一個空間。
傳遞函數的參數 前四個參數傳遞給相應寄存器(見表1),后續(xù)參數按順序裝載到堆棧j27 + 0xC的起始空間中。注意,如果傳遞的參數是結構類型,則其所有元素都將入棧。例如第五個參數是兩元素的結構體,則元素一放于j27+0xC,元素二放于j27+0xD,在使用參數時只需從對應的位置上讀取匯編子程序即可。
C環(huán)境在調用C函數時自動管理這些操作,當匯編與C接口時,必須采用與C一樣的方式進行操作。特別需要注意的是,由于C編譯器不提供檢查堆棧溢出的任何手段,因此必須保證有足夠的空間用于堆棧,否則若發(fā)生溢出現象,將破壞程序的運行環(huán)境,從而導致程序的癱瘓。
2 軟件設計優(yōu)化策略
2.1 代碼優(yōu)
代碼優(yōu)化[8,9]包括優(yōu)化C/C++語言的執(zhí)行速度和代碼長度。現代DSP的C/C++語言編譯器可提供一定程度的代碼優(yōu)化,但是編譯器不能同時做兩種類型的優(yōu)化,只能涉及執(zhí)行速度和代碼大小的平衡。相對編譯器來說,手工改變代碼的大小比較困難。ADI公司的VisualDSP++4.5集成開發(fā)環(huán)境支持C/C++語言編譯和優(yōu)化,同時編譯器可以進行幾個不同級別的優(yōu)化。
2.1.1 優(yōu)化代碼的執(zhí)行速度
對于某些有時間限制或頻繁執(zhí)行的代碼段,執(zhí)行速度是非常重要的,可以通過手工的辦法做很多優(yōu)化,以提高這些代碼段的效率。中斷服務程序、高優(yōu)先級的任務、有實時限制的計算、計算密集型或者頻繁調用的函數都是優(yōu)化速度時的候選對象。在實際的嵌入式系統(tǒng)中,由于處理器的特點不同,所以優(yōu)化手段會有所不同。主要有算法優(yōu)化、指令優(yōu)化、數據流優(yōu)化三種[1]。
算法優(yōu)化 根據任務的具體情況,尋找最優(yōu)算法,有效提高代碼的效率,同時保證結果的正確性,如FFT和DFT的快速算法、查表法等。同時也可以根據處理器的結構特點,利用處理器的并行處理結構,實現SIMD和MIMD,甚至多DSP的并行處理,有效進行算法優(yōu)化。必須說明的是,算法的優(yōu)化和數據類型以及數據在內存空間的分布也有很大的關系。例如在圖像處理時,ADSP-TS101可以把一副圖像分為上下兩部分進行同時處理,或者幾幅圖像同時進行多片處理。
常用的指令優(yōu)化主要有用移位來實現除法運算;最好用的索引方式訪問數組;使用inline函數;使用全局變量減少函數調用參數;復雜運算模塊采用手工匯編,局部利用嵌入式匯編;減少條件判斷轉移;Switch語句中根據發(fā)生頻率來進行case排序,大的switch語句轉為嵌套switch語句;重點優(yōu)化循環(huán)體;避免使用C++的昂貴特性。
數據流優(yōu)化主要有以下幾個方面:
(1) 選用合適的數據類型;
(2) 訪問時保證從數據對齊地址開始;
(3) 將數據存儲在不同的存儲空間;
(4) 頻繁訪問的數據放在內部存儲器內;
(5) 數據的輸入/輸出及DSP間的大量數據交換,采用DMA方式進行。
2.1.2 優(yōu)化代碼的長度
優(yōu)化代碼的長度與優(yōu)化代碼的執(zhí)行速度往往是互相矛盾的。因此前面提到的優(yōu)化代碼的執(zhí)行速度方法,如果反其道而行之,則會起到優(yōu)化代碼長度的效果。除了編譯器自動優(yōu)化代碼長度外,還有一些手工的方法可減少代碼的大小:
(1) 避免使用大的標準化程序庫例程。很多庫例程由于設法處理很多可能情況,所以形成龐大代碼。避免使用大的標準庫例程,用簡潔代碼實現子功能。
(2) 使用本地字長,減少附加指令。
(3) 使用Goto語句。Goto語句的使用與優(yōu)化代碼速度相矛盾,但是可以去除復雜控制結構以及共享重復代碼。
除了以上技術,手工編寫匯編能夠最大限度地減少代碼長度。
2.2 系統(tǒng)優(yōu)化[9,10]
除了以上優(yōu)化手段,系統(tǒng)的優(yōu)化還需要保證系統(tǒng)的實時性和可靠性。
2.2.1 系統(tǒng)實時性保證
對于實時性系統(tǒng),通過優(yōu)化代碼的執(zhí)行效率來減少任務的完成時間,可以提高系統(tǒng)的實時性。但對于復雜的多DSP系統(tǒng),任務的分配和調度需要嵌入式實時操作系統(tǒng)RTOS來完成,以保證系統(tǒng)的可靠性及實時性,而開發(fā)者只需關注應用程序算法的研究。RTOS的高效任務管理、快速靈活的任務間通信、高度的可裁減性、動態(tài)的內存管理等優(yōu)點都有效提高了嵌入式系統(tǒng)的資源利用率,同時快速有效的中斷及異常時間處理能力保證了系統(tǒng)的安全性。常見的RTOS有Linux,WinCE,μCOS/Ⅱ,VxWorks等。對TS101,可用的嵌入式操作系統(tǒng)有OSE,VDK等。
另外,在多處理器系統(tǒng)中,較少通信開銷對提高系統(tǒng)的效率非常重要。
2.2.2 系統(tǒng)可靠性保證
系統(tǒng)可靠性保證是指通過軟件設計上的一些措施來實現對系統(tǒng)故障的檢測、隔離以及實時監(jiān)控系統(tǒng)遇到的異常信息,并盡可能保證在異常狀態(tài)或帶故障狀態(tài)下,系統(tǒng)能完成正常的任務,避免系統(tǒng)死機。提高系統(tǒng)的可靠性是軟件設計優(yōu)化的一個重要方面。
在實時環(huán)境下,由于外部異常導致系統(tǒng)中數據傳輸或者通信被打斷,需要重新建立通信,保證數據傳輸的暢通。算法出現的異常,如除法溢出、數值超出范圍等,需要建立相應的處理機制。例如,在總線共享結構的多DSP處理系統(tǒng)中,軟件設計上加入實時的系統(tǒng)狀態(tài)查詢操作,多片工作時當一片出現問題,主控處理器會根據具體的出錯狀態(tài)來自動將其隔離,并通知其他的處理器,重新進行任務分配,利用它們完成剩余的工作。
異常維護操作必然會使軟件運行效率下降,這就需要根據系統(tǒng)具體的運行環(huán)境進行取舍。為此它通常用于對可靠性維護的操作,以模塊子函數的形式存放在軟件相應的故障對策庫中,以便根據故障的具體情況來選擇應用中應采取的相應措施。
2.3 程序優(yōu)化的總體策略
根據不同的處理器及實際應用,嵌入式DSP系統(tǒng)的優(yōu)化情況有所不同。同時應當指出,編譯器的優(yōu)化是速度優(yōu)化與代碼長度優(yōu)化的折中,多數情況下代碼速度優(yōu)化與長度優(yōu)化是互相矛盾的,應當根據實際情況有所取舍。在優(yōu)化系統(tǒng)的過程中,要兼顧以下幾點:
(1) 不要一味地追求程序的效率,應當在滿足正確性、可靠性、健壯性、可讀性等質量因素的前提下,設法提高程序的效率;
(2) 以提高程序的全局效率為主,提高局部效率為輔;
(3) 在優(yōu)化程序的效率時,應當先找出限制效率的“瓶頸”,不要在無關緊要之處優(yōu)化;
(4) 先優(yōu)化數據結構和算法,再優(yōu)化執(zhí)行代碼;
(5) 不要追求緊湊的代碼,因為緊湊的代碼并不能產生高效的機器碼。
同時,充分了解系統(tǒng)硬件結構,特別是處理器內部結構,與硬件工程師有效協(xié)調配合,設計出高效率代碼,同時保證系統(tǒng)的安全性及實時性。
3 混合編程在程序中的應用
下面以一個嵌入式DSP程序為例,其系統(tǒng)由兩片DSP構成,主DSP0 負責通過IRQ0接收控制臺從RS 232串口傳來的控制字、譯碼,并通過控flag3產生下降沿觸發(fā)IRQ3 中斷來啟動另一片DSP。這里用C搭建框架,匯編控制底層硬件,效率高,可讀性強。由于篇幅限制,這里略去串口初始化、串口數據接收函數以及其他芯片的處理程序。以下是系統(tǒng)管理中的混合編程:
Main.c 文件:
# include < signal.h >
# include < sysreg.h >
# include " def TS101.h"
int code[ 2 ] ;
volatile int inruptflag = 0,IRQ3flag = 0 ;
extern void INITIAL_DSP ( ) ;
extern int decode (int ( 3 p) [ ]) ;
extern void IRQ0_int () ;
# pragma interrupt //中斷服務程序標識(第2 種方法)
void IRQ3_int (void) { IRQ3flag = 1 ;}
main ()
{
int temp,r ;
INITIAL_DSP() ;// 一些初始化設置
temp = __builtin_sysreg_read (__IMASKH) ;
// 讀取IMASKH
temp | = (1 < < IN T_IRQ0_P) | (1 < < INT_IRQ3_P) ;
__builtin_sysreg_write (__IMASKH,temp ) ;
// 打開IRQ0 、IRQ3 中斷屏蔽位
interrupt (SIGIRQ0,IRQ0_int) ;
// 賦IRQ0 中斷入口地址
while(inruptflag = = 0) { } //等待接收IRQ0傳命令字
Rev8byte ( &code;) ;
// 接收串口命令字并存放于code (略)
r = decode ( &code;) ;/ / 調用asm 譯碼程序
if ( 3 (int 3 ) (r + 1) = = 2)
{// 判斷命令碼第2 個字是否為2
__builtin_sysreg_write (__IVIRQ0,(int) IRQ3_int) ;
// 第2 種方法設置中斷矢量表
__builtin_sysreg_write ( __SQCTLCL,0xf0ffffff) ;
// 拉低FLAG3,產生IRQ3 中斷
while( IRQ3flag = = 0) { } // 等中斷啟動各片DSP
…}
… }
INITIAL.asm 文件:
# include " def TS101.h"
# define mPUSHQ(arg) Q[ k27 + = - 4 ] = arg;
// 宏定義保存寄存器
# define mPOPQ(arg) k27 = k27 + 4;arg = Q[ k27 + = 0 ];// 宏定義釋放寄存器
# define mENTER j26 = j27 - 0x40 ;k26 = k27 - 0x40;
// 宏定義實現調用進入
[ j27 + = 0xFFFFFFF4 ] = cJMP ;k27 = k27 - 0x04;
# define mRETURN cjmp = [j26 + 0x40 ];
// 宏定義實現調用返回
cjmp(ABS) (NP) ;j27:24 =Q[j26 + 0x44] ;k27:24 =Q[k26 + 0x44];
.global _INITIAL_DSP,_IRQ0_int,_decode,_Rev8byte ;// 聲明為全局變量
.extern _inruptflag,_code ;// 外部變量
.section flags ;
.var dsp_code[8 ] ;
.section program ;
_INITIAL_DSP:mEN TER
xr5 = 0x08800000;sqctlst = xr5;
// 令flag3 為輸出高電平
xr5 = 0xfff6ffff;sqctlcl = xr5;
// 設置IRQ3、IRQ0 為邊沿觸發(fā)
xr0 = 0xD0010000;IMASKH = xr0;//打開異常中斷
yr17 = j27;yr16 = j26;yr15 = k27;yr14 = k26 ;
// 嵌套調用,保護堆棧指針寄存器
. align_code 4 ;call InitialSPA(np) (abs);
// 調用UART 芯片初始化程序(略)
j27 = yr17;j26 = yr16;k27 = yr15;k26 = yr14;
// 釋放堆棧指針寄存器
_INITIAL_DSP.END:mRETURN
_decode:mEN TER
xr3 = [ j4 + 0 ];j2 = dsp_code;[ j2 + = 1 ] = xr3;
// j4 傳遞參數,譯碼結果存于dsp_code
j8 = dsp_code;// j8作為返回值返dsp_code的首地址
_decode.end:mRETURN
_IRQ0_int:mPUSHQ(xr3:0) mPUSHQ(j3:0)
// 保存所有寄存器
j1 = _inruptflag;xr0 = 1;[ j1 + 0 ] = xr0;
mPOPQ(j3:0) mPOPQ(xr3:0) // 釋放所有寄存器
_IRQ0_int .end:rti (ABS) (NP) ;nop ;nop ;nop;
4 結 語
匯編語言與高級語言的混合編程方法能夠最大限度地發(fā)揮兩種編程方法的優(yōu)勢,讓開發(fā)者寫出可移植性好,執(zhí)行效率高的DSP應用程序。設計良好的混合編程軟件既能有效滿足嵌入式系統(tǒng)對功能與性能的需求,也可以為程序的擴展和移植預留足夠的空間。該方法對ADI公司其他系列的DSP編程也有很大的參考價值。
參考文獻
[1]Michael Barr.Programming Embedded Systems in C and C++ [M].First Edition.O′Reilly,1999.
[2]王霞,馮立營,趙曉群.一種TI5000系列DSP C/C++語言和匯編語言混合編程的方法[J].現代電子技術,2004,27(15):71-74.
[3]齊崇英.TMS320C3x DSP的C匯編語言混合編程及中斷的C語言實現[J].國外電子元器件,2002(4):23-25.
[4]Analog Devices INC.ADSP-TS101 TigerSHARC Processor Software Reference[Z].2003.
[5]張永.C語言與匯編語言混合編程的研究與實現 [J].計算機與數字工程,2006(5):120-122.
[6]劉書明.TigerSHARC DSP 應用系統(tǒng)設計[M].北京:電子工業(yè)出版社,2004.
[7]陳麗安,魏宏偉.C54x DSP混合編程及中斷的C語言實現[J].微處理機,2005(6):52-56.
[8]任志考.用C語言進行DSP軟件設計的優(yōu)化方法[J].青島科技大學學報,2003(9):102-104.
[9]劉朝暉,鄭玉墻.用C語言進行DSP軟件設計的優(yōu)化考慮[J].空軍雷達學院學報,2001(2):49-52.
[10]Andrew Sloss.ARM System Developer's Guide:Designing and Optimizing System Software[M].Elsevier,2005.