新闻  |   论坛  |   博客  |   在线研讨会
Running an Application from Internal Flash Memory on the TMS320F28xxx DSP
luge1983 | 2010-04-13 09:52:49    阅读:6953   发布文章



《Running an Application from Internal Flash Memory on the TMS320F28xxx DSP》
SPRA958H

摘要

从TMS320F28xxx DSP片内flash运行应用程序有几个特殊的要求。在RAM中开发时,通常不需要这些要求,因为CCS调试器会将问题关联到初始化过的段以及如何链接到存储器,从而掩盖了这些要求。本应用报告包括了正确配置应用软件从片内flash运行的要求,DSP/BIOS工程和非DSP/BIOS工程都有描述,也讨论了一些性能考虑和技术。本文还有一些从eZdspF2812/2808/28335开发板片内flash运行的例程代码,以及从片内RAM运行的例子。如果您需要,可以用这些这些例子作为开发过程的开始。

本应用报告附带的工程和源码可在http://www-s.ti.com/sc/techlit/spra958.zip下载。

注意,本应用文档讨论的事项可直接应用于TMS320F28xxx DSP系列的:F2810,F2811,F2812,F2801,F2801-60,F2802,F2802-60,F2806,F2808,F2809,F28015,F28016,F28044,F28232,F28234,F28235,F28332,F28334,F28335。不保证可应用于此系列的未来其他芯片。另外,本应用文档中的DSP/BIOS工程代码和技术是在CCS3.3.81.5上开发的,使用的是C编译器5.1.0和DSP/BIOS5.33。我们希望读者将这些版本更新到最新,然而需要留心的是,DSP/BIOS未来的版本特性变化可能会使本报告中的某些事项变的不再需要。(但为保持后向兼容性,这里讨论的技术还是会起作用)

本报告不提供为F28xxx DSP编写和构建代码的教程。本文假设,用户已经准备好了从RAM运行的工程版本的主要框架。本报告只描述迁移到从片内flash运行需要考虑的事项。

1 介绍

TMS320F28xxx DSP系列是为在嵌入式控制器应用中独立操作而设计的。由于存在片内flash,因此常常不再需要“外部non-volatile存储器+引导装载用的主处理器”组合。只要遵守几个简单步骤,配置从片内flash运行其实很简单。本报告包括从片内flash运行工程的主要要点和步骤。DSP/BIOS工程和非DSP/BIOS工程都有描述,也讨论了一些性能考虑和技术。

注意,本应用文档讨论的事项可直接应用于TMS320F28xxx DSP系列的当前成员。在接下来的章节中,术语“F28xxx”是指F2810,F2811,F2812,F2801,F2801-60,F2802,F2802-60,F2806,F2808,F2809,F28015,F28016,F28044,F28232,F28234,F28235,F28332,F28334,F28335。不保证可应用于此系列的未来其他芯片。另外,本应用文档中的DSP/BIOS工程代码和技术是在CCS3.3.81.5上开发的,使用的是C编译器5.1.0和DSP/BIOS5.33。我们希望读者将这些版本更新到最新,然而需要留心的是,DSP/BIOS未来的版本特性变化可能会使本报告中的某些事项变的不再需要。(但为保持后向兼容性,这里讨论的技术还是会起作用)

最后,本报告不提供为F28xxx DSP编写和构建代码的教程。本文假设,用户已经准备好了从RAM运行的工程版本的主要框架。本报告只描述迁移到从片内flash运行需要考虑的事项。

2 创建一个用户链接器命令文件(User Linker Command File)

2.1 非DSP/BIOS工程

在非DSP/BIOS应用中,user linker command file指定大部分存储器在何处定义,以及大部分段在何处链接。此文件的格式与你从RAM运行的当前工程中使用的链接器命令文件(linker command file)是一样的。不同的地方在于你要把段链接到哪里(在第三章讨论)。更多关于linker command file的信息见参考文献9。本应用报告附带的非DSP/BIOS例程里有一些linker command files,可以用来参考。

DSP281x,DSP280x,DSP2804x和DSP2833x的外设头文件里有一些linker command files,分别是:DSP281x_Headers_nonBIOS.cmd,DSP280x_Headers_nonBIOS.cmd,DSP2804x_Headers_nonBIOS.cmd和DSP2833x_Headers_nonBIOS.cmd(见参考文献15-18)。这些文件包含链接器MEMORY和SECTIONS声明,用于链接外设寄存器结构体。根据你的硬件,将其中的一个cmd文件添加到你的工程中去,你原先的user linker command file也不要删。

通常情况下,不必考虑cmd文件的顺序,因为CCS在编译时会先评估(evaluate)每个cmd文件的MEMORY段,然后再评估每个cmd文件的SECTIONS段。这保证了在将任何段链接到存储区之前,所有的存储区都已经定义过了。尽管如此,在一些罕见情况下,高级用户可能需要手动控制cmd文件的评估顺序,指定cmd文件的顺序见CCS的Project——Buile_Options,Link_Order标签页。

