TMS320F28XXX的程序段从flash复制到ram中运行的实现
摘要 这个应用报告和相关的代码提供了一种把编译后的程序段从TMS320F28xxx的flash复制到ram的功能,这样可以提高代码的运行速度。这个解决方案在直接启动之后,进入c_int00 ——C语言代码运行之前实现此功能。 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为例。 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部分是根据设备的内存空间来连接编译好的段的。详情参见具体控制器的数据手册。
|