C28x DSP程序加载与运行

C28x DSP程序加载与运行

文章目录

  • C28x DSP程序加载与运行
  • 1 C28x DSP程序加载和运行
    • 1.1 程序加载
      • 1.1.1 加载地址和运行地址
      • 1.1.2 引导加载程序
    • 1.2 程序入口地址
    • 1.3 Run-Time初始化
      • 1.3.1 _c_int00()函数
      • 1.3.2 RAM模型与ROM模型
        • 1.3.2.1 在运行时自动初始化变量(——rom_model)
        • 1.3.2.2 加载时初始化变量(——ram_model)
        • 1.3.2.3 -rom_model和-ram_model链接器选项
    • 1.4 Run-Time重定向
    • 1.5 参考文献

1 C28x DSP程序加载和运行

1.1 程序加载

在DSP复位后,执行程序之前,需要将程序放入目标设备的内存中,这就是程序的加载。加载过程初始化了设备内存、程序代码和数据,完成程序执行的准备工作。程序加载完成后,CPU会跳转到相应的入口地址继续执行程序。

加载程序可能是设备上的另一个程序,一个外部代理(例如,一个调试器),或者设备可能在开机后初始化自己,这称为引导加载(BootLoader)。

加载程序负责在程序启动之前在内存中构建加载镜像。加载镜像是程序执行前内存中的代码和数据。加载程序的组成部分取决于环境,例如操作系统是否存在。在使用COFF RAM模型时,加载程序负责解析.cinit部分,并在加载时执行.cinit代码的初始化。

程序可以通过以下方式加载:

  • 在连接调试器的主机工作站上运行:在典型的嵌入式开发设置中,设备从属于运行调试器(如Code Composer Studio (CCS))的主机。该设备与主机通过调试接口(如JTAG接口)连接。CCS读取程序并通过调试接口将加载镜像直接写入目标内存。
  • 将加载镜像“刻录”到EPROM模块上:hex转换器(hex2000)可以将可执行的目标文件转换成适合EPROM的格式,EPROM集成在设备中并成为设备内存的一部分。
  • 专用外设(如SCI外设)引导加载:在设备复位时,首先会运行BOOT ROM中的程序,该程序会通过判断BOOT引脚的状态来确定从FLASH启动还是RAM启动,或者是执行外设引导程序。BOOT ROM中有多个外设引导加载程序,通过配置BOOT引脚可以选择复位后执行哪个外设引导加载程序。C2Pro烧录工具就是使用SCI引导加载程序加载.hex文件的。
  • 设备上运行的另一个程序:正在运行的程序可以加载镜像并跳转到新加载的程序中继续执行。如果使用了操作系统,该操作系统可能具有加载和运行程序的能力。

1.1.1 加载地址和运行地址

加载地址是镜像文件中给数据对象分配的存储地址,运行地址是数据对象在程序执行期间存在的地址。(数据对象是一块内存,它表示一个Sector、段、函数或变量等数据)。

  • 程序代码和只读数据的加载地址和运行地址是相同的,如.econst Sector和.text Sector。在这种情况下,程序可以直接从加载地址读取数据。

  • 可写数据(如:变量)的加载地址和运行地址是不同的,如:.data Sector,.data Sector的起始内容加载到ROM中,运行时复制到RAM中。复制通常发生在程序启动期间(_c_int00启动函数自动完成,如:变量的初始化),但也可能是在启动后的某个时刻由用户显示实现(如:某个函数在FLASH中加载,在RAM中运行)。

  • 没有初始值的Sector,如.ebss Sector没有加载地址,或者说加载地址和运行地址相同。如果为未初始化的数据指定不同的加载地址和运行地址,链接器将提供警告并忽略加载地址。

汇编代码和目标文件中的符号几乎总是指向运行地址。当查看程序中的地址时,几乎总是在查看运行地址除了初始化之外,很少使用加载地址