2.2 DSP/BIOS工程

DSP/BIOS配置工具生成的cmd文件指定了如何链接DSP/BIOS产生的所有段,以及(默认情况下)C编译器产生的所有段。当从RAM中运行程序时,可能只用到这一个cmd文件。然而,当从flash运行时,很可能需要产生并链接一个或多个用户定义的段。实际上,任何操作片内flash控制寄存器(例如flash等待状态)的代码都不能从flash运行。另外,您也许想从RAM而不是flash中运行一些时间苛刻的代码以提高性能。这时,必须创建一个user linker command file来处理这些用户定义的段。

CCS允许一个工程有多个cmd文件。因此,用户需要做的就是:在DSP/BIOS生成的cmd文件基础上,为工程再添加user linker command file。通常情况下,不必考虑cmd文件的顺序,因为CCS在编译时会先评估(evaluate)每个cmd文件的MEMORY段,然后再评估每个cmd文件的SECTIONS段。这保证了在将任何段链接到存储区之前,所有的存储区都已经定义过了。尽管如此,在一些罕见情况下,高级用户可能需要手动控制cmd文件的评估顺序,指定cmd文件的顺序见CCS的Project——Buile_Options,Link_Order标签页。

DSP281x,DSP280x,DSP2804x和DSP2833x的外设头文件里有一些linker command files,分别是:DSP281x_Headers_nonBIOS.cmd,DSP280x_Headers_nonBIOS.cmd,DSP2804x_Headers_nonBIOS.cmd和DSP2833x_Headers_nonBIOS.cmd(见参考文献15-18)。这些文件包含链接器MEMORY和SECTIONS声明,用于链接外设寄存器结构体。根据你的硬件,将其中的一个cmd文件添加到你的工程中去。

3 段在哪里链接

段有两种基本类型:初始化段和未初始化段。初始化段上电时必须包含有效数据。例如,代码和常量都位于初始化段。使用F28xxx DSP设计一个独立嵌入式系统(例如,在运行时无仿真器、无调试器、没有执行引导加载的主处理器)时,所有初始化段必须连接到non-volatile存储器(例如片内flash)。未初始化段在上电时不包含有效数据。例如,变量位于未初始化段。代码在运行时会将数据写入变量位置。因此,未初始化段必须连接到volatile存储器(例如RAM)。

建议:在链接器中使用-w选项。如果链接器在工程中遇到在cmd文件中未明确指定链接的段,-w选项会产生警告。当链接器遇到未指定段时,会按默认算法对段进行链接(将段链接到首个定义过的且空间足够的存储区)。这常常有风险,可能引起不可靠的、不可预料的代码行为。-w选项会识别未指定的段(例如用户偶然忘记指定了),这可以帮助用户完善cmd文件。在CCS中,菜单Project--Build_Options,Linker选项卡,选择Advanced目录,选中-w选项。新建工程时,-w是默认选中的。

警告
重要的是C编译器要使用large memory model(与small memory model相对而言)。small memory model要求特定的初始化段链接到可寻址空间的低64K word的non-volatile存储区。然而,F28xxx的这个区域没有flash,也许未来F28xxx芯片会有。因此,必须使用large memory model。在CCS中,选择large memory model可到菜单Project--Build_Options,Compiler标签页,Advanced目录,选中-ml选项。非DSP/BIOS工程必须包含large memory model C编译器运行时支持库。对定点芯片而言是rts2800_ml.lib(与之对应,rts2800.lib支持small memory model),对浮点芯片而言,C代码使用rts2800_fpu32.lib,C++代码使用rts2800_fpu32_eh.lib(浮点芯片没有支持small memory model的库)。DSP/BIOS工程中,DSP/BIOS会自动包含必要的库,用户不应手动包含任何运行时库。

3.1 非DSP/BIOS工程

编译器使用一些指定的段。不论从RAM中运行还是从flash中运行,这些段都是一样的。然而,从flash中运行时,所有初始化段必须链接到non-volaile存储器,所有非初始化段必须链接到volatile存储器。Table 1展示了将编译器生成的F28xxx DSP段链接到哪里。各个段的功能见参考文献5。用户创建的所有初始化段必须链接到flash(例如,使用CODE_SECTION compiler pragma创建的段),所有非初始化段必须链接到RAM(例如,使用DATA_SECTION complier pragma创建的段)。
【Table 1】

Table 1注释:
.reset段只能包含指向运行时支持库(_c_int00 routine)中C编译器引导函数的32bit中断向量。通常情况下不会用到它,而是用户通常会创建自己的分支指令指向代码的starting point(见第6章和第7章)。当不使用.reset段时,必须在cmd文件中使用DSECT修饰符将它从代码build中忽略掉。例如:
/********************************
* User's linker command file
*********************************/
SECTIONS
{
        .reset        :>FLASH,    PAGE = 0,  TYPE = DSECT
}

3.2 DSP/BIOS工程

