Global Sources
電子工程專輯
 
電子工程專輯 > FPGA/PLD
 
 
FPGA/PLD  

在DSP應用程式中整合C/組合語言程式碼

上網時間: 2008年09月23日  打印版  Bookmark and Share  字型大小:  

關鍵字:C語言  組合語言  DSP 

本文將討論如何將組合語言程式碼整合到C語言中,以最大化性能以及程式設計人員生產力,內容涵蓋了編譯器慣例(convention)、內嵌(inlining)、內嵌函數(intrinsic)、暫存器連結(register binding)和除錯策略。

隨著DSP處理器性能的提升以及編譯器最佳化技術的進步,曾經紅極一時、僅用組合語言編寫DSP應用程式的作法已逐漸被淘汰。今天,幾乎每個DSP應用程式都使用C語言程式碼和組合語言程式碼混合的方式。對於一些性能需求極高的關鍵功能,DSP工程師會繼續使用高度最佳化的組合語言程式碼;而一些次要的功能現在也使用C語言編寫,使程式碼更容易維護和移植。對於C語言和組合語言程式碼的結合,每位DSP工程師都需要掌握特殊的工具和方法。

眾所皆知,組合語言編碼具有更高的性能優勢,而用C語言編碼則較容易且速度也更快。為瞭解其中原因,以下我們進一步比較組合語言編碼與C語言編碼的優缺點:

組合語言編碼的優點:

•組合語言編碼可以充分利用處理器的獨特指令以及各種專門的硬體資源。而C語言程式碼是通用型程式碼,必須支援各種硬體平台,因此很難支援特殊平台程式碼。

•組合語言程式設計人員通常對應用程式非常熟悉,可以作出編譯器無法作出的假設。

•組合語言程式設計人員可以發揮人類的創造性;而再先進的編譯器也只是一個自動化的程式。

組合語言編碼的缺點:

•組合語言程式設計人員必須解決耗時的機器級問題,如暫存器分配和指令排程。若使用C語言程式碼,這些問題可以由編譯器解決。

•使用組合語言編碼的程式設計人員必須瞭解DSP架構及其指令集的專業知識;而C語言編碼只需要掌握相當普及的C語言知識。

•若使用組合語言,將應用程式從一個平台移植到另一個平台非常困難也相當耗時;而C語言應用程式的移植相對而言非常容易。

圖1顯示了如何利用專用硬體機制來獲得高度最佳化的組合語言程式碼。左邊的C語言編碼利用模組演算法設計出一個循環緩衝區P1;右邊高度最佳化的組合語言程式碼中,等效的緩衝區是利用CEVA-TeakLite-III DSP核心的模組運算機制(Modulo Mechanism)設計產生的。只要緩衝區指標(本例中的r0)有更新,模組運算機制便會自動執行模組運算。這種運算與指標更新在同一個週期發生,因此組合語言程式碼比C語言程式碼更加高效,可為模組運算產生獨立的指令。

圖1:右邊的CEVA-TeakLite-III組合語言程式碼可以建置成左邊的C語言程式碼。
圖1:右邊的CEVA-TeakLite-III組合語言程式碼可以建置成左邊的C語言程式碼。

為DSP應用選擇C/組合語言程式碼

混合使用的問題就在於該如何劃分C語言程式碼和組合語言程式碼的界限,而答案取決於剖析器提供的性能分析結果。然而在使用剖析器之前,DSP工程師需要為應用程式定義清晰的物件,一些典型的物件包括迴圈數、程式碼大小和數據大小。一旦這些物件確定後,所有應用程式都應該先以C語言編寫和製作,隨後使用剖析器來分析性能。

在一些極端情況下,如控制應用,用C語言層級的編碼就足夠了;但大多數情況下,原始C語言層級應用程式版本不會遵從一個或多個物件,這也意味著需要使用一些組合語言程式碼來完成。在求助於組合語言編碼之前,C語言編碼可提供許多方法來提高性能,但這些方法不屬於本文討論的範疇。假設所有C語言級的方法全用完了,並且準備使用組合語言編碼,這時強烈建議將原始C語言程式碼保存起來。這樣不僅方便除錯,而且一旦條件許可(比如轉移到更強大的平台)還可以回復原始的C語言。

