TMS320C54x C編譯器的使用
TMS320C54x C編譯器的使用<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
1. 前言
TMS<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />320C54x 是TI公司針對通訊應用推出的中高檔16位定點DSP系列器件。該系列器件功能強大、靈活,較之TMS320C2xx ,具有以下突出優點:
(1)速度更快:40~100MIPS;
(2)指令集更為豐富;
(3)更多的定址方式選擇;
(4)兩個40位的累加器;
(5)硬體堆疊指標;
(6)支援塊重複和環型緩衝區管理。
TMS320C54x 的應用一般較為複雜,因此,完全採用組合語言進行程式設計將是一件很困難、也很低效的事情。而C語言以其靈活性、易移植性,已成為微控制器開發的一種趨勢,對於更為複雜的DSP應用來說,更是如此。
TI公司為TMS320C54x提供了功能強大的C編譯器,支援優化功能。下面就對使用TMS320C54x C編譯器中的一些體會介紹給讀者。作者在實踐中使用的TMS320C54x C編譯器的版本為1.10版,以下的討論都基於該版本。
2. C語言的移植
在進行復雜演算法開發時,一般的做法是先在PC上用高階語言(如C語言)進行模擬,然後再移植到DSP平臺中。考慮到效率問題,可進一步進行手工彙編的調整。編譯器的工作方式大致可分為兩類:一類直接由高階語言產生目的碼;另一類則是先生成中間的彙編程式碼,再彙編成目的碼。TI公司提供的C編譯器屬於後者。這樣,使用者可方便地根據中間的彙編程式碼進行手工調整和改寫。
在進行C語言移植時,涉及到兩個問題:一是庫函式的使用,二是字長問題。TI的TMS320C54x C編譯器提供符合ANSI C的函式庫。至於字長,要取決於硬體機器字長和作業系統,但一般來說,在不同的系統中,short型為16位,long型為32位,而int型卻有所不同。如,在VC環境中,int型為32位,而對於TMS320C54x C編譯器來說,int型則為16位。因此,在將C源程式由模擬環境向TMS320C54x平臺移植時,應根據各變數的取值範圍,儘可能地將int型歸類為short型或long型。
3. 段的分配
雖然,C語言是一種相對高效的高階語言,並且TI提供的C編譯器還結合硬體特點支援三級優化功能,但生成的彙編程式碼效率仍可能會不盡人意。如預使用環型緩衝區管理功能,這就要求該緩衝區應被定位到相對特定的位置。因此,使用者對C編譯器究竟是如何進行儲存分配的,應有一定的瞭解。
目標碼是以段為單位組織的。這裡,結合C語言的特點,簡單介紹一下幾種常用段的使用。
總的來說,可將所有的段分為兩類:已初始化段和未初始化段。
已初始化段中包含資料表和可執行程式碼,常用的有3個:.text 段、.cinit段和.const段。其中,.text 段中包含所有可執行的程式碼以及常量;.cinit段中包含未用const宣告的外部(external)或靜態(static)資料表;.const段中則包含已用const宣告的外部或靜態資料表以及字串常量。
未初始化段在儲存器(通常為RAM)中保留空間,用於程式執行時建立和儲存變數,常用的有兩個:.bss段和.stack段。其中,.bss段用於為全域性和靜態變數保留空間,在程式開始執行時,由C載入程式將.cinit段中的已初始化資料複製到.bss段中。.stack段用作C的系統堆疊,向被調函式傳遞引數,併為區域性變數分配空間。
在上述五個常用段中,.text 段和.cinit段被固定連線至程式空間,儲存器型別可以是ROM或RAM(一般為ROM,具體取決於編譯時RAM或ROM方式的選擇);.bss段和.stack段則被固定連線至資料空間,儲存器型別只能是RAM。
而.const段的使用則較為靈活。.const段被固定連線至資料空間,但儲存器型別可以是ROM或RAM。這就有別於.cinit段:.cinit段被連線至程式空間,程式執行時,再被複制到資料空間中的.bss段中。這樣,一張未用const宣告的資料表要同時佔用程式(.cinit段)和資料空間(.bss段)的一部分。與之相比較,如果系統支援資料ROM,則該資料表改用const宣告後,只需佔用資料空間(.const段)的一部分。
如果程式較為複雜,由C編譯器生成的.bss段會比較龐大,這種情況下對.bss段進行特定操作就比較困難。如將幾個常用的資料表連線至片內RAM,而非片外RAM;或需對某個資料表進行特殊定址。這時,即使目標系統不支援資料ROM,也有必要使用.const段。這就需要在連線時對.const段作如下規定:在程式空間中載入,而在資料空間中執行。C載入程式對其處理的方法類似與.cinit段,將其從程式空間複製到資料空間。但在使用1.10版的TMS320C54x C編譯器時發現,該版本的載入程式並不支援.const段的載入。通過閱讀C編譯器所帶的庫函式的原始碼,參照載入程式對.cinit段的處理,修改了該段載入程式,使之支援對.const段的載入。
除上述五個常見段外,TMS320C54x C編譯器還可能產生兩個段:.switch段和.sysmem段。其中,.switch段包含用於開關(switch)語句的資料表;而.sysmem段則用於動態儲存分配。如果直接使用了彙編,還可能用到.data段以及自命名的已初始化(程式碼)段和未初始化(變數)段。
4. C與組合語言的混用
在C語言中使用匯編語言,可採取兩種方式:一是呼叫匯編語言子程式;二是使用嵌入彙編。
4.1 呼叫匯編語言子程式
在TMS320C54x C程式中呼叫匯編語言子程式,必須遵循其函式呼叫規則和暫存器使用規則。
TMS320C54x C編譯器在處理函式呼叫時,關於如何傳遞入口引數以及如何返回出口引數制定了一套嚴格的規則。詳細內容請參見文獻[2]。
TMS320C54x 共有8個通用暫存器(AR0~AR7),其中AR1、AR6和AR7可能會被編譯器所使用,其中AR1一般用作區域性變數暫存,而AR7常被用作全域性變數。所以,如果所呼叫的組合語言子程式中使用了這3個暫存器,應作上下文保護。
TMS320C54x C編譯器支援暫存器變數的使用,最多兩個,分別為:AR1和AR6。通常的用法是建立全域性暫存器變數。較之一般儲存單元,C54x 對暫存器的存取速度最快。因此,對一些使用頻率很高的變數來說,將其宣告為暫存器變數,可顯著提高處理速度。
如果使用了全域性暫存器變數,應採用-r選項對所有程式碼(包括庫函式)進行重新編譯,以禁止C編譯器對AR1或AR6的使用。
4.2 嵌入彙編
雖然TMS320C54x C編譯器提供了三級優化功能,但作者實際使用的體會仍不夠滿意。而在C中呼叫匯編語言子程式,又要求嚴格遵循其引數傳遞規則。由於TMS320C54x C編譯器支援在C原始碼中直接用asm語句嵌入彙編,故在C編譯器所產生的中間彙編程式碼的基礎上,可區域性使用嵌入彙編。這樣,一方面可大大提高一些頻繁使用的程式碼段的效率,另一方面,又不用改變原來的函式框架,也便於對照模擬結果進行除錯。
在進行嵌入彙編時,要特別注意流水線的衝突。TMS320C54x 採用6級流水線結構,在出現流水線衝突的地方,可插入空操作(nop)指令來解決。常見的易產生流水線衝突的指令如STLM、ADDM等。
關於1.10版,還有一個值得注意的問題,即如何由兩個短整數獲得32位的長整數乘積。
如:
short m1,m2;
long result;
result = (long) (m1 * m2); (1)
result = ((long) m1 * (long) m2); (2)
對於(1),1.10版的編譯器只是將乘積的低16位存入result;而對於(2),得到的是一個32位的長整數乘積,但卻是呼叫了兩個長整數相乘的庫函式(L$$MPY)而得到的,這對於含有大量乘加運算的DSP應用來說,效率上難以令人接受。為此,採用嵌入彙編可方便地解決這個問題。
5.CCS的安裝
(1) 安裝Setup CCS軟體
(2) 開啟Setup CCS.exe檔案如下圖所示
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
圖3-1
中間一列是可安裝的硬體裝置,選中所需裝置後雙擊開啟
圖3-2
選擇Bord Proparties 項,將I/O Port 的值改為0x378,
圖3-3
再選擇Processor Configuration 項,在左邊的Available Processors方框中選擇所需的處理器,然後按Add Single 鍵新增後完成對處理器的安裝設定.
6.CCS的使用(以CCS C5000 1.2為例)
圖3-4
1. 建立 .gel 檔案
.gel檔案是DSP晶片初始化程式.其中包含分配記憶體、設定引腳電平等內容.要注意的是,不同的DSP晶片所使用的.gel檔案也不相同,如果使用的.gel檔案與所用的晶片不符合,則會在將源程式生成的.out檔案匯入DSP晶片時發生錯誤.
具體做法為:先開啟如5-4圖中左上方框內的GEL files ①資料夾,將原有預設的.gel檔案刪除後用滑鼠右鍵點選GEL files 資料夾,選擇Load GEL項,然後將與所使用的DSP型號相匹配的.gel檔案匯入.
以下是‘C5402的.gel檔案
StartUp()
{
GEL_Reset(); /* reset the target */
GEL_MapOn(); /* enable the memory map */
GEL_MapReset();
/* Uncomment for extended addressing support
GEL_XMDef(0, 0x1E, 1, 0x8000, 0x7F);
GEL_XMOn();
GEL_MapAdd(0x18000,0,0x8000 ,1,1);
*/
GEL_MapAdd(0x0000, 0,0x10000,1,1); /* int vectors */
GEL_MapAdd(0x0000, 1,0x10000,1,1); /* data space */
GEL_MapAdd(0x0000, 2,0x0010 ,1,1); /* uart */
PMST = 0xffa0; /* on-chip program */
SWWSR = 0x7492; /* 2 wait states */
BSCR = 0x0002; /* bank-switch */
*(int*)0x0004@io = 1; /* enable external ram at 0x8000 */
}
2. 建立.mak檔案
.mak檔案是一專案檔案.
具體做法為:點選工具欄中的project④項,在下拉選單種選擇New,然後在儲存時將檔案屬性改為.mak即可.
3.將源程式生成為.out檔案
CCS可以使用C語言源程式(*.C)和組合語言源程式(*.asm)兩種.在將源程式寫入DSP中前必須將其生成為.out檔案.
具體做法為: 點選工具欄中的project項,在下拉選單種選擇Add Files to Project,選擇所需的源程式,便將源程式匯入到.mak下的Source 資料夾中 ②.雙擊後可在右上方框中顯示該程式內容.點選控制欄中Rebuild⑤,對源程式進行編譯,並將結果顯示在左下方框中.如所編的源程式中存在錯誤,則會出現”The program contains compile error Do you wish to continue linking?”的對話方塊,取消後可根據方框中的提示對源程式進行修改. 編譯通過後,在原始檔同一檔案加下會生成一原始檔同名的.out和.obj檔案.out檔案使用以寫入DSP的輸出檔案,而.obj則是一連結檔案.
4.將.out檔案寫入DSP
在工具欄中點選File項,在下拉選單種選擇Load Program,選中已生成的.out檔案,便將程式寫入DSP中.
5.執行程式
在控制欄中選擇Register Window⑦則會出現如圖5-4中右下方框,這是用以顯示各暫存器的狀態的.在此框中單擊滑鼠右鍵會出現一可選選單,選擇Edit Register項,在彈出的控制框中將PC(程式計數器)值改為0x0080(程式預設入口).點選控制欄中的Run⑥,程式執行.
如希望程式以單步執行可點選step by step12,點選halt13則程式暫停。
6.復位與重新開始
在工具欄中選擇Debug⑧,在下拉選單中選Reset DSP或Restart.
當重新寫入新的程式時應先將DSP晶片復位.當需要重新執行現有成序時可使用Restart
將程式計數器指標復原.
7.更改暫存器和儲存器的值
在硬體除錯過程中, 更改暫存器和儲存器的值是一種非常有效的手段.工具欄中的Edit⑨中有Memory和Edit register兩項可實現該功能.
8.檢視各儲存空間
軟體除錯中瞭解各儲存空間的值是非常重要的.控制欄中的View memory⑩可檢視Data,Program和I/O共192K字儲存空間中各地址的內容.而View Stack11可檢視堆疊中的值.
9.常見問題的處理
在編譯時時常出現以下警告:” warning: entry point symbol _c_int00 undefined”,這可通過選擇工具欄中的Project④項,在出現的下拉選單中選擇Option項,則會有如下控制框彈出如圖 5-5所示控制框。
選擇Linker項,在如圖所示的下拉選單中選擇No Autoinitialization項,然後再Rebuild即可.
圖3-5
相關文章
- 使用C編譯器編寫shellcode編譯
- FreeBSD中的GNU C編譯器--編譯器GCC(轉)編譯GC
- GCC編譯器的使用GC編譯
- 在c++程式中呼叫被C編譯器編譯後的函式,為什麼要使用extern “C”C++編譯函式
- CMM編譯器和C編譯器過程呼叫實現的比較編譯
- protobuf 的交叉編譯使用(C++)編譯C++
- 使用makefile編譯c程式編譯C程式
- C++編譯器優化C++編譯優化
- 安裝c, c++編譯器 on AIXC++編譯AI
- C編譯: 使用gdb除錯編譯除錯
- C 編譯: 使用 gdb 除錯編譯除錯
- 配置C#命令列編譯器C#命令列編譯
- makefile教程---nmake命令編譯器的使用編譯
- 編譯器的編譯基本過程編譯
- 一文搞懂C/C++常用編譯器C++編譯
- Rust 編譯器探索使用 PGORust編譯Go
- c++模板類的使用,編譯的問題C++編譯
- -debug(C# 編譯器選項)C#編譯
- C語言編譯器手機版C語言編譯
- 微軟開源 C# 編譯器 Roslyn微軟C#編譯ROS
- gcc 編譯器與 clang 編譯器GC編譯
- 在C,C++,java和python執行時直譯器和編譯器的區別C++JavaPython編譯
- 【譯】使用 Python 編寫虛擬機器直譯器Python虛擬機
- 富文字編譯器UEditor+SSM的使用編譯SSM
- 在命令列下使用vs的編譯器命令列編譯
- 第一個C語言編譯器是怎樣編寫的?C語言編譯
- 第一個 C 語言編譯器是怎樣編寫的?編譯
- 為什麼C++編譯器不能支援對模板的分離式編譯 (轉)C++編譯
- C語言_來了解一下GCC編譯器編譯C可執行指令碼的過程C語言GC編譯指令碼
- 使用 Sublime Text 3 編譯 C 語言編譯
- C/C++—— C++編譯器是如何實現多型C++編譯多型
- 淺談彙編器、編譯器和直譯器編譯
- [譯]iOS編譯器iOS編譯
- C/C++開發者必不可少的15款編譯器+IDEC++編譯IDE
- 編譯原理——C++版桌面計算器編譯原理C++
- 編譯器的自展和自舉、交叉編譯編譯
- C++編譯器認為的指標型別(靜態聯編)C++編譯指標型別
- 編譯 TensorFlow 的 C/C++ 介面編譯C++