DSP/BIOS配置工具的memory section manager允许用户为各个DSP/BIOS和C编译器生成的段指定链接位置。Table 2指明了memory section manager中每个选项卡的段链接到何处(RAM或flash)。注意本表专为DSP/BIOS v5.33所制。DSP/BIOS的新版本可能有所不同。读者要检查自己的DSP/BIOS版本,留心处理时潜在的不同点。检查CCS的版本的方法是:(略)

【Table 2】

Table 2注释:
1 .trcdata段必须在运行时由用户从加载地址(Load_Address选项卡指定)拷贝到它的运行地址(BIOS_Data选项卡指定)。拷贝操作的细节参见4.3节。

2 PIEVECT RAM是与外设中断扩展(PIE)外设关联的特定RAM块。在目前的F28xxx芯片中,PIE RAM是从数据空间的0x000D00开始的256×16的块。其他芯片需要确认地址。DSP/BIOS配置工具的memory section manager应该已经预定义了一个名叫PIEVECT的内存块。.hwi_vec段必须在运行时由用户从加载地址(Load_Address选项卡指定)拷贝到它的运行地址(BIOS_Code选项卡指定)。拷贝操作的细节参见4.2节。

3 被选作本段加载地址的指定flash存储区必须与先前被选作该段运行地址的flash存储区相同(例如,在BIOS_Data, BIOS_Code或Compiler_Section选项卡中)。


4 将段从Flash拷贝到RAM

4.1 拷贝中断向量表(仅非DSP/BIOS工程)

F28xxx芯片的PIE模块管理中断请求。上电时,所有中断向量必须位于non-volatile存储区(即flash),且被你的代码在设备初始化时拷贝到PIEVECT RAM。在目前的F28xxx芯片中,PIE RAM是从数据空间的0x000D00开始的256×16的RAM块。

有几种方法将中断向量链接到flash并在运行时把它们拷贝到PIEVECT RAM。一个方法是创建一个C函数指针结构体常量,该结构体包含所有128个32bit向量。如果使用DSP28xxx外设结构体(见参考文献15-18),则结构体PieVectTableInit已经在DSP28xxx_PieVect.c中创建了。由于使用了const修饰符,它将被编译器定位到.econst段。你只要简单的在运行时将这个结构体拷贝到PIEVECT RAM。C编译器运行时支持库包含一个内存拷贝函数memcpy(),可以完成这个拷贝任务。这个函数使用例子如下:

/*********************************************************************
* User's linker command file
*********************************************************************/
/*********************************************************************
* NOTE: This function assumes use of the DSP28xxx Peripheral Header
* File structures (see References [15-18]).
*********************************************************************/
#include <string.h>

void main(void)
{
/***Initialize the PIE_RAM***/
        PieCtrlRegs.PIECTRL.bit.ENPIE = 0;    // Disable the PIE
        asm(" EALLOW");    // Enable EALLOW protected register access
        memcpy((void *)0x000D00, &PieVectTableInit, 256);
        asm(" EDIS");    // Disable EALLOW protected register access
}

上面的例子使用立即数0x000D00作为PIE RAM的起始地址。如果不想这么做(菜鸟才用立即数地址),可以使用DATA_SECTION pragma创建一个无初始值的伪变量,把这个变量链接到PIE RAM。然后可以用这个伪变量的名字取代立即数地址。例如,使用DSP28xxx芯片外设结构体时,可以创建一个无初始值的结构体PieVectTable,把它链接到PIEVECT RAM。则上面例子中的memcpy()语句可以这么写:

        memcpy(&PieVectTable, &PieVectTableInit, 256);

注意长度是256。memcpy函数拷贝的单位是16bit的字(128个32bit的字 == 256个16bit的字)。

4.2 拷贝.hwi_vec段(仅DSP/BIOS工程)

DSP/BIOS的.hwi_vec段包含中断向量,必须加载到flash并且从RAM运行。用户须负责将该段从加载地址拷贝到运行地址。拷贝过程通常放在main()中。DSP/BIOS会生成一些全局符号帮助用户确定加载地址、运行地址和hwi_vec段的长度。这些符号是:
        hwi_vec_loadstart
        hwi_vec_loadend
        hwi_vec_runstart
符号表示什么意思一看便知。注意这些符号不是指针,而是.hwi_vec段位置(即start和end)的16bit数据的符号表示。C编译器运行时支持库包含一个内存拷贝函数memcpy(),可以完成这个拷贝任务。在C语言中使用这个函数的例子如下。注意PIEVECT RAM是EALLOW保护的,因此必须用EALLOW和EDIS内联汇编语句将.hwi_vec段拷贝语句包围起来。

/*********************************************************************
* User's C-source file
*********************************************************************/
#include <string.h>

extern unsigned int hwi_vec_loadstart;
extern unsigned int hwi_vec_loadend;
extern unsigned int hwi_vec_runstart;

void main(void)
{
/***Initialize the .hwi_vec section***/
        asm(" EALLOW");    /* Enable EALLOW protected register access */
        memcpy(&hwi_vec_runstart,
               &hwi_vec_loadstart,
               &hwi_vec_loadend - &hwi_vec_loadstart);
        asm(" EDIS");     /* Disable EALLOW protected register access */
}