程式碼中的組合語言部份應盡可能維持在最少,這樣便能分析從剖析器得到的性能結果,並定義應用程式中的關鍵函數。關鍵函數會佔用大部份執行時間,必須用組合語言重寫才能滿足性能物件。當兩到三個最關鍵的函數重寫後,需要重新進行性能測量,若應用程式仍然不能滿足物件需求,那麼必須使用組合語言定義並重寫額外的關鍵函數,這個過程需要不斷地重複直到滿足性能物件需求為止。

組合語言設計師的編譯器考量

在編寫會與C語言程式碼結合的組合語言程式碼時,組合語言程式設計人員必須瞭解編譯器的慣例和假設。其中有個重要的編譯器慣例是函數呼叫慣例,也稱為函數參數傳遞慣例。這個慣例描述了編譯器如何在一個函數呼叫另一個函數時傳遞參數。為了使組合語言函數能被C語言函數成功呼叫;反之亦然;組合語言函數必須截取參數,然後將參數發送到由函數呼叫慣例定義的硬體資源上,通常為暫存器或堆疊記憶體。

組合語言程式設計人員還必須瞭解編譯器的暫存器使用慣例。這些慣例將硬體暫存器分成被呼叫者保存(callee-saved;或呼叫者使用,caller-used);以及被呼叫者使用(callee-used;或呼叫者保存,caller-saved)暫存器。編譯器假設被呼叫者保存暫存器在函數呼叫過程中保持不變的值,若組合語言程式設計人員希望使用這種暫存器,他們必須先將暫存器的值備份,然後在返回到C語言程式碼之前恢復這些暫存器的內容;相反的,被呼叫者使用暫存器被認為在函式呼叫過程中不會保持一定的值。這意味著組合語言程式設計人員使用這些暫存器之前無需進行備份,不過他們必須記住,當組合語言函數呼叫C語言函數時,被呼叫者可以對這些暫存器進行覆寫。

圖2為一個從CEVA-X1641 DSP核心FFT實作中截取的組合語言程式碼案例。其中以黃色標示的add指令遵循CEVA-X1641編譯器的呼叫慣例,在r0位址暫存器中傳遞指標參數。標為藍色的pushd指令用於備份,隨後函數會使用的被呼叫者保存暫存器。

圖2:從CEVA-X1641手寫FFT演算法組合語言實作摘錄的一段程式碼。
圖2:從CEVA-X1641手寫FFT演算法組合語言實作摘錄的一段程式碼。

除了呼叫慣例和暫存器使用慣例(針對每個編譯器下定義),一些編譯器在人工編寫的組合語言程式碼方面可能會有一些額外的假設。這些假設通常是針對編譯器,因此編譯器供應商應該提供完善的資料和說明。例如,一些DSP架構會有記憶體存取對齊限制,用於這些DSP的編譯器通常假設堆疊指標以某個寬度(如32位元)對齊,這允許編譯器最佳化堆疊的讀寫作業,並使用機器的全部記憶體頻寬;此外亦要求組合語言程式設計人員在呼叫C語言函數前確保堆疊對齊,否則會發生對齊錯誤的存取。

編譯器假設的另外一個例子與人工編寫的組合語言程式碼中特殊指令的位置有關。例如,CEVA-X1641編譯器假設一個mov acX, rN指令(將累加器移動到位址暫存器)永遠不會作為組合語言函數的第一條指令。當填充呼叫指令(呼叫一個函數)的延遲槽時,這個假設可提供更佳的指令排程。像這樣特殊的假設通常可以用專用編譯功能覆蓋。

連接C/組合語言的延伸功能

大多數用於嵌入式平台的編譯器,特別是用於DSP程式設計上,都具有豐富的C語言和組合語言連接功能。其中絕大部份功能不屬於標準C語言,因此被稱為C語言延伸功能。以下列出的是一些對DSP程式設計更有用的功能。

