1 TMS320C64X的特點 采用VelociTI.2體系結構的C64X系列定點DSP芯片,在結構上有許多特點: 1) C64X片內(nèi)有2個數(shù)據(jù)通道、8個功能單元(.L1,.L2,.S1,.S2,.D1,. D2,.M1和.M2,其中.M1和.M2為兩個乘法器)和2個一般目的寄存器文件(A和B)。而8個功能單元和2個寄存器文件又分成了相同的兩組,每組占用一個數(shù)據(jù)通道。兩個數(shù)據(jù)通道之間包含有兩個數(shù)據(jù)交叉通路。 2) C64X DSP采用超長指令字(VLIW),即在每個時鐘周期最高可提供8條32位指令,總字長為256位的指令包同時分配到8個并行處理單元。在600MHz的時鐘頻率下,當片內(nèi)8個處理單元同時運行時,其最大處理能力可以達到4800MIPS。 3) C64X DSP具有雙16bit擴充功能,芯片能在一個周期內(nèi)完成雙16bit的乘法、加減法、比較、移位等操作。 2 TMS320C64X的軟件開發(fā) 流程。 TMS320C64X內(nèi)部有8個獨立的功能單元,所以在一個周期內(nèi)最多可以并行執(zhí)行8條指令。然而指令與功能單元之間特殊的映射關系、每一條指令執(zhí)行時間的不相同、每一條指令的數(shù)據(jù)通路的不相同和指令間操作數(shù)的相關性等因素,致使一個周期內(nèi)實際能并行執(zhí)行的指令數(shù)達不到8條,從而降低了DSP的性能,因此,必須采用合理的開發(fā)和優(yōu)化流程,盡可能的對代碼進行優(yōu)化,從而提高指令執(zhí)行的并行度。 如圖1所示,工作流程一般分為三個階段。 階段一:直接按照需要用C語言實現(xiàn)功能。在實際的DSP應用中,許多算法都是非常復雜,直接用匯編代碼編寫,雖然優(yōu)化效率很高,可是實現(xiàn)的難度卻很大,所以一般都采用先用C語言來實現(xiàn),然后編譯運行,利用C64X開發(fā)環(huán)境的profile clock工具測試程序運行時間,若不能滿足要求,則進行第二階段。 階段二:C語言級的優(yōu)化。選擇C64X開發(fā)環(huán)境提供的優(yōu)化方式以及充分運用其他技巧,優(yōu)化C代碼,若還不能滿足效率要求,則進行第三步。 階段三:匯編級的優(yōu)化。將上一階段C程序中優(yōu)化效率較低的部分提出來,用線性匯編語言編寫,利用匯編優(yōu)化器進行優(yōu)化。匯編優(yōu)化器的作用是讓開發(fā)人員在不考慮C64X流水線結構和分配其內(nèi)部寄存器的情況下,編寫線形匯編語言程序,然后匯編優(yōu)化器通過分配寄存器和循環(huán)優(yōu)化將匯編語言程序轉化為利用流水線方式的高速并行匯編程序。 上述的三個階段不是都必須經(jīng)過,當在某一階段獲得了期望的性能,就不必進行下一階段的優(yōu)化。 3 C代碼的優(yōu)化方法 對于C64X平臺下的C源程序,可以選用的優(yōu)化方法有: 1) 選用C編譯器提供的優(yōu)化選項 在編譯器中提供了分為若干等級和種類的自動優(yōu)化選項,如下: ● -o:使能軟件流水和其他優(yōu)化方法 ● -pm:使能程序級優(yōu)化 ● -mt:使能編譯器假設程序中沒有數(shù)據(jù)存儲混淆,可進一步優(yōu)化代碼。 ● -mg:使能分析(profile)優(yōu)化代碼 ● -ms:確保不產(chǎn)生冗余循環(huán),從而減小代碼尺寸 ● -mh:允許投機執(zhí)行 ● -mx:使能軟件流水循環(huán)重試,基于循環(huán)次數(shù)對循環(huán)試用多個方案,以便選擇最佳方案。 根據(jù)實際編譯的程序,選擇合適的優(yōu)化選項,進行源程序的優(yōu)化。 2) 減小存儲器相關性 為使指令達到最大效率,C64X編譯器盡可能將指令安排為并行執(zhí)行。為使指令并行操作,編譯器必須知道指令間的關系,因為只有不相關的指令才可以并行執(zhí)行。當編譯器不能確定兩條指令是否相關時,則編譯器假定它們是相關的,從而不能并行執(zhí)行。設計中常采用關鍵字const來指定目標,const表示一個變量或一個變量的存儲單元保持不變。因此,在代碼中加入關鍵字const,可以去除指令間的相關性。例如下面的程序: void vecsum(short *sum,short*in1,short*in2,unsigned int N) { int i; for(i=0;i<N;i++) sum[i]=in1[i]+in2[i]; }  由其相關圖2(a)可見,寫sum可能對指針in1、in2所指向的地址有影響,從而in1和in2的讀操作必須等到寫sum操作完成之后才能進行,降低了流水效率,為幫助編譯器確定存儲器的相關性,使用const關鍵字來指定一個目標,上面的源程序可改為含關鍵字const的優(yōu)化源代碼: void vecsum(short * sum, const short*in1,const short*in2,unsigned int N) { int i; for(i=0;i<N;i++) sum[i]=in1[i]+in2[i]; }
由其相關圖2(b)可見,由于使用了關鍵字const,消除了指令之間的相關路徑,從而使編譯器能夠判別內(nèi)存操作之間的相關性,找到更好的指令執(zhí)行方案。 3) 使用內(nèi)聯(lián)函數(shù)(intrinsics) 內(nèi)聯(lián)函數(shù)是C64X編譯器提供的專門函數(shù),它們與嵌入式的匯編指令是一一對應的,其目的是快速優(yōu)化C源程序。在源程序中調(diào)用內(nèi)聯(lián)函數(shù),與調(diào)用一般的函數(shù)相同,只不過內(nèi)聯(lián)函數(shù)名稱前有下劃線作特殊標識。當匯編指令功能不易采用C語言表達時,可采用內(nèi)聯(lián)函數(shù)表示。例如在定點運算中經(jīng)常要求出源操作數(shù)的冗余符號位數(shù),這一功能如果用C完成的話,需要如下的代碼: unsigned int norm(int src1) { unsigned int sign, result = 0; sign = src1 & 0x80000000; while(1) { if(sign) { if((src1 = src1 << 1) & sign) result += 1; else return result; } else { if((src1 = src1 << 1) sign) return result; else result += 1; } } } 該源程序代碼冗長,有較多的邏輯操作和判斷跳轉,運行效率低下。若用內(nèi)聯(lián)函數(shù),則是result = _norm(src1),減少了代碼長度,提高了運行效率。因此對于需要大量C代碼才能表示的復雜功能,應該盡量用C64X的內(nèi)聯(lián)函數(shù)來表示。 4) short型數(shù)據(jù)的int處理 C64XDSP具有雙16bit擴充功能,芯片能在一個周期內(nèi)完成雙16bit的乘法、加減法、比較、移位等操作。在設計時,當對連續(xù)的short型數(shù)據(jù)流操作時,應該轉化成對int型數(shù)據(jù)流的操作,這樣一次可以把兩個16位的數(shù)據(jù)讀入一個32位的寄存器,然后用內(nèi)部函數(shù)來對它們處理(如_sub2等),充分運用雙16bit擴充功能,一次可以進行兩個16bit數(shù)據(jù)的運算,速度將提升一倍。 5) 盡量少進行函數(shù)調(diào)用 函數(shù)調(diào)用的時候,要將PC和一些寄存器壓棧保存,函數(shù)返回時,則將這些寄存器出棧返回,增加了一些不必要的操作。所以一些小的函數(shù),最好是用適當?shù)膬?nèi)聯(lián)函數(shù)代替直接寫入主函數(shù)里,一些調(diào)用不多的函數(shù),也可以直接寫入主函數(shù)內(nèi),這樣可減少不必要的操作,提高速度。但是這樣往往會增加程序的長度,因此是一種利用空間換取時間的辦法。 6) 盡量使用邏輯運算代替乘除運算 在DSP里,乘除運算指令的執(zhí)行時間要遠遠超過邏輯移位指令,尤其是除法指令,在設計的時候,可以根據(jù)實際情況,進行一些調(diào)整,盡量用邏輯移位運算來代替乘除運算,這樣可以加快指令的運行時間。 7) 軟件流水線技術的使用 軟件流水線技術用來對一個循環(huán)結構的指令進行調(diào)度安排,使之成為多重迭代循環(huán)并行執(zhí)行。在編譯代碼時,可以選擇編譯器的-o2或-o3選項,則編譯器將根據(jù)程序盡可能地安排軟件流水線。圖3顯示一個運用軟件流水線方式的循環(huán),它包括A、B、C、D、E5次迭代,同一周期可最多執(zhí)行5次迭代的不同指令(陰影部分)。  在DSP算法中存在大量的循環(huán)操作,因此充分地運用軟件流水線方式,能極大地提高程序的運行速度。但使用軟件流水線還有下面幾點限制: ● 循環(huán)結構不能包含代碼調(diào)用,但可以包含內(nèi)聯(lián)函數(shù)。 ● 循環(huán)計數(shù)器應該是遞減的。 ● 循環(huán)結構不能包含break,if語句不能嵌套,條件代碼應當盡量的簡單。 ● 循環(huán)結構中不要包含改變循環(huán)計數(shù)器的代碼。 ● 循環(huán)體代碼不能過長,因為寄存器(32個)的數(shù)量有限,應該分解為多個循環(huán)。 在軟件流水線的運用上,應該盡量使復雜的循環(huán)分解成簡單的小循環(huán),以避免寄存器的數(shù)量不夠;對于過于簡單的循環(huán),應該適當?shù)恼归_,以增加代碼數(shù)量,增加流水線中的迭代指令。 8) 采用指令亂序技術 程序中,有些指令的執(zhí)行順序沒有嚴格的要求,可以作出一些位置上的調(diào)整,因此可以適當?shù)恼{(diào)整這些指令的位置,穿插于其他的指令之中,從而減小指令的相關性,增加運行時的并行性。 尤其在循環(huán)里,當循環(huán)體較小的時候,可以把多個循環(huán)的代碼寫在一個循環(huán)體里,合并成一個循環(huán),從而減小循環(huán)內(nèi)指令的相關性,增加指令運行的并行性。但是要注意不要使循環(huán)過于復雜,以至不能進行軟件流水線的優(yōu)化。 4 匯編代碼級的優(yōu)化 在經(jīng)過C代碼的優(yōu)化之后,還不能滿足性能上的要求,則可以通過profile clock工具找出效率很低的部分,使用線性匯編重新改寫。再通過匯編優(yōu)化器編譯,匯編優(yōu)化器從輸入的線性匯編代碼中,完成以下功能: ● 尋找可以平行執(zhí)行的CPU指令。 ● 在軟件流水線期間,處理流水線標號。 ● 分配寄存器的用法。 ● 分配功能單元。 TI提供的匯編優(yōu)化器可以得到很高的效率,一般可以滿足性能上的要求。 5 優(yōu)化中的問題 在優(yōu)化過程中,總是要對程序進行一定的改動,這樣經(jīng)常會出現(xiàn)一些問題。 1) 優(yōu)化結果的驗證 優(yōu)化過的程序往往不知道是否運行正確,這就需要加以驗證。一般采用的辦法就是通過測試序列來驗證。測試序列指的是對于不同的算法所取的一組特殊的數(shù)據(jù),這些數(shù)據(jù)可以準確的反映算法的特性。測試序列中每組數(shù)據(jù)包括輸入數(shù)據(jù)和輸出數(shù)據(jù),通過對輸入數(shù)據(jù)的運算,把結果與輸出數(shù)據(jù)進行比較,判斷程序的正確性。一些常見的算法,一般都提供了測試序列。還有一些,沒有測試序列。這時就需要根據(jù)算法的特點,自己構造測試序列,進行驗證。構造的時候,注意序列最好有幾組,數(shù)據(jù)最好有一定的長度,這樣驗證的更準確。 2) 內(nèi)存泄漏的問題 C64X系列DSP的內(nèi)部存儲空間有1MB,其中程序和數(shù)據(jù)還有CPU的二級緩存將共享這片空間,因此當程序的運行不正常時,很有可能就是內(nèi)存泄漏造成的。因此,在程序設計中,應盡量不用指針,同時注意進行邊界檢測。 6 程序設計的一些方法 程序設計時,一切以滿足實際的要求為目標。在實際的設計中,除了優(yōu)化能夠提高性能以外,還可以采取其他的辦法,利用DSP的特性,提高程序的運行性能,滿足實際的設計要求。 1) 把程序和經(jīng)常要用的數(shù)據(jù)放入片內(nèi)RAM 片內(nèi)RAM與CPU 工作在同一時鐘頻率,比片外RAM性能高得多。因此把程序放在片內(nèi)可以大大提高運行的速度。同時對于一些經(jīng)常要用到的數(shù)據(jù),放入片內(nèi),也會節(jié)省處理時間。 2) 通過DMA技術搬移數(shù)據(jù) 對于C64X芯片,其片內(nèi)RAM有1MB,但是對于一些大型的圖像處理算法而言,仍可能是不夠的,因此經(jīng)常通過DMA技術,把需要用到的數(shù)據(jù)搬入片內(nèi),把不需要的搬到片外,可以大大的提高程序的運行速度。 3) CACHE的使用 增大CACHE,可以明顯的提高性能。但是C64X系列DSP中程序和數(shù)據(jù)還有CACHE共享片內(nèi)RAM,因此增大CACHE,就減小了實際的片內(nèi)可用空間,設計中需要注意。 7 結論 以上均是筆者在實際的DSP實踐中總結得出,對實際開發(fā)非常有幫助。以筆者對5×5模板的高斯濾波算法優(yōu)化為例,在優(yōu)化之前,算法的運算量為50MIPS,優(yōu)化后的運算量約為2MIPS,提高了20多倍,可見優(yōu)化的效果很明顯。以上這些經(jīng)驗主要是針對TI公司的64系列,但其中的一些對于別的型號的DSP也有借鑒作用。 |