4.3 拷贝.trcdata段(仅DSP/BIOS工程)

DSP/BIOS的.trcdata段必须加载到flash并且从RAM运行。用户须负责将该段从加载地址拷贝到运行地址。然而与.hwi_vec段不同,拷贝.trcdata段必须在main()之前进行。这是因为DSP/BIOS初始化时会更新.trcdata的内容(在main()之前进行)。

为在main()之前执行.trcdata段拷贝和DSP/BIOS初始化,DSP/BIOS配置工具提供了一个用户初始化函数。在System-Global Settings的Properties中,见Figure 1.

【Figure 1】

接下来要创建用户初始化函数。DSP/BIOS配置工具生成一些供代码访问的全局符号,以确定加载地址、运行地址和每个段的长度。这些符号的名字是:
        trcdata_loadstart
        trcdata_loadend
        trcdata_runstart
符号表示什么意思一看便知。注意这些符号不是指针,而是段位置(即start和end)的16bit数据的符号表示。C编译器运行时支持库包含一个内存拷贝函数memcpy(),可以完成这个拷贝任务。在C语言中使用这个函数的例子如下。

/********************************
* User's C-source file
*********************************/
#include <string.h>

extern unsigned int trcdata_loadstart;
extern unsigned int trcdata_loadend;
extern unsigned int trcdata_runstart;

void UserInit(void)
{
/***Initialize the .trcdata section before main()***/
        memcpy(&trcdata_runstart,
               &trcdata_loadstart,
               &trcdata_loadend - &trcdata_loadstart);
}


4.4 初始化Flash控制寄存器(DSP/BIOS工程和非DSP/BIOS工程)

不能在flash中初始化flash控制寄存器FOPT、FPWR、FSTDBYWAIT、FACTIVEWAIT、FBANKWAIT和FOTPWAIT,否则后果不可预料。因此初始化flash控制寄存器的函数必须在运行时从flash(加载地址)拷贝到RAM(运行地址)。

警告
flash控制寄存器被代码安全模块(CSM)保护,如果CSM被保护,则只能从CSM安全RAM(例如L0到L3 SARAM,见芯片手册)中运行flash寄存器初始化代码,否则将不能访问flash寄存器。注意,在芯片重启时,CSM总是被保护的。使用伪密码0xFFFF可使ROM bootloader解除CSM保护。

可使用C编译器的CODE_SECTION pragma为flash初始化函数创建一个可独立链接的段。例如,假设C函数InitFlash()执行flash寄存器初始化,用户希望把这个函数链接到secureRamFuncs段,则下面的C代码例子展示了如何按照一个flash寄存器配置例子使用CODE_SECTION pragma。

/*********************************************************************
* User's C-source file
*********************************************************************/
/*********************************************************************
* NOTE: The InitFlash() function shown here is just an example of an
* initialization for the flash control registers. Consult the device
* datasheet for production wait state values and any other relevant
* information. Wait-states shown here are specific to current F280x
* devices operating at 100 MHz.
* NOTE: This function assumes use of the DSP28xxx Peripheral Header
* File structures (see References [15-18]).
*********************************************************************/
#pragma CODE_SECTION(InitFlash, "secureRamFuncs")
void InitFlash(void)
{
        asm(" EALLOW");    /* Enable EALLOW protected register access */
        FlashRegs.FPWR.bit.PWR = 3;                     // Flash shet to active mode
        FlashRegs.FSTATUS.bit.V3STAT = 1;               // Clear the V3STAT bit
        FlashRegs.FSTDBYWAIT.bit.STDBYWAIT = 0x01FF;    // Sleep to standby cycles
        FlashRegs.FACTIVEWAIT.bit.ACTIVEWAIT = 0x01FF;  // Standby to active cycles
        FlashRegs.FBANKWAIT.bit.RANDWAIT = 3;           // F280x Random access wait states
        FlashRegs.FBANKWAIT.bit.PAGEWAIT = 3;           // F280x Paged access wait states
        FlashRegs.FOTPWAIT.bit.OTPWAIT = 5;             // F280x OTP wait states
        FlashRegs.FOPT.bit.ENPIPE = 1;                  // Enable the flash pipeline
        asm(" EDIS");     /* Disable EALLOW protected register access */
       
/*** Force a complete pipeline flush to ensure that the write to the last register
     configured occurs before returning. Safest thing is to wait 8 full cycles. ***/
       
        asm(" RPT #6 || NOP");
       
} // end of InitFlash()

然后可以用cmd文件链接secureRamFuncs段了。该段的加载地址和运行地址需分离开。我们还希望获得链接器生成的全局符号,以确定加载地址、运行地址和段长度。在拷贝过程中需要这些信息。cmd文件将是下面这个样子:

/*********************************************************************
* User's linker command file
*********************************************************************/