內嵌組合語言。這個功能可讓程式設計人員將組合語言指令插入C語言程式碼,當必需使用如裝置驅動程式等低階C語言程式碼直接存取機器資源時,會常使用到該功能。由於在大多數使用該功能的實作中,編譯器對所要插入的指令資訊所知有限,因此對它們的特性會作出最壞的假設,這種假設可能會妨礙許多編譯器最佳化作業。例如,在支援某些指令(並非全部指令)平行處理的架構中,編譯器不會將插入指令與其它指令作平行處理,因為這種作法很可能會導致非法指令封包。

將硬體暫存器連結到C變數。將一個硬體暫存器連結到一個C變數時,C語言程式碼中的變數值即反映出硬體暫存器的值;反之亦然。每當C變數被讀寫時,硬體暫存器也相對地被讀寫。這個功能在低階程式碼中很常見,時常與組合語言指令內嵌功能結合在一起,允許內嵌組合語言程式碼存取C語言層級的變數。圖3的例子顯示了內嵌組合語言功能(標示為橙色)和硬體暫存器連結功能(標示為紫色)的常見組合。

圖3:結合內嵌組合語言和硬體暫存器連結的程式碼片段。
圖3:結合內嵌組合語言和硬體暫存器連結的程式碼片段。

記憶體扇區屬性。預設狀態下,編譯器將全域C變數和函數分配到標準的預定義記憶體扇區,該扇區屬性允許程式設計人員將上述變數和函數分配到特殊的使用者定義記憶體扇區。在隨後的連結階段,這些記憶體扇區可以被映射到具體的記憶體位址。該功能可讓程式設計人員將C語言層級單元分配到實際的記憶體位置,這對DSP應用程式來說至關重要。

使用者定義呼叫慣例。如上所述,編譯器有一個組合語言程式設計人員必須遵守的預定義呼叫慣例;然而在某些情況下,組合語言函數可利用不同的呼叫慣例獲得更佳的最佳化效果。例如,編譯器理論上會在累加器中傳遞參數。若執行延伸位址計算的函數能接收位址暫存器中的參數,那麼它的效率會更高。該功能會依靠附加在函數原型的專用語法,並通知修正後的呼叫慣例編譯器。

編譯器內嵌函數。意指能夠用專用的巨集或函數呼叫,觸發內建編譯器功能的總稱。例如,CEVA-X和CEVA-TeakLite-III編譯器可為語音編碼器中常見的ETSI/ITU基本DSP作業,提供編譯器內嵌函數。針對這些作業,編譯器可利用其等效高度最佳化組合語言序列,取代每個基本作業。

相反地,沒有內嵌函數支援的編譯器必須呼叫使用者定義的函數,這樣做會導致兩大性能缺陷:首先,使用者定義函數可能會在一個迴路裡產生函數呼叫和返回(如圖4),因此產生了巨額的開銷;其次,使用者定義函數將如同其它C語言程式碼一樣被編譯,這意味著使用者定義函數可能會獲得次要的最佳化性能。而另一方面,具有內嵌函數的編譯器已經內建了最佳化的實作。

圖4:H.264編碼器——一個關鍵的函數性能案例。
圖4:H.264編碼器——一個關鍵的函數性能案例。

圖4說明了這個功能的重要性。在圖4中,左邊的C語言程式碼使用ETSI的mult_r(循環相乘)基本作業,CEVA-TeakLite-III編譯器產成了如右邊的高效實作結果。mult_r作業在左邊的C語言程式碼和右邊的組合語言程式碼中以紫色標示。

組合語言內嵌函數。組合語言內嵌函數是將組合語言程式碼內嵌到C語言程式碼的一種先進方法,詳細介紹如下。

組合語言內嵌函數——將組合語言指令當作C語句一樣編寫

上述內嵌組合語言功能具有顯著的缺點:

1. 它會破壞各種編譯器的最佳化作業,由於編譯器不瞭解內嵌程式碼的內容,因此會使用最壞的假設。

2. 它可能迫使程式設計人員處理低階問題,如暫存器分配和指令排程。

