在TMS320F2812上实现从flash拷贝整个程序到RAM上运行的方法探讨 zealane@gmail.com 1. 前言 TMS320F2812 DSP 里,代码从内部flash里运行,比从内部RAM里运行要慢30%左右,所以对运行时间苛刻的程序直接在flash里运行,往往不能满足要求。故而,需要将代码拷贝到RAM以提高运行速度。TI文档只提供了部分代码从flash拷贝到RAM中的方法。然而,在一些应用中,需要将整个代码段都拷贝到RAM中执行,以提高整体运行速度。本文通过对TMS320F2812 的启动代码研究,来探讨如何在从FLASH启动后将整个代码段拷贝到RAM中,然后在RAM中运行的方法。 2. TMS320F2812 启动过程 TMS320F2812的内部存储器中,0x3F F000 ----0x3FFFC0是4K*16的Boot ROM。 https://bbs.21ic.com/upfiles/img/20077/200771414205491.jpg 图1 2812内部BootRom地址图 CPU向量表位于北部ROM的底端(0x3FFFC0 ---- 0x3FFFF)。当VMAP=1,ENPIE=0 ,MPNMC=0时,该向量表被激活。复位向量出厂时被编程指向函数InitBoot。这个函数开始启动过程。当然,启动过程完成之后,我们需要初始化PIE中断向量表,同时使能PIE block。初始化PIE中断向量表之后,除了复位,所有中断向量均从PIE获得。
TMS320F2812启动代码固化在该内部ROM中。当TMS320F2812上电或者热复位后,首先由芯片本身将一些寄存器初始化: 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 - 0x44F作为启动过程中的堆栈);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 0x3F 7FF6 下图为BootROM 函数的流程图 https://bbs.21ic.com/upfiles/img/20077/2007714142153244.jpg 图2 BootROM 函数的流程图 对于内部flash启动,如图
https://bbs.21ic.com/upfiles/img/20077/2007714142225969.jpg 图3跳转到Flash启动的流程图 在0x3F7FF6必须放置一个跳转指令,该指令跳转到你自己的启动代码或者应用程序。 3. 搬移思路 根据上节2的启动过程,flash启动过后,跳到0x3F7FF6,然后根据其内容再跳转到应用程序。我们在这里稍微修改一下,就可以将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中程序
|