SECTIONS
{
/*** User Defined Sections ***/
secureRamFuncs:    LOAD = FLASH,        PAGE = 0
                   RUN = SECURE_RAM,    PAGE = 0
                   RUN_START(_secureRamFuncs_runstart),
                   LOAD_START(_secureRamFuncs_loadstart),
                   LOAD_END(_secureRamFuncs_loadend)
}

在这个例子中,假设存储区FLASH和SECURE_RAM已经在user linker command file中MEMORY段中定义(非DSP/BIOS工程),或已经在DSP/BIOS配置工具的memory section manager中定义(DSP/BIOS工程)。这些存储区的PAGE应该符合定义。上面的例子假设2个存储区都在PAGE0(程序存储区)声明。RUN_START、LOAD_START和LOAD_END指令会为相关地址按指定名称生成全局符号。注意全局符号定义开始处的下划线(例如_secureRamFuncs_runstart)。

最后,把段从flash拷贝到RAM。如4.1节-4.3节的描述那样,可以使用编译器运行时支持库的memcpy()函数。

/*********************************************************************
* User's C-source file
*********************************************************************/
#include <string.h>

extern unsigned int secureRamFuncs_loadstart;
extern unsigned int secureRamFuncs_loadend;
extern unsigned int secureRamFuncs_runstart;

void main(void)
{
/*** Copy the secureRamFuncs section ***/
        memcpy(&secureRamFuncs_runstart,
               &secureRamFuncs_loadstart,
               &secureRamFuncs_loadend - &secureRamFuncs_loadstart);

/* Initialize the on-chip flash registers */
        InitFlash();
}

4.5 通过在RAM中执行时间苛刻的函数使性能最大化(DSP/BIOS工程和非DSP/BIOS工程)

当前F28xxx芯片片内RAM可达到的代码执行性能是:150MHz——150MIPS,100MHz——100MIPS,60MHz——60MIPS。然而,片内flash可达到的性能要少一些:150MHz——90-100MIPS,100MHz——85-90MIPS,60MHz——55MIPS。因此可能希望在片内RAM中执行一些时间苛刻或计算量大的程序。然而,在独立嵌入式系统中,所有代码预先都是放在non-volatile存储区中的,因此,需要为将在RAM中执行的代码建立加载地址和运行地址,并在运行时将代码从片内flash搬移到RAM中。步骤见4.4节。

使用CODE_SECTION pragma,用户可以将多个函数链接到同一段。然后整个段可以指派为从一个特定RAM块中运行。像4.4节讨论的那样,用户可以一次将整个段拷贝到RAM中。如果希望更好的链接粒度(granularity),也可以为每个函数创建各自的段。

4.6 通过将严苛的(Critical)全局常量链接到RAM中使性能最大化(DSP/BIOS工程和非DSP/BIOS工程)

C语言中有const修饰符的数据结构称为常量。在large memory model下,编译器将所有常量放在.econst段中。在当前F28xxx芯片中,即使通过特定管道为执行代码进行了有效的flash性能加速,在访问片内flash中的数据常量时,每次访问也都要花费多个周期。典型的flash等待状态为:150MHz芯片是5个周期,100MHz芯片是3个周期,60MHz芯片是2个周期(查阅芯片手册上的flash等待周期指标)。因此可能希望将频繁访问的常量或常量表放在片内RAM中。然而,在独立嵌入式系统中,所有代码预先都是放在non-volatile存储区中的,因此,需要为将放在RAM中的常量建立加载地址和运行地址,并在运行时将这些常量从片内flash搬移到RAM中。实现这一目标有2个不同的做法。

4.6.1 方法1:将所有常量放在RAM中

本方法为整个.econst段创建独立的加载地址和运行地址。本方法的优点是简单方便,缺点是RAM耗的太多了(可能只有几个常量是频繁访问的,但是本方法将所有常量都定位在RAM中)。

4.6.1.1 非DSP/BIOS工程

可以使用4.4节的办法。很简单:只要在user linker command file中为.econst段分别创建加载地址和运行地址,并写代码在运行时将整个.econst段拷贝到RAM就可以了。例:

/*********************************************************************
* User's linker command file
*********************************************************************/
SECTIONS
{
.econst         LOAD = FLASH,    PAGE = 0
                RUN = RAM,       PAGE = 1
                RUN_START(_econst_runstart),
                LOAD_START(_econst_loadstart),
                LOAD_END(_econst_loadend)
}

/*********************************************************************
* User's C-source file
*********************************************************************/
#include <string.h>

extern unsigned int econst_loadstart;
extern unsigned int econst_loadend;
extern unsigned int econst_runstart;

void main(void)
{
/*** Copy the .econst section ***/
        memcpy(&econst_runstart,
               &econst_loadstart,
               &econst_loadend - &econst_loadstart);
}

4.6.1.2 DSP/BIOS工程

虽然DSP/BIOS配置工具允许.econst段的加载地址和运行地址不同,但是不会生成拷贝时使用的符号。因此,用户需在DSP/BIOS处理cmd文件前将.econst段链接在user linker command file中。user linker command file如下所示:

/*********************************************************************
* User's linker command file (DSP/BIOS Projects)
*********************************************************************/
SECTIONS
{
/*** Preemptively link the .econst section ***/
/* Must come before DSP/BIOS linker command file is evaluated */
.econst         LOAD = FLASH,    PAGE = 0
                RUN = RAM,       PAGE = 1
                RUN_START(_econst_runstart),
                LOAD_START(_econst_loadstart),
                LOAD_END(_econst_loadend)
}

为保证工程build过程中先处理user linker command file再处理DSP/BIOS生成的cmd文件,用户必须在CCS中指定链接顺序。点击Project--Build_Options,选择Link_Order选项卡,指定链接顺序。Figure 2展示了一个例子,在这个例子中,F2808_BIOS_flash.cmd是user linker command file,F2808_example_BIOS_flashcfg.cmd是DSP/BIOS生成的cmd文件。
【Figure 2】

注意,由于DSP/BIOS生成的cmd文件也试图链接.econst段,链接器会给出一个警告“Multiple definitions of SECTION named '.econst'.”.这个警告可以忽略,不会引起问题。

如下代码会将.econst段从加载地址拷贝到运行地址:

/*********************************************************************
* User's C-source file
*********************************************************************/
#include <string.h>

extern unsigned int econst_loadstart;
extern unsigned int econst_loadend;
extern unsigned int econst_runstart;

void main(void)
{
/*** Copy the .econst section ***/
        memcpy(&econst_runstart,
               &econst_loadstart,
               &econst_loadend - &econst_loadstart);
}

4.6.2 方法2:将部分常量放在RAM中(DSP/BIOS工程和非DSP/BIOS工程)

这种方法将常量在运行时有选择地从flash拷贝到RAM中。步骤类似方法1,但只有被选中的常量才会被放到一个已命名的段并拷贝到RAM中(而不是把所有常量拷贝到RAM中)。

例如,设想用户创建的5 word常量数组table[ ]想放到RAM中。使用DATA_SECTION pragma将table[ ]放在用户定义的ramconsts段中。C代码如下:

/*********************************************************************
* User's C-source file
*********************************************************************/
#pragma DATA_SECTION(table, "ramconsts")
const int table[5] = {1,2,3,4,5};

void main(void)
{
}

使用user linker command file,ramconsts段就被链接到flash加载地址和RAM运行地址,也会生成全局符号供拷贝用。user linker command file如下:

/*********************************************************************
* User's linker command file
*********************************************************************/
SECTIONS
{
/*** User Defined Sections ***/
ramconsts:      LOAD = FLASH,    PAGE = 0
                RUN = RAM,       PAGE = 1
                LOAD_START(_ramconsts_loadstart),
                LOAD_END(_ramconsts_loadend),
                RUN_START(_ramconsts_runstart)
}

最后,运行时必须将table[ ]从加载地址拷贝到运行地址:

/*********************************************************************
* User's C-source file
*********************************************************************/
#include <string.h>

extern unsigned int ramconsts_loadstart;
extern unsigned int ramconsts_loadend;
extern unsigned int ramconsts_runstart;

void main(void)
{
/*** Copy the ramconsts section ***/
        memcpy(&ramconsts_runstart,
               &ramconsts_loadstart,
               &ramconsts_loadend - &ramconsts_loadstart);
}

5 对代码安全模块密码编程(DSP/BIOS工程和非DSP/BIOS工程)

F28xxx芯片的CSM模块可以防止别人非法获取你的固件。当前的F28xxx芯片中,整个flash,OTP,L0-L3 SARAM都被CSM保护(flash配置寄存器也被CSM保护)。当保护生效时,只有被保护区域的代码才能访问被保护区域的数据。未保护区域的代码不能访问被保护区域的数据。CSM模块的详细信息见参考文献6-8.

CSM使用由8个独立的16bit密码组成的128bit密码。在当前F28xxx芯片中,密码保存在flash最高的8个字中(例如,对F281x,F280x和F280xx是地址0x3F7FF8到0x3F7FFF;对F2833x是地址0x33FFF8到0x33FFFF)。开发过程中,推荐使用伪密码0xFFFF。当使用伪密码时,只要伪读(dummy read)密码区域就能解锁CSM。在flash编程时,擦除操作后密码区域就全是0xFFFF了,这可以作为将密码区域设置为伪密码的手段。用户需要避免将任何段链接到密码区域,这样密码就会保持为0xFFFF。

开发完成后,可能会希望启用密码。另外,为了保护F28xxx的CSM模块,CSM模块前的120字开始,连续118个flash地址应该写入0x0000,例如,对F281x,F280x和F280xx是地址0x3F7F80到0x3F7FF5;对F2833x是地址0x33FF80到0x33FFF5(见参考文献1-3)。要完成启用密码和写入0x0000这两个任务,有一个简单的方法,只需要一些简单的汇编语句。下面的例子中,汇编代码指定了一个密码,并将密码放在passwords段中。这段代码还创建了csm_rsvd段,这个段包含所有的0x0000,长度是刚才提到的118个字。关于汇编语言指令的详细信息见参考文献9.