一个Sector的加载地址和运行地址由链接命令文件(CMD文件)控制,并记录在目标文件中。加载地址决定加载程序将该Sector的原始数据存放在何处。运行地址决定了程序运行时该Sector的地址。对该Sector的任何引用(例如对其中的变量的引用)都指向它的运行地址。如果在CMD文件中为Sector指定了不同的的运行地址和加载地址,在调用该Sector中的数据之前,用户程序必须将该Sector从其加载地址复制到其运行地址。复制不会自动发生,所以需要在用户程序中显式地完成复制过程。

程序中的全局变量是可写的,所以全局变量必须位于可写内存中,通常是RAM中。然而,RAM是不稳定的,这意味着当断电时,它将失去数据。如果该数据有初始值,那么该初始值必须存储在非易失性内存中,如FLASH。在使用初始值之前,必须将初始值从非易失性ROM(如:FLASH)复制到RAM中。

1.1.2 引导加载程序

在设备复位时,首先会从BOOT ROM中获取复位中断向量,然后根据复位中断向量运行BOOT ROM中的程序,该程序会通过判断BOOT引脚的状态来确定从FLASH启动还是RAM启动,或者是执行外设引导加载程序。BOOT ROM中有多个外设引导加载程序,通过配置BOOT引脚可以选择复位后执行哪个外设引导加载程序。

在不同设备之间,引导加载(bootloader)程序的细节差别很大。不是每台设备都支持每一种引导加载模式,使用引导加载程序是可选的。本节讨论各种引导加载模式,以帮助您理解它们的工作方式。请参考您的设备的数据表,以了解哪些引导加载方案是可用的,以及如何使用它们。

  • 典型的嵌入式系统使用bootloader程序来初始化设备。程序代码和数据可以存储在ROM或闪存中。在开机时,内置在设备硬件中的片上bootloader程序自动启动。

图1-1引导加载过程
C28x DSP程序加载与运行_第1张图片

  • 对于许多程序,主bootloader程序不能加载整个程序,因此这些程序提供了一个更有能力的辅助bootloader程序。主bootloader加载辅助bootloader程序,然后跳转到辅助bootloader程序。然后,辅助bootloader程序加载用户程序,并跳转到用户程序。可以有任意数量的引导加载程序层,每层加载一个功能更强的bootloader程序,并跳转到新加载的bootloader程序中。

​ 图 1-2 辅助引导程序引导加载过程
C28x DSP程序加载与运行_第2张图片

1.2 程序入口地址

程序入口地址是程序开始执行的地址,是启动程序的地址。启动程序负责完成初始化并调用程序的其余部分。对于一个C/C++程序,启动程序通常命名为c_int00。程序加载完成后,入口地址c_int00的值被放置在PC寄存器中,CPU从PC寄存器中获取入口地址_c_int00并开始执行,实现C/C++环境的初始化

目标文件有一个入口地址字段。对于C/C++程序,链接器默认将c_int00填充到入口地址中。用户也可以选择一个自定义入口地址,如在某些程序中入口地址是code_start,然后在code_start中跳转到c_int00。

MEMORY
{
PAGE 0 : 
        START            : origin = 0x080000, length = 0x000002
        
}
SECTIONS
{
        codestart        : > BEGIN       PAGE = 0, ALIGN(4)
      
}

以TMS320F28075为例,在上述CMD文件中创建一个名为codestart的Sector,并将该Sector放入START内存区域,START内存是FLASH的起始地址。默认情况下,TMS320F28075复位后先执行BOOT ROM中的程序,然后从FLASH开始启动,即从FLASH的起始地址START开始执行代码。

在F2807x_CodeStartBranch.asm文件中,使用.sect指令创建了一个名为codestart的Sector,该Sector中的代码在连接后会放入START中。设备复位后,从START处开始执行codestart中的代码,并且程序会从这里跳转到c_int00,然后在c_int00中调用主函数Main()并执行用户程序。

***********************F2807x_CodeStartBranch.asm**********************

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

    .ref _c_int00
    .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 _c_int00         ;Branch to start of boot._asm in RTS library
    .endif