組合語言內嵌函數功能可以幫助程式設計人員實現內嵌組合語言程式碼,且不會產生上述缺點。從程式設計人員的角度來看,組合語言內嵌函數就像是C語言巨集或函數,它們會在呈現一個單一組合語言指令時,接收C語言層級變數並返回C語言輸出結果。由於涉及該功能的所有程式碼都在C語言層級,因此程式設計人員不必擔心暫存器分配、指令排程和其它低階語言問題。組合語言內嵌函數不僅不會妨礙編譯器最佳化作業,還會參與最佳化過程,就好像是編譯器固定產生的組合語言指令一樣,這些特性造就了強大的組合語言內嵌函數功能。

利用組合語言內嵌函數,程式設計人員可以從特殊組合語言指令中受益,這些指令不太可能從編譯器中產生,且通常是為特定演算法量身定做的。在適當的位置採用這些指令可以大幅提高性能;例如,CEVA-X1641的bitrev指令就是為FFT等演算法定製的。由於編譯器不太可能把一個程式看作FFT而使用bitrev指令,因此程式設計人員只需將bitrev組合語言內嵌函數嵌入到C語言程式碼中。

結合程式設計人員對應用程式的專業知識,組合語言內嵌函數功能也相繼提升。利用這種專業知識,程式設計人員可將精密的組合語言序列內嵌函數用在C應用程式中的關鍵性能區域裡。這樣一來,程式設計人員便能確保編譯器產生的組合語言程式碼效率就如同手動編寫的程式碼一樣高。

圖5是使用組合語言內嵌函數與CEVA-X1641編譯器的例子。左邊的C語言函數使用st(儲存,以紅色標示)和msu(乘法和減法,以紫色標示)組合語言內嵌函數。st內嵌函數參與判斷(標示為藍色)和延遲時隙填充(標示為綠色);msu內嵌函數則參與迴路解開(標示為橙色)和Quad-Mac(標示為紫色)。組合語言內嵌函數還受益於由CEVA-X1641編譯器處理的所有機械相關問題,如暫存器分配、指令排程和硬體單元分配。

圖5:CEVA-X1641編譯器支援組合語言內嵌函數的使用。
圖5:CEVA-X1641編譯器支援組合語言內嵌函數的使用。

同時使用C/組合語言進行除錯

組合語言程式碼的除錯並非一件小事,它需要對如延遲和記憶體對齊限制等架構和機械層級問題有深入的瞭解。單純地結合C語言程式碼與組合語言程式碼會使問題變得更加棘手,因為這樣一來程式設計人員便得對C語言程式碼和組合語言程式碼間的連結進行除錯。

進行混合應用程式除錯的第一步就是隔離問題。假設組合語言程式碼的C語言層級實作維持不變,且C語言層級實作能正常作業,那麼將組合語言函數轉換成C語言實作並重新測試應用程式就變得相當容易。為了迅速檢測問題,程式設計人員可以採用交互作業程序:每一個步驟都將一半的可疑函數轉換為相應的C語言實作,這樣一來程式設計人員在進行每一步時都只需測試前一步中一半的函數。

一旦有問題的組合語言函數被確定,就應該同時調查單獨組合語言問題和C語言與組合語言的連接問題。單獨組合語言問題的除錯對組合語言程式設計人員來說十分簡單明瞭,但C語言與組合語言的連接問題就有點麻煩。不同於單獨的組合語言問題,在查看組合語言函數本身時,無法看見C語言與組合語言的連接問題;為了找出這些問題,程式設計人員必須檢查編譯器的慣例,例如呼叫慣例和暫存器使用慣例。

程式設計人員還必須檢查編譯器假設,例如組合語言指令的行蹤(重複前面提到的例子,CEVA-X1641編譯器假設mov acX, rN指令絕不會作為組合語言函數的第一條指令)。為了節省除錯時間,程式設計人員應該在第一次實作組合語言函數時驗證是否所有的編譯器慣例和假設都有遵循慣例。

H.264視訊編碼器和AMR-NB