***********************************************************************
* File: passwords.asm
***********************************************************************
***********************************************************************
* Dummy passwords of 0xFFFF are shown. The user can change these to
* desired values.
*
* CAUTION: Do not use 0x0000 for all 8 passwords or the CSM module will
* be permanently locked. See References [6-8] for more information.
***********************************************************************
      .sect "passwords"
      .int   0xFFFF        ;PWL0 (LSW of 128-bit password)
      .int   0xFFFF        ;PWL1
      .int   0xFFFF        ;PWL2
      .int   0xFFFF        ;PWL3
      .int   0xFFFF        ;PWL4
      .int   0xFFFF        ;PWL5
      .int   0xFFFF        ;PWL6
      .int   0xFFFF        ;PWL7 (MSW of 128-bit password)
;----------------------------------------------------------------------
      .sect "csm_rsvd"
      .loop (3F7FF5h - 3F7F80h + 1)
      .int   0x0000
      .endloop
;----------------------------------------------------------------------

      .end                 ;end of file passwords.asm
     
注意这个例子展示了伪密码0xFFFF。将0xFFFF替换为你希望的密码就可以实现保护了。

警告:
不可使用0x0000作为8个密码,否则会永久锁死CSM模块!见参考文献6-8.

passwords段和csm_rsvd段要用user linker command file定位。

对非DSP/BIOS工程,用户应在user linker command file的MEMORY区的PAGE 0定义PASSWORDS存储区和CSM_RSVD存储区(对上面例子而言)。然后passwords段和csm_rsvd段就会被链接到这两个存储区。下面的例子也适用于当前F28xxx系列芯片(未来芯片需参考芯片手册,确认密码区和CSM保留区地址)。

/**********************************************************************
* User's linker command file (non-DSP/BIOS Projects)
**********************************************************************/
MEMORY
{
PAGE 0:    /* Program Memory */
   CSM_RSVD    : origin = 0x3F7F80, length = 0x000076
   PASSWORDS   : origin = 0x3F7FF8, length = 0x000008
PAGE 1:    /* Data Memory */
}

SECTIONS
{
/*** Code Security Password Locations ***/
passwords:     >PASSWORDS,    PAGE = 0
csm_rsvd:      >CSM_RSVD,     PAGE = 0
}

对DSP/BIOS工程,用户应使用DSP/BIOS配置工具的memory section manager定义PASSWORDS存储区和CSM_RSVD存储区(对上面例子而言)。下面2幅图片显示了当前F28xxx芯片的这2个存储区在DSP/BIOS memory section manager中的属性。对其他的芯片,需参考芯片手册,确认密码区和CSM保留区地址。
【Figure 3】
【Figure 4】

然后,通过user linker command file就可以将passwords和csm_rsvd段链接到这些存储区。对DSP/BIOS工程,user linker command file如下所示:

/**********************************************************************
* User's linker command file (DSP/BIOS Projects)
**********************************************************************/
SECTIONS
{
/*** Code Security Password Locations ***/
passwords:     >PASSWORDS,    PAGE = 0
csm_rsvd:      >CSM_RSVD,     PAGE = 0
}

6 DSP重启后从Flash执行你的代码(DSP/BIOS工程和非DSP/BIOS工程)

F28xx芯片含有一个ROM bootloader,在芯片复位后,它可将代码执行权交给flash。关于ROM bootloader的细节见参考文献10-12.当引导模式选择引脚被配置为"Jump to Flash"模式时,ROM bootloader会跳转到位于flash中0x3F7FF6地址的指令(这是对F280x和F281x而言,别的芯片需参考芯片手册)。用户需在这个地址放置一条跳转到自己代码的指令。回忆一下,CSM密码区从地址0x3F7FF8开始(对F280x和F281x而言),所以这个跳转指令只能是2个word长。一个长跳转指令(LB汇编码)占用2个word。

一般情况下,这条跳转指令都会跳到C编译器运行时支持库中的C语言环境初始化routine的开始处。这个routine的entry符号是_c_int00。只有在这个routine执行后,才能执行C代码。有时需要在C代码执行前运行一些汇编代码(比如关闭看门狗定时器)。这种情况下就要跳转到你的汇编代码开始处。当然,这条跳转指令也要放在flash中。最简单的做法就是使用汇编代码。下面的一个例子创建了一个codestart段,该段包含一条跳转到C语言设置routine的长跳转指令。需要使用user linker command file定位codestart段。

**********************************************************************
* CodeStartBranch.asm
**********************************************************************
      .ref _c_int00
     
      .sect "codestart"
      LB _c_int00             ;branch to start of code
     
      .end                    ;end of file CodeStartBranch.asm
     
对非DSP/BIOS工程,用户需在user linker command file的MEMORY部分的PAGE 0中定义一个存储区,例如BEGIN_FLASH存储区。然后将codestart段链接到这个存储区。下面的例子适用于F281x,F280x,以及F280xx芯片。对其他F28xxx芯片,需阅读芯片手册以确定boot to flash的目标地址。