;end codestart section
  • 如果使用引导加载程序(BootLoader),则引导表(如:.hex文件)包含一个入口地址字段。当引导加载程序完成程序加载后,将跳转到对应的入口地址。
  • 如果使用复位中断向量,则入口地址为复位中断处理程序。当设备复位时,启动程序将被调用
  • 如果使用调试器(如CCS),调试器可能会显式地设置程序计数器(PC)的入口地址的值。

1.3 Run-Time初始化

程序加载完成后就可以运行了,下面描述C/C++程序的初始化。汇编程序可能不需要执行这些步骤的全部。

1.3.1 _c_int00()函数

函数_c_int00是C/C++程序的启动程序,它执行C/C++程序初始化所需的所有步骤。

函数名称c_int00表示它是中断号为0的中断处理程序,用于复位,并配置C环境。该复位中断处理函数的名称不是一定要取名为c_int00,但是链接器默认将c_int00设置为C程序的入口地址。编译器的运行时支持库提供c_int00的默认实现。

启动程序c_int00负责执行以下操作:

  1. 设置状态和配置寄存器;
  2. 建立堆、栈;
  3. 处理.cinit初始化表,从而初始化需要初始化的全局变量(使用——rom_model选项,运行时初始化);
  4. 调用.init_array(用于EABI)或 .pinit(用于COFF)中的所有全局对象构造函数;
  5. 调用函数main;
  6. 当main函数返回值时退出程序;

1.3.2 RAM模型与ROM模型

ROM模型在启动程序c_int00期间执行更多的工作。RAM模型在加载应用程序时执行更多的工作。

如果您的应用程序需要频繁地复位,或者是一个独立的应用程序,那么ROM模型可能是更好的选择,因为启动程序c_int00将拥有初始化RAM变量所需的所有数据。但是,对于带有操作系统的设备,最好使用RAM模型。

  • COFF RAM模型中,加载程序首先负责处理.cinit Sector。此时.cinit Sector是一个NOLOAD部分,这意味着链接器不会为它分配内存。相反,加载器负责解析.cinit Sector,并在加载时执行其中代码的初始化。

  • COFF ROM和EABI ROM模型中,C启动程序c_int00将数据从.cinit Sector复制到变量的运行时位置。

  • 在EABI RAM模型中,在启动时不会生成.cinit记录。

1.3.2.1 在运行时自动初始化变量(——rom_model)

在运行时自动初始化变量是自动初始化的默认方法。要使用此方法,请使用——rom_model选项调用链接器。

ROM模型允许初始化数据存储在慢速非易失性内存(如:FLASH)中,并在每次程序复位时复制到快速内存(如:RAM)中。

  • 对于带有EABI的ROM模型,.cinit Sector与所有其他初始化Sector一起加载到内存中。链接器定义了一个特殊的符号,称为ti_cinit_base,它指向内存中初始化表的开头。当程序开始运行时,C启动程序c_int00将表(由.cinit指向)中的数据复制到变量的运行时位置。

  • 对于带有COFF的ROM模型,.cinit Sector与所有其他初始化Sector一起加载到内存中。链接器定义了一个称为cinit的特殊符号,它指向内存中初始化表的开头。当程序开始运行时,C启动程序c_int00从初始化表(由.cinit指向)中复制数据,并将数据赋值给.ebss Sector或用户自定义Sector中的指定变量。

​ 图 1-3 运行时自动初始化
C28x DSP程序加载与运行_第3张图片

1.3.2.2 加载时初始化变量(——ram_model)

RAM模型在加载时初始化变量。要使用此方法,请使用——ram_model选项调用链接器。此模型可以减少启动时间并节省初始化表使用的内存。

  • 当使用——ram_model链接器选项时,链接器会在.cinit Sector的标题中设置STYP_COPY位。这告诉加载程序不要将.cinit Sector加载到内存中(.cinit Sector在内存映射中不占空间)。

  • 对于COFF,链接器还将cinit符号设置为-1(通常,cinit指向初始化表的开头)。这C向启动程序c_int00表明,初始化表不存在于内存中,因此,在启动时不执行运行时初始化。

  • 对于EABI,链接器将_ ti_cinit_base设置为_ ti_cinit_limit,以表明不存在.cinit记录。

