在TMS 320F 2812上實作從flash拷貝整個程式到RAM上運作的方法探讨
1. 前言
TMS 320F 2812 DSP 裡,代碼從内部flash裡運作,比從内部RAM裡運作要慢30%左右,是以對運作時間苛刻的程式直接在flash裡運作,往往不能滿足要求。故而,需要将代碼拷貝到RAM以提高運作速度。TI文檔隻提供了部分代碼從flash拷貝到RAM中的方法。然而,在一些應用中,需要将整個代碼段都拷貝到RAM中執行,以提高整體運作速度。本文通過對TMS 320F 2812 的啟動代碼研究,來探讨如何在從FLASH啟動後将整個代碼段拷貝到RAM中,然後在RAM中運作的方法。
2. TMS 320F 2812 啟動過程
TMS 320F 2812的内部存儲器中,0x 3F F000 ----0x3FFFC0是4K*16的Boot ROM。
圖1 2812内部BootRom位址圖
CPU向量表位于北部ROM的底端(0x3FFFC0 ---- 0x3FFFF)。當VMAP=1,ENPIE=0 ,MPNMC=0時,該向量表被激活。複位向量出廠時被程式設計指向函數InitBoot。這個函數開始啟動過程。當然,啟動過程完成之後,我們需要初始化PIE中斷向量表,同時使能PIE block。初始化PIE中斷向量表之後,除了複位,所有中斷向量均從PIE獲得。
TMS 320F 2812啟動代碼固化在該内部ROM中。當TMS 320F 2812上電或者熱複位後,首先由晶片本身将一些寄存器初始化:
PIE disabled(ENPIE=0,VMAP=1,OBJMDE=0,AMODE=0,MOM1MAP=1),
然後dsp晶片會對XMPNMC管腳采樣,根據采樣值的高低,來決定啟動模式是“微處理器模式”還是“微計算機模式”。當XMPNMC=0時,為“微計算機模式”,此時,啟動ROM存儲器被使能而XINTF Zone 7被禁止。複位向量從内部啟動ROM擷取,啟動ROM在複位期間一直被使能。
啟動ROM裡的複位向量(位于0x3FFFC0)指向InitBoot函數(位于0x3FFC00)。在完成器件初始化(InitBoot)之後,Boot loader将檢查GPIO管腳的狀态,然後再決定選用的啟動模式。啟動模式有4種:跳轉到flash,跳轉到H0 SARAM,跳轉到OTP或者調用片上啟動程式。
InitBoot Function 所做工作有:1.初始化狀态寄存器;2.将堆棧指針設為0x400(0x400 - 0x 44F 作為啟動過程中的堆棧);3.讀CSM密碼保護部分;4.調用SelectBootMode;5.調用ExitBoot
在完成選擇啟動模式過程之後,根據選擇的啟動模式,dsp會跳到相應的啟動入口。也可以自己選擇啟動入口。這些入口位址都在這之前已經被dsp定義好的。
如果從flash啟動,那麼我們的管腳狀态應該是
GPIOF4 | GPIOF12 | GPIOF3 | GPIOF2 | |
(SCITXDA) | (MDXA) | (SPISTEA) | (SPICLK) | |
内部上拉 | 無内部上拉 | 無内部上拉 | 無内部上拉 | Mode Selected |
1 | x | x | x | Jump to Flash address 0x 3F 7FF6 |
下圖為BootROM 函數的流程圖
圖2 BootROM 函數的流程圖
對于内部flash啟動,如圖
圖3跳轉到Flash啟動的流程圖
在0x 3F 7FF6必須放置一個跳轉指令,該指令跳轉到你自己的啟動代碼或者應用程式。
3. 搬移思路
根據上節2的啟動過程,flash啟動過後,跳到0x 3F 7FF6,然後根據其内容再跳轉到應用程式。我們在這裡稍微修改一下,就可以将PC指針跳到RAM首位址,程式就能在RAM裡運作了。
将“搬移程式”燒在flash上,從flash啟動之後,“搬移程式”會被執行。“搬移程式”做的工作就是将“應用主程式”代碼拷貝到内部RAM,然後經過初始化環境(InitBoot,ExitBoot),将PC指針指向RAM裡代碼首位址。這樣,你的程式就在RAM中運作起來了。也就是說,我們需要一個“搬移程式”,啟動時運作,用來拷貝flash上的代碼到RAM中;當然,還需要一個你的“應用主程式”,該主程式被“搬移程式”從flash裡“挪”到RAM後在RAM中運作。我們還要做的工作就是,把“應用主程式”燒寫到flash裡的某一塊,這個塊又不會影響flash啟動時運作“搬移程式”。完成這個燒寫過程的程式,我們稱之為flash燒寫程式。
總結一下,完成整個搬移過程,一共需要三個程式。“搬移程式”和“應用主程式”被固化到flash裡“燒寫flash程式”将“應用主程式”燒入flash中指定的塊中。TI的燒寫flash插件用來燒寫“搬移程式”。
4. 搬移方法
1)首先你要用的主程式必須編譯通過,并且通過仿真器在RAM裡運作無問題。将主程式的CMD檔案進行改寫,保證程式段(.text段)配置設定在連續的存儲空間。程式從flash啟動,所有初始化段連結在非易失存儲器裡,而非初始化段必須連結在易失存儲器。我們可以把初始化段都放在一個連續的内部RAM空間,而非初始化段放在另一個内部RAM空間。如果你的代碼不是很大,也可以都放在連續的RAM空間。但在實際項目中,通常你會遇到存儲空間不夠的問題。這時就要考慮将無關緊要的段放在另外的非程式空間了。
.cinit | Flash |
.cio | RAM |
.const | Flash |
.econst | Flash |
.pinit | Flash |
.switch | Flash |
.text | Flash |
.bss | RAM |
.ebss | RAM |
.stack | Lower 64Kw RAM |
.sysmem | RAM |
.esysmem | RAM |
.reset | RAM1 |
例如:
MEMORY
{
PAGE 0 :
RAMH0 : origin = 0x3F8000, length = 0x002000
PAGE 1 :
/* SARAM */
RAMM0M1 : origin = 0x000000, length = 0x000800
RAML0L1 : origin = 0x008000, length = 0x002000
}
SECTIONS
{
/* Allocate program areas: */
.reset : > RAMH0 PAGE = 0
vectors : > RAMH0 PAGE = 0
.cinit : > RAMH0 PAGE = 0
.text : > RAMH0 PAGE = 0
.const : > RAMH0 PAGE = 0
.econst : > RAMH0 PAGE = 0
.switch : > RAMH0 PAGE = 0
/* Allocate data areas: */
.stack : > RAMM0M1 PAGE = 1
.bss : > RAML0L1 PAGE = 1
.ebss : > RAML0L1 PAGE = 1
.sysmem : > RAML0L1 PAGE = 1
}
從上面的CMD可知,主程式代碼均放在RAMH0中,長度為0x2000。
2)其次,将該工程編譯成功後,加載到内部ram,仿真器自動完成必要的初始化環境之後,pc指針應該指向_c_init00,記下現在PC指針的位置,在Boot.asm中會用到。
3)自制一個flash燒寫程式,或者從網上下載下傳其他網友的flash燒寫程式,将目标位址放在除flashJ塊以外的塊中。燒寫的長度不能小于被燒寫的主程式長度。該燒寫程式在RAM中運作。,其代碼段不能和被燒寫的主程式用的代碼段存儲區域相同,否則會破壞主程式在ram中的代碼。
燒寫之後,可以用CCS的Save data功能,來檢視flash中的數值是否和ram裡主程式空間數值一緻。
4) 用TI的燒寫插件燒寫“搬移程式”。注意,該“搬移程式”要能在dsp啟動後執行。并且,燒寫的時候,不能将上一步燒到flash上的主程式代碼擦除。“搬移程式”具體見下節。
5. “搬移程式”具體實作方法
Boot.asm檔案内容:
.def _InitBoot
.ref _EntryAddr_H
.ref _EntryAddr_L
.sect ".InitBoot"
;
; _InitBoot
;
; 1) Initalizes the stack pointer
; 2) Sets the device for C28x operating mode
; 3) Calls the main boot functions
; 4) Calls an exit routine
;
_InitBoot:
; Initalize the stack pointer.
MOV SP, #0 ; Initalize the stack pointer
; Initalize the device for running in C28x mode.
C28OBJ ; Select C28x object mode
C28ADDR ; Select C27x/C28x addressing
C28MAP ; Set blocks M0/M1 for C28x mode
CLRC PAGE0 ; Always use stack addressing mode
MOVW DP,#0 ; Initialize DP to point to the low 64 K
CLRC OVM
; Set PM shift of 0
SPM 0
; Read the password locations – this will unlock the
; CSM only if the passwords are erased. Otherwise it
; will not have an effect.
MOVL XAR1,#0x3F7FF8;
MOVL XAR0,*XAR1++
MOVL XAR0,*XAR1++
MOVL XAR0,*XAR1++
MOVL XAR0,*XAR1
; Cleanup and exit. At this point the EntryAddr
; is located in the ACC register
BF _ExitBoot,UNC
;
; _ExitBoot
;
;
;This module cleans up after the boot loader
;
; 1) Make sure the stack is deallocated.
; SP = 0x400 after exiting the boot
; loader
; 2) Push 0 onto the stack so RPC will be
; 0 after using LRETR to jump to the
; entry point
; 2) Load RPC with the entry point
; 3) Clear all XARn registers
; 4) Clear ACC, P and XT registers
; 5) LRETR – this will also clear the RPC
; register since 0 was on the stack
;
_ExitBoot:
;
; Insure that the stack is deallocated
;
MOV SP,#0
;
; Clear the bottom of the stack. This will endup
; in RPC when we are finished
;
MOV *SP++,#0
MOV *SP++,#0
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
;
; Load RPC with the entry point as determined
; by the boot mode. This address will be returned
; in the ACC register.
;向堆棧中壓入0x3f8000,該位址即為主程式在ram中運作的首位址。
MOV *SP++, #0x8000
MOV *SP++, #0x3F
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
POP RPC
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
;
; Put registers back in their reset state.
;
; Clear all the XARn, ACC, XT, and P and DP
; registers
;
; NOTE: Leave the device in C28x operating mode
; (OBJMODE = 1, AMODE = 0)
;
ZAPA
MOVL XT,ACC
MOVZ AR0,AL
MOVZ AR1,AL
MOVZ AR2,AL
MOVZ AR3,AL
MOVZ AR4,AL
MOVZ AR5,AL
MOVZ AR6,AL
MOVZ AR7,AL
MOVW DP, #0
;
; Restore ST0 and ST1. Note OBJMODE is
; the only bit not restored to its reset state.
; OBJMODE is left set for C28x object operating
; mode.
;
; ST0 = 0x0000 ST1 = 0x0A0B
; 15:10 OVC = 0 15:13 ARP = 0
; 9: 7 PM = 0 12 XF = 0
; 6 V = 0 11 M0M1MAP = 1
; 5 N = 0 10 reserved
; 4 Z = 0 9 OBJMODE = 1
; 3 C = 0 8 AMODE = 0
; 2 TC = 0 7 IDLESTAT = 0
; 1 OVM = 0 6 EALLOW = 0
; 0 SXM = 0 5 LOOP = 0
; 4 SPA = 0
; 3 VMAP = 1
; 2 PAGE0 = 0
; 1 DBGM = 1
; 0 INTM = 1
;
MOV *SP++,#0
MOV *SP++,#0x0A0B
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
POP ST1
POP ST0
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
;
; Jump to the EntryAddr as defined by the
; boot mode selected and continue execution
;
LRETR
;
.end
“搬移程式”部分代碼:
unsigned long *srcAddr = (unsigned long *)0x3D8000;
unsigned long *desAddr = (unsigned long *)0x3F8000;
InitSysCtrl();
// Disable and clear all CPU interrupts:
DINT;
IER = 0x0000;
IFR = 0x0000;
// Initialize Pie Control Registers To Default State:
InitPieCtrl();
InitPieVectTable();
//以下即将flash上的代碼拷貝到ram中,根據自己需要,更改源位址和目标位址
for(i = 0; i < 0x2000; i++)
{
*(desAddr + i) = *(srcAddr + i);
}
InitBoot(); //調用Boot.asm中程式