/**********************************************************************
* User's linker command file (non-DSP/BIOS Projects)
**********************************************************************/
MEMORY
{
PAGE 0:    /* Program Memory */
   BEGIN_FLASH : origin = 0x3F7FF6, length = 0x000002
PAGE 1:    /* Data Memory */
}

SECTIONS
{
/*** Jump to Flash boot mode entry point ***/
codestart:      > BEGIN_FLASH,  PAGE = 0
}

对DSP/BIOS工程,用户需使用DSP/BIOS配置工具的memory section manager定义一个存储区,例如BEGIN_FLASH存储区。Figure 5展示了F281x,F280x,以及F280xx芯片中该存储区在memory section manager中的属性。对其他芯片,需阅读芯片手册以确定boot to flash的目标地址。
【Figure 5】

然后在user linker command file中将codestart段链接到这个存储区。DSP/BIOS工程的cmd文件应该如下所示:

/**********************************************************************
* User's linker command file (DSP/BIOS Projects)
**********************************************************************/
SECTIONS
{
/*** Jump to Flash boot mode entry point ***/
codestart:      > BEGIN_FLASH,  PAGE = 0
}

7 在引导C语言环境时禁止看门狗(DSP/BIOS工程和非DSP/BIOS工程)

C编译器运行时支持库中的C语言环境初始化函数_c_int00执行全局和静态变量的初始化工作。其中包括对初始化全局变量所做的从.cinit段(位于片内flash中)到.ebss段(位于RAM中)的数据拷贝。例如,在源代码中声明一个全局变量:
        int x=5;
数字“5”被放在.cinit初始化段中,而符号“x”保存在.ebss段中。然后_c_int00会在运行时将“5”拷贝到“x”位置处。当软件中存在大量的初始化全局变量和静态变量时,由于C语言环境引导routine执行时间太长,使得从上电到调用main()进行看门狗配置/喂狗/禁止的时间太长,从而有可能导致看门狗超时。在RAM版本代码开发中可能不会发现这个问题,因为从链接到RAM的.cinit段拷贝数据是很快的。但是,当.cinit段被链接到片内flash时,由于片内flash在上电时wait-states周期数是其最大值(只有进入main()后才能由用户代码对wait-states周期数进行配置),因此每一个数据的拷贝都会消耗很多周期。另外,进行数据拷贝的代码是从flash中执行的,这又额外增加了时间(取代码和取数据都要访问flash,它们会竞争)。与此同时,上电时看门狗超时时限默认是最小值,因此看门狗超时非常可能发生。

使用CCS有一个简单的方法可以检测到这个情况。要检测看门狗是否超时:
1. 程序烧写到flash后,加载代码符号(点击File-->Load_Symbols-->Load_Symbols_Only).
2. 复位DSP(点击Debug-->Reset_CPU)
3. 重启DSP(点击Debug-->Restart)(如果bootloader被配置为“Jump to Flash”模式,则不需要这一步)
4. 运行到main()(点击Debug-->Go_Main)。如果没有来到main(),就很可能在C语言环境引导routine完成之前发生了看门狗超时。

解决看门狗超时带来的问题最简单的方法就是在C语言环境引导routine开始之前禁止看门狗。进入main()后执行用户代码时,可以重新使能看门狗。将WDCR寄存器WDDIS位置1就禁止了看门狗。要在引导routine开始之前禁止看门狗,必须使用汇编代码(因为C语言环境尚未建立)。在第6章,codestart汇编代码段执行了向C语言环境初始化routine——_c_int00——的跳转指令。要禁止看门狗,就要先跳转到禁止看门狗的代码,然后再跳转到_c_int00。下面的例子代码执行了这些任务:

***********************************************************************
* File: CodeStartBranch.asm
* Devices: TMS320F28xxx
* Author: David M. Alter, Texas Instruments Inc.
* History: 02/11/05 - original (D. Alter)
***********************************************************************

WD_DISABLE .set 1  ;set to 1 to disable WD, else set to 0

    .ref _c_int00

***********************************************************************
* Function: codestart section
* Description: Branch to code starting point
***********************************************************************
    .sect "codestart"
    .if WD_DISABLE == 1
        LB wd_disable       ;Branch to watchdog disable code
    .else
        LB _c_int00         ;Branch to start of boot.asm in RTS library
    .endif
;end codestart section

***********************************************************************
* Function: wd_disable
* Description: Disables the watchdog timer
***********************************************************************
    .if WD_DISABLE == 1

    .text
wd_disable:
    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 _c_int00         ;Branch to start of boot.asm in RTS library

    .endif

;end wd_disable
***********************************************************************

 .end                   ; end of file CodeStartBranch.asm

8 C代码样例【略】

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
技术心得、资料备忘、自翻译文档。
最近文章
NE555工作原理
2010-06-19 10:34:01
讲CMD格式的文档
2010-06-11 10:00:18
DSP/BIOS空间裁剪
2010-06-10 09:37:44
推荐文章
最近访客