COFF加载程序必须能够执行以下任务,以便在加载时进行初始化:

  • 检测目标文件中是否存在.cinit Sector;
  • 确定.cinit Sector标头中的STYP_COPY被置位,这样加载器就知道不应该将.cinit Sector加载到内存中。
  • 了解初始化表的格式;

对于EABI,加载程序直接从.data Sector中复制数据到内存中。

​ 图 1-4 加载时初始化
C28x DSP程序加载与运行_第4张图片

1.3.2.3 -rom_model和-ram_model链接器选项

下面的列表概述了使用-ram_model或-rom_model选项调用链接器时发生的情况。

  • 符号_c_int00被定义为程序入口地址。_c_int00符号是boot. C .obj中的C启动程序的开始。引用_c_int00可以确保从适当的运行时支持库中将boot.c.obj自动链接进来。

  • 对于COFF, .cinit输出Sector有一个终止记录,以告诉C启动程序c_int00(运行时自动初始化)或加载程序(加载时初始化)何时停止读取初始化表。

  • 当使用RAM模型【加载时初始化】(-ram_model选项):

    • 对于EABI,链接器定义了一个称为ti_cinit_base的特殊符号,该符号指向内存中初始化表的开头。当程序开始运行时,C启动程序c_int00将表(由.cinit指向)中的数据复制到变量的运行时位置;
    • 对于COFF,链接器在.cinit Sector头中置位STYP_COPY标志(0010h),STYP_COPY是一个特殊的属性,它告诉加载程序直接执行初始化,而不要将.cinit Sector加载到内存中。链接器不为.cinit Sector分配内存空间;
    • 对于COFF,链接器将cinit设置为-1。这表明初始化表不在内存中,因此在c_int00运行时不执行初始化(加载时以完成初始化);
  • 当使用ROM模型【运行时自动初始化】(- rom_model选项):

    • 对于COFF,链接器将cinit定义为.cinit Sector的起始地址。C启动程序c_int00使用这个符号作为自动初始化的起点;
    • 对于EABI,链接器将_ ti_cinit_base设置为_ ti_cinit_limit,以表明不存在.cinit记录

注意:加载程序不是TMS320C28x C/C++编译器工具的一部分。请使用Code Composer Studio集成开发环境或其他工具作为加载器。

1.4 Run-Time重定向

用户可能希望将代码加载到内存的一个区域,并在运行之前将其移动到另一个区域。例如,在FLASH中有性能关键的代码,这些代码加载到FLASH中,但是它需要RAM中运行以获取更好的性能。

链接器提供了一种处理方法。使用SECTIONS指令,用户可以有选择地指导链接器为Sector分配两个地址:第一个地址是Sector的加载地址,第二个地址是Sector的运行地址。对加载地址使用load关键字,对运行地址使用run关键字。

如果一个Sector在链接时被分配了两个地址,那么该Sector中定义的所有标签都将被重定向到运行地址。

 ramfuncs1      : LOAD = FLASHD,
                  RUN = RAM0,
                  LOAD_START(_RamFuncs1LoadStart),
                  LOAD_END(_RamFuncs1LoadEnd),
                  RUN_START(_RamFuncs1RunStart),
                  PAGE = 0, ALIGN(4)

上述代码表示Sector ramfuncs1的加载地址是FLASHD,运行地址是RAM0。

如果仅为Sector分配一个地址,则该Sector在相同的地址加载和运行。未初始化的Sector(如.ebss或.bss)没有加载地址,因此惟一有效的地址是运行地址。链接器对未初始化的Sector只分配一个地址,如果用户同时指定了运行地址和加载地址,链接器会警告并忽略加载地址。

 .econst          : > FLASHA    PAGE = 0, ALIGN(4)

上述代码表示Sector .econst的加载地址和运行地址都是FLASHA。

1.5 参考文献

[1] TMS320C28x Assembly Language Tools

你可能感兴趣的:(TI,DSP,嵌入式)