翻譯自TI應用手冊SPRAAU8
摘要
這個應用報告和相關的代碼提供了一種把編譯後的程序段從TMS320F28xxx的flash複製到ram的功能,這樣可以提高代碼的運行速度。這個解決方案在直接啓動之後,進入c_int00 ——C語言代碼運行之前實現此功能。
本應用報告中所討論的項目內容和源代碼可以從以下網址下載:http://www-s.ti.com/sc/techlit/spraau8.zip
1.引言:
在許多應用中,代碼的執行速度是至關重要的。例如在醫療,監控,電機控制等等一些對時間有嚴格要求的終端設備。許多應用使用TMS320F28xxx DSCs是因爲它的內置flash儲存器。內置flash是TMS320F28xxx的一個優勢,因爲它使得設計者不需要外接flash來儲存代碼。使用內部flash缺點是訪問Flash需要等待狀態,這使得程序的運行變慢。在大多數應用中,這不是一個問題。其他一些應用中可能會爲了獲得最高的運行速度要求無等待狀態。內部RAM存儲器具有零等待狀態,它是易失性存儲器。所以,引導的初始化代碼段不可以存儲在此存儲器中。
現在提供的解決方案,使得設計者能夠在運行時把被編譯器初始化的代碼段從flash複製到ram裏,獲得最大的運行速度。這使代碼執行從多達15個等待狀態的提升到0等待狀態。另一種解決方案是只將某些函數從Flash複製到RAM。詳見:《Running
an Application from Internal Flash Memory on the TMS320F28xx DSP》
(SPRA958)。這種方法應該使用在大多數使用C2000™ DSC的應用上,其他要求嚴格的時序和連續的零等待狀態的應用程序應採用這裏提出的解決方案。
編寫彙編程序來完成代碼從Flash到RAM的複製。該彙編代碼在復位向量後調用c_int00之前執行。這保證了在c_int00調用mian()之前完成複製。
有一些工程比較小,可以把所有初始化了的段都複製到ram。然而,其他一些工程的初始化了的段比所有的內部ram還要大。這些工程可能不可以把所有的初始化了的段都複製到ram,但是用這種方法複製其中一部分段。
2.編譯的代碼段:
編譯器生成的包含代碼和數據的多個部分,稱爲段。這下段被分爲兩個不同的組:初始化了的和沒被初始化的,初始化的部分是由所有的代碼,常量和初始化表組成的。下表列出了由編譯器產生的初始化段。
段名 | 內容 |
限制 |
---|---|---|
.cinit | 顯式初始化的全局變量和靜態變量表 | 代碼 |
.const | 顯式初始化的全局和靜態的const變量和字符串常量 | 不超過64K長度 |
.econst | 長調用的常量 | 數據中的任何地方 |
.pinit | 全局對象的構造函數表 |
代碼 |
.switch | switch語句產生的表 | 代碼或者數據 |
.text | 可執行代碼和常數 | 代碼 |
沒初始化的段是由未初始化的變量,堆棧和malloc產生的內存。下表列出了由編譯器產生的沒初始化段。
段名 | 內容 | 限制 |
---|---|---|
.bss | 全局和靜態變量 | 不超過64K長度 |
.ebss | 長調用的全局或靜態變量 | 數據中的任何地方 |
.stack | 堆棧空間 | 不超過64K長度 |
.sysmem | malloc函數產生的內存 | 不超過64K長度 |
.esysmem | far_malloc函數產生的內存 | 數據中的任何地方 |
一旦編譯器生成的這些段,連接器會從各個源文件中取出這些段,並結合它們來創建一個輸出文件。連接器命令文件(.cmd)就是用來告訴連接器去哪裏找這些段的。初始化段必須分配到非易失性存儲器,如flash/ ROM,當電源被撤除時,程序不會消失。未初始化的段可以被分配到RAM中,因爲它們是在代碼執行期間被初始化的。
關於更多編譯段和連接的信息,請參見:《TMS320C28x Assembly Language
Tools User’s Guide 》(SPRU513) 和《 the TMS320C28x Optimizing C/C++ Compiler User’s Guide》(SPRU514)。
德州儀器(TI)提供了多個例子顯示如何使用鏈接器命令文件分配編譯段。其中一個就是《Running an Application from Internal Flash Memory on the TMS320F28xx DSP 》(SPRA958)。此應用文檔提供的例子,演示了使用基於RAM和Flash的項目的鏈接器命令文件。
3.軟件:
本應用文檔相關的代碼文件,包括修改後的版本的CodeStartBranch.asm文件和非DSP/BIOS™項目用的文件DSP28xxx_SectionCopy_nonBIOS.asm,由the C/C++ Header Files and Peripheral Examples提供。每個TMS320F28xxx 處理器都提供了現成的連接器命令文件。提供的示例項目演示瞭如何使用這些文件。本應用文檔以TMS320F2808爲例。
該軟件獨立存放於F28xxx_Flash_to_Ram文件夾中。代碼使用的來自the C/C++ Header Files and Peripheral Examples的幾個文件,經過了Code Composer Studio™ 3.3和F28xxx代碼生成工具5.0.0B3版本的測試。
3.描述:
一般的程序流程是這樣子的:code_start->wd_disable->copy_sections->c_int00->mian()
。這個軟件流程比標準的軟件流程僅僅多了調用複製代碼段函數。標準的軟件流程:code_start->wd_disable->c_int00->mian()。
程序開始和關閉看門狗:
code_start 和wd_disable 的運行代碼由DSP28xxx_CodeStartBranch.asm文件提供。上電後,code_start正常執行,因爲它被分配給Flash的引導地址的0x3F7FF6。詳見:《Running
an Application from Internal Flash Memory on the TMS320F28xx DSP
》(SPRA958)
WD_DISABLE .set 1 ;set to 1 to disable WD, else set to 0
.ref copy_sections
.global code_start
***********************************************************************
* Function: codestart section
*
* Description: Branch to code starting point
***********************************************************************
.sect "codestart"
code_start:
.if WD_DISABLE == 1
LB wd_disable ;Branch to watchdog disable code
.else
LB copy_sections ;Branch to copy_sections
.endif
這個函數從the C/C++ Header Files and Peripheral Examples提供的CodeStartBranch.asm文件修改而來,只是第二個調用用copy_sections代替了_c_int00。這個調用僅僅在WD_DISABLE爲0時執行。 上面的代碼,WD_DISABLE 被設置爲1。這使得wd_disable運行。wd_disable的代碼如下:
***********************************************************************
* Function: wd_disable
*
* Description: Disables the watchdog timer
***********************************************************************
.if WD_DISABLE == 1
.sect "wddisable"
wd_disable:
SETC OBJMODE ;Set OBJMODE for 28x object code
EALLOW ;Enable EALLOW protected register access
MOVZ DP, #7029h>>6 ;Set data page for WDCR register
MOV @7029h, #0068h ;Set WDDIS bit in WDCR to disable WD
EDIS ;Disable EALLOW protected register access
LB copy_sections ;Branch to copy_sections
.endif
這要求看門狗在copy_sections和c_int00函數運行期間被除能,否則,看門狗可能會在進入main()之前超時。這個函數也是從the C/C++ Header Files and Peripheral Examples提供的CodeStartBranch.asm文件修改而來,只是用copy_sections代替了_c_int00。
Copy_sections:
DSP28xxx_SectionCopy_nonBIOS.asm文件提供了copy_sections的代碼,第一次運行到這裏,看門狗是關閉的,段已經準備好被複制,段大小被存放在累加器,裝載地址放在XAR6中,執行地址放在XAR7中,這個功能例子如下:
MOVL XAR5,#_text_size ; Store Section Size in XAR5
MOVL ACC,@XAR5 ; Move Section Size to ACC
MOVL XAR6,#_text_loadstart ; Store Load Starting Address in XAR6
MOVL XAR7,#_text_runstart ; Store Run Address in XAR7
LCR copy ; Branch to Copy
段的大小,裝載開始標誌,執行開始標誌都由連接器產生,這是在內存分配 -鏈接器命令文件一節討論。
在地址和段長度都被存放好之後,copy程序被調用來確定段是否被編譯器產生,這由檢測累加器是否爲0來確定。
copy:
B return,EQ ; Return if ACC is Zero (No section to copy)
RPT AL ; Copy Section From Load Address to
|| PWRITE *XAR7, *XAR6++ ; Run Address
return:
LRETR ; Return
如果累加器爲0,程序會返回到調用前的地址,如果累加器不爲0,有段需要被複制。這用上面所示的PWRITE指令來實現,PWRITE複製XAR6指向的存儲器的內容到XAR7指向的內容。在這裏,就是複製裝載代碼的地址的內容到運行代碼的地址。這樣,一直到累加器爲0,完成整個段的複製,當所有段都被複制完,程序就會跳到c_int00,如下:
LB _c_int00 ; Branch to start of boot.asm in RTS library
到這裏,C語言環境被建立,main()是可進去的。
完整的copy_sections程序請參見相關文件夾中的DSP28xxx_SectionCopy_nonBIOS.asm。內存分配
- 連接命令文件(.cmd):
如第二節所述,連接命令文件(.cmd)是用來告訴連接器怎麼分配編譯器產生的段的。The C/C++ Header Files and Peripheral Examples提供了標準的連接命令文件(.cmd)。
相關代碼文件中提供了三個鏈接器命令文件用於配置內存分配。
· F280xx_nonBIOS_flash.cmd
· F281x_nonBIOS_flash.cmd
· F2833x_nonBIOS_flash.cmd
每個文件一般都用相同的方法編寫,只是在存儲器方面有很小的一些差異(特殊設備)。連接命令文件(.cmd)的Memory部分是根據設備的內存空間來連接編譯好的段的。詳情參見具體控制器的數據手冊。
下表展示TMS320F2808的存儲器映射:
TMS320F28xxx系列控制器內置RAM,可以被分配爲一個單獨的段,或者更多的段,因爲它是連續的存儲器映射。如上圖所示,F2808有映射到存儲器空間的L0,L1和H0 SARAMs,允許生成一個大的內存塊,這個塊可以被CMD文件的MEMORY部分如下定義:
RAM_H0L0L1 : origin = 0x008000, length = 0x004000 /* on-chip RAM */
其餘的也可以定義在MEMORY部分,完整的內存分配,請參見相關文件中的CMD文件。
鏈接器命令文件的第二部分是SECTIONS。這是實際編譯器把段連接到的存儲區。所有DSP28xxx_CodeStartBranch.asm 和 DSP28xxx_SectionCopy_nonBIOS.asm的段都被裝載到flash中運行,這部分如下所示分配:
codestart : > BEGIN_FLASH, PAGE = 0 /* Used by file CodeStartBranch.asm */
wddisable : > FLASH_AB, PAGE = 0 /* Used by file CodeStartBranch.asm */
copysections : > FLASH_AB, PAGE = 0 /* Used by file SectionCopy.asm */
其他被初始化的段被下載到flash,但是在ram中運行。這是通過load和run指令來實現。下面展示一個例子:
.text : LOAD = FLASH_AB, PAGE = 0 /* Load section to Flash */
RUN = RAM_H0L0L1,PAGE = 0 /* Run section from RAM */
LOAD_START(_text_loadstart),
RUN_START(_text_runstart),
SIZE(_text_size)
爲了獲得與一個段相關聯的特定地址,如上所示,使用了LOAD_START, RUN_START, 和SIZE指令。這些指令的地址和大小在DSP28xxx_SectionCopy_nonBIOS.asm文件使用到,用以在複製過程中指向正確的地址。DSP28xxx_SectionCopy_nonBIOS.asm把這些值創建爲全局變量,如下圖所示
.global _cinit_loadstart, _cinit_runstart, _cinit_size
.global _const_loadstart, _const_runstart, _const_size
.global _econst_loadstart, _econst_runstart, _econst_size
.global _pinit_loadstart, _pinit_runstart, _pinit_size
.global _switch_loadstart, _switch_runstart, _switch_size
.global _text_loadstart, _text_runstart, _text_size
測試例子:
提供的示例在TMS320F2812,TMS320F2808,TMS320F28335eZdsp開發板上進行了測試。板子上LED的閃爍可以從視覺上證實程序是否正確運行。下面的程序是基於F2808eZdsp評估板設計和測試的。同樣的,這種方法可以用於其他eZdsp開發板。
Code Composer Studio環境:
1.使用USB線連接F2808eZdsp開發板到PC,接上電源線給板子供電。
2.打開Code Composer Studio,設置F2808 eZdsp 仿真器。
3.打開和編譯Example_280xx_Flash_to_RAM_nonBIOS.pjt。
4.下載.out文件到芯片的flash中。
5.調試程序(debug)。
6.運行程序(run)。
在eZdsp電路板上的LED應閃爍,表示程序正在運行。
應用:
現有的Flash應用程序可以很容易地通過移植相關代碼文件來實現此功能。基本的移植步驟如下:
1.用DSP28xxx_CodeStartBranch.asm替換CodeStartBranch.asm。
2.在工程中添加DSP28xxx_SectionCopy_nonBIOS.asm文件。
3.用特殊生成的CMD文件代替現有的CMD文件。
這個基本步驟不適用於一些特殊情況,比如用戶自己定義的段,等
應用例子:
爲了演示的應用程序集成的過程,在C280x,C2801x
C / C ++頭文件和外設示例的Example_2808_Flash.pjt中使用下列步驟移植。
1.下載安裝C280x, C2801x C/C++ Header Files and Peripheral Examples。
2.如上所述連接板,打開項目文件。
3.刪除項目中的DSP280x_CodeStartBranch.asm文件,在項目中添加DSP28xxx_CodeStartBranch.asm文件。
4.在項目中添加DSP28xxx_SectionCopy_nonBIOS.asm文件。
5.刪除項目中的cmd文件,在項目中添加F280xx_nonBIOS_flash.cmd文件。
6.把DSP280x_usDelay.asm中的.sect “ramfuncs”改爲.text,使DSP28x_usDelay在被分配在.test段中。
7.刪除DSP280x_SysCtrl.c文件中的#pragma CODE_SECTION(InitFlash, “ramfuncs”);。使得InitFlash( )函數被分配到.test而不是ramfuncs。
8.刪除Example_280xFlash.c文件中的#pragma CODE_SECTION(epwm1_timer_isr, “ramfuncs”);和#pragma CODE_SECTION(epwm2_timer_isr, “ramfuncs”);。使得中斷服務函數被分配到.test而不是ramfuncs。
9.刪除Example_280xFlash.c文件中的MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);和
InitFlash( );。由於代碼已經被複制到RAM,這些是不需要的了。
10.如上所述,編譯連接程序,把程序下到芯片裏運行。
在eZdsp電路板上的LED應閃爍,表示程序正在運行。
存儲空間佔用:
因爲僅僅在DSP28xxx_SectionCopy_nonBIOS.asm文件中增加了copy_sections的代碼。增加的佔用的片內flash爲0x3C。code_start 和wd_disable函數沒有增加額外的代碼,他們本來就在C/C++ Header Files and Peripheral Examples的所用項目中被使用。
測試:
因爲這個功能開機後直接實現,閃存等待狀態,鎖相環(PLL)都沒有配置,因此,它們都運行在默認值。Flash等待狀態爲15個週期,對於F280xx/F281x設備SYSCLKOUT爲OSCCLK/2,對於F2833x設備SYSCLKOUT爲OSCCLK/
4。使用Code
Composer Studio的分析功能可以測量運行時間。下表給出了每個F28xxx
控制器從啓動到main()函數的第一個指令所用的時間,如下所示,由於個平臺的代碼長度和系統時鐘不一樣,他們的運行時間也不一樣。
限制:
此實現的限制因素爲使用的TMS320F28xxx控制器內部RAM的大小。這限制了那些工程可以使用這種方法,如果工程太大,以至於沒法放進RAM裏,這種方法是不能用的。
建議:
有一些項目需要這種功能,但不是所有被初始化段都要複製到RAM或者沒有足夠的RAM放下所有的段。僅僅需要複製應用代碼本身。這種情況下,僅僅需要複製.text段到RAM。這樣子,可以把DSP28xxx_SectionCopy_nonBIOS.asm文件和cmd文件中複製其他段的代碼刪掉,把其他段放在flash中運行。減少flash的佔用空間和縮短了運行到main()的時間。
應該確定應用程序可以處理複製代碼執行時間的一點滯後。如果應用程序不能處理這段時間,可以使用Running an Application from Internal Flash Memory on the TMS320F28xx DSP (SPRA958)中的方法複製一部分主要的代碼到ram。
如果使用DSP的引導,建議使用Running an Application from Internal Flash Memory on the TMS320F28xx DSP (SPRA958)中的方法複製一部分主要的代碼到ram。一個使用DSP
/ BIOS的項目,通常是一個較大的項目,不建議使用此方案。
結語:
這份應用文檔展示,在建立C語言環境之前,通過把flash的代碼複製到ram,可以使TMS320F28xxx的控制器實現零等待狀態運行。這方案給出了代碼和存儲空間的限制,爲設計者提供了實現了這種功能的相關文件。