翻译自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