本文討論的技術和方法已被CEVA公司用於各種應用程式中,包括視訊編解碼器、音訊編解碼器、語音編碼器和裝置驅動器。此外,本文所述的功能無論用在何種案例,均能顯著地提升性能。

H.264視訊編碼器是一個很好的研究案例。它在處理能力(通常以MHz衡量)及其它資源方面都有強烈需求,特別是在與音訊編解碼器等其它類型的編解碼器比較上。CEVA公司利用其高階CEVA-X16xx DSP核心系列及其MM2000多媒體平台,提供這種編碼器所需的處理能力。

CEVA公司利用先進剖析技術確定這種編碼器的關鍵函數,然後對它進行最佳化。編碼器的關鍵函數最佳化過程是逐步完成的。首先,利用如組合語言內嵌函數這樣的先進功能全面地將函數最佳化成C語言;然後進一步將編譯器提供的組合語言程式碼最佳化成組合語言層級。

圖6顯示出透過對這種編碼器關鍵函數進行最佳化過程所獲得的性能改善。只有最後一個最佳化階段涉及到全部組合語言程式碼範圍;所有其它階段都基於具有組合語言內嵌函數的C語言程式碼。這些組合語言內嵌函數主要用於SIMD(單指令多資料)作業,如avg_acW_acX_acZ_4b。這條指令對8個輸入位元組執行位元組平均,進而產生4位元組。這種SIMD作業對執行大量位元組層級運算的視訊編解碼器而言相當實用(這也是為何CEVA-X16xx架構為位元組層級的SIMD作業提供廣泛支援的原因)。

圖6:CEVA-TeakLite-III編譯器內建ETSI基本作業支援。
圖6:CEVA-TeakLite-III編譯器內建ETSI基本作業支援。

AMR-NB(自適應多碼率——窄頻)是廣泛應用在無線通訊應用的語音編解碼器。CEVA已為其所有DSP核心建置該語音編碼器;但為遵循本文主旨,我們在此只討論CEVA-X1620建置。將這種語音編碼器完全建置到組合語言的情況相當常見,倘若使用本文提到的各種功能,C語言實作和CEVA-X1620編譯器可達到與組合語言實作競爭的結果。其中提升CEVA-X1620編譯器性能的關鍵功能就是支援ETSI內嵌函數的功能。

圖7顯示了整個AMR-NB應用經過最佳化過程後在MCPS(每秒百萬循環)上所獲得的性能改善。只有最後的最佳化階段涉及了全範圍的組合語言編碼,所有其它階段都基於具有ETSI內嵌函數和組合語言內嵌函數等的C語言程式碼。

圖7:對ARM-NB進行各種最佳化方法所獲得的MCPS改善。
圖7:對ARM-NB進行各種最佳化方法所獲得的MCPS改善。

總之,H.264編碼器和AMR-NB的案例清楚地顯示了組合語言實作的性能優勢,但也顯示出純組合語言實作並非首選的最佳化方法。利用高品質軟體開發工具鏈提供的各種C語言和組合語言功能,DSP程式設計人員不必用組合語言建置整個應用程式也能達到令人滿意的性能結果。正如本文所述,編寫C語言和組合語言混合程式碼不是一件簡單的工作;不過,本文討論的各種功能都有助於DSP工程師更輕鬆地完成這項任務。

作者:Eran Balaish

資深編譯器專案經理

CEVA公司





投票數:   加入我的最愛
我來評論 - 在DSP應用程式中整合C/組合語言程式碼
評論:  
*  您還能輸入[0]個字
*驗證碼:
 
論壇熱門主題 熱門下載
 •   將邁入40歲的你...存款多少了  •  深入電容觸控技術就從這個問題開始
 •  我有一個數位電源的專利...  •  磷酸鋰鐵電池一問
 •   關於設備商公司的工程師(廠商)薪資前景  •  計算諧振轉換器的同步整流MOSFET功耗損失
 •   Touch sensor & MEMS controller  •  針對智慧電表PLC通訊應用的線路驅動器
 •   下週 深圳 llC 2012 關於PCB免費工具的研討會  •  邏輯閘的應用


 
返回頁首