摘要:TI高速信号处理器TMS320C5X和TMS320C6X需要从外部的存储器(ROM或FLASH)中引导应用程序这是开发中的重点和难点之一,关系到系统的可靠性和处理速度。以TMS320C6000 DSP为例,介绍了应用程序的三种引导方式;以实际工程为背景详细叙述了从ROM中引导程序的实现方法,并钟对其中重要的命令文件和用户引导程序,给出了相应的示例文件和程序。
关键词:TMS320C6000 DSP CMD 引导
1 TMS320C6000 DSP硬件结构概述TMS320C6000是TI公司生产的TMS320系列产品中新一代高性能的DSP芯片,适用于高速数字信号处理。TMS320C6000主要由三个部分组成:CPU内核、外设和存储器。CPU中8个功能单元可以并行工作,这些功能单元被分成类似的两组,每组由4个基本功能单元组成。CPU有两组寄存器,每组寄存器由16个32位寄存器组成。由于在运行期间不做硬件数据相关性检查,所以程序运行时可以同时执行8条指令,极大地提高了芯片处理速度,这使得该系列的芯片在电子测量、测控、图像、雷达、声纳和软件无线电等领域得到了广泛的应用。
2 加电后DSP的运行过程
系统加电后,RESET信号为低,芯片复位。在RESET信号上升沿处,锁存BOOTMODE[4:0]信号,借以决定芯片的存储器映射方式、地址0处的存储器类型以及复位后芯片的自举模式,复位结束后,芯片从存储器的0地址开始执行指令。
TMS320C6000器件可以设置成三种自举方式,其加载过程分别叙述如下:
(1)不加载。CPU直接从存储器的0地址处开始执行指令。如果系统中使用的是SDRAM,那么CPU会先挂起,直到SDRAM的初始化完成。TMS320C621X/C671X不具有这类方式。
(2)ROM加载。位于外部空间的ROM中的程序首先通过DMA/EDMA搬入地址0处。尽管加载过程是在芯片外部被复位信号释放以后才开始的,但是当芯片仍处于内部复位保持时,就开始了上述的传输过程了。用户可以指定外部ROM的存储宽度,EMIF会自动将相邻的8bit/16bit数据合并成32bit。ROM中的程序必须以little endian的格式存储。用DMA/EDMA进行的这一加载过程是一个单帧的数据块传输。传输过程完成之后,CPU退出复位状态,开始执行地址0处的指令。对于不同的芯片,这一过程略有不同,整个过程如图1所示。
对于TMS320C620X/C670X,DMA使用默认的ROM时序从CE1空间中拷贝64KB数据到地址0处。
对于TMS320C621X/C671X/C64X,EDMA使用默认的ROM时序从CE1空间(C64X从EMIFB CE1空间)拷贝1KB数据到地址0处。
(3)主机(HPI)引导。CPU停留在保持状态,其余硬件部分均保持正常状态。在这期间,外部主机通过主机口初始化CPU的存储空间。主机完成所有的初始化工作后,将主机口控制寄存器中的DSPINT位设置为1,结束引导过程。此时CPU退出复位状态,开始执行地址0处的指令。在主机引导过程中,主机可以对DSP所有的存储空间进行读和写。
此外,用户应选择存储器的映射方式(当有多种映射方式可供选择时)和首地址为0存储空间的类型。TMS320C6201/C6701芯片有专门的BOOTMODE管脚决定芯片的各种设置。TMS320C6202则通过将扩展总线上的XD[4:0]直接映射为BOOTMODE40确定芯片的设置,这些管脚可通过电阻上拉或下拉设置引导方式和存储器映射方式。TMS320C6211/C6711仅有一种存储器映射方式,引导方式只需要两位进行设置,主机口的HD[4:3}映射为BOOTMODE[4:0]同样是利用电阻上拉或下拉设置引导方式实现的。
图2 代码生成流程图
3 ROM引导模式实现
在许多基于TMS320C6000 DSP的应用程序的开发中,程序代码或数据表总是保存在ROM、 FLASH或其它非易失存储器中,以保证掉电时代码仍在。但这些存储器的速度很慢,对速度性能要求很严格的代码就需要运行到其它快速存储器中,如片内RAM或片外静态RAM。这就需要在运行前将代码从ROM存储器中装载到RAM中,这是开发人员遇到的难点之一。从ROM引导的应用程序的开发过程分以下几个步骤:
·在CCS环境中,调试程序,编写相应的命令文件(.cmd),编译连接以形成.hex(COFF格式)文件,通过编程器转化成.bin文件(二进制文件),烧到ROM中;
·系统加电复位,芯片从ROM中拷贝固定长度的数据块(其中包括用户自身的引导代码)到RAM中,用以初始化部分存储器;
·执行用户的引导代码和其它数据段以及程序段初始化所必需的段复制;
·使用包含.cinit段中的数据初始化.bss段中C语言变量;
·程序从main()处开始执行。
在开发从ROM引导的应用程序时,必须考虑以下几点:用户引导代码必须连接到应用程序中,这样在系统加电复位后,用户的引导程序才能被合适地加载和执行;只有通过编写自己的引导代码用户才能正确地从ROM中拷贝COFF格式的段;在程序连接时,只有通过合理编写命令文件(.cmd),使这些段被合理地连接后,才能实现从ROM中加载,从DSP内部RAM中执行。在这个过程中,命令文件和用户引导代码编写对于程序能否正确地执行起着非常重要的作用。在某软件无线电工程的开发中,笔者就是在C语言环境下,成功地开发了TMS320C6701的自引导程序。下面就针对这两个方面加以详细说明,以供大家借鉴。
3.1 命令文件
利用TI的代码产生工具,可以按照图2所示的步骤生成可执行文件(.out)。
汇编器接收汇编优化器或编译器产生的.asm文件,经过汇编后产生可重新分配地址的COFF格式的目标文件.obj。该格式文件包含了汇编器所生成的各个段(如表1所示),命令文件就是指导连接器如何将各段分配到相应的存储器中。编写命令文件时,有一点需要注意,那就是在很多情况下需要对某段说明两个不同的地址:加载地址和运行地址。加载地址决定了二进制程序代码的存储位置和程序的引导地址,但在运行过程中对于该段的任何引用则是以它的运行地址作为参考的。因此,当用户对某段分别说明了加载地址和运行地址时,只有将该段从加载地址复制到运行地址上,该段才可以被访问。
表1 编译器产生的默认代码段和数据段
段 名
段数据
段说明
建议分配方法
.text
代码段
程序代码
Load=ROM,Run=RAM
.switch
初始化数据段
Switch语句跳转表
Load=ROM,Run=RAM
.bss,.far
未初始化数据段
c变.运行时从.cinit中自动初始化
Load=Run=RAM
.cinit,.pinit
初始化数据段
C变量和函数初始化表
Load=Run=ROM
.const
初始化数据段
常量
Load=Run=ROM
.data,.cio,.sysmem
未初始数据段
其它的.data段
Load=Run=RAM在命令文件中,合理地分配.cinit段对于整个程序的正确运行起到关键的作用。在TI可查阅的文档和相关网站上,很少有涉及这方面内容的**。经过反复实践,笔者取得了处理.cinit段的一些经验,请大家在下面的命令文件和用户引导程序中注意该段的处理过程。
下面是在实践中编写的命令文件(.cmd)
-c ;说明复位后的初始化方式
-stack 0x5000 ;说明堆的大小
-heap 0x400 ;说明栈的大小
-l rts6701.lib ;说明程序中引用的库文件
MEMORY ;将整个存储器分成具有不同名称的存储区域
{
VECS: origin = 0x00000000, len = 0x00000200
PMEM: origin = 0x00000200, len = 0x0000d000
PCINIT: origin = 0x0000d200, len = 0x00001e00
CE1VECS: origin=0x01400000, len = 0x00000200
CE1PMEM:origin = 0x01400200, len = 0x0000d000
CE1INIT: origin = 0x0140d200, len = 0x00001e00
CE3: origin = 0x03000000, len = 0x01000000
DRAM: origin = 0x80000000, len = 0x00010000
}
SECTIONS ;说明目标文件中各段的加载地址和运行地址
{
.myBootCode :load=CE1VECS, run=VECS
;用户的引导代码段
.text : load=CE1PMEM, run=PMEM
.cinit : load=CE1INIT, run=DRAM
;装载到ROM中,在片内数据区运行
.const > DRAM
.data > DRAM
.bss > DRAM
.sysmem > DRAM
.stack > DRAM
.far > CE3
}
3.2 编写用户引导代码
DSP加电复位后,自动从CE1空间中拷贝64K数据(程序代码)到地址0处,然后从0地址处开始执行指令。由于在命令文件中,将.cinit装载到外部ROM中,但其运行地址却在片内数据区,所以采用下面这段程序,其主要作用是将Cinit从外部ROM中搬到片内数据区,使其能在C编程环境下进行正确的初始化工作,保证程序的顺利进行。
.sect “.myBootCode”
;将用户引导代码分配到
myBootCode段中
.global myBootCode
.ref _c_int00
;C程序的入口地址
myBootCode
......
;EMIF寄存器和DMA寄存器初始化
;使用DMA方式将以原地址0x0000d200开始的0x380长的存储空间拷贝到目标地址上
mvkl DMA0_SRA ,A5 ;装载原地址0xd200
mvkl 0x0000d200, B4
mvkh DMA0_SRA ,A5
mvkh 0x0000d200m B4
stw B4,*A5
mvkl DMA0_DSA, A5 ;装载目标地址0x80000000
mvkl 0x80000000, A4
mvkh DMA0_DSA, A5
mvkh 0x80000000, A4
stw A4,*A5
mvkl DMA0_CNT A5 ;装载数据长度0x380(这个长度可以通过查看.map文件中.cinit的长度获得)
mvkl 0x00000380, B1
mvkh DMA0_CNT ,A5
mvkh 0x00000380, B1
;启动DMA开始传输
wait:
mvkl DMA0_PCR ,A5
mvkh DMA0_PCR ,A5
ldw *A5 ,B2
mvkl 0x0000000c,A5
mvkh 0x0000000c ,A5
and A5,B2,B2
b2 b wait
nop 5
;传输结束后,跳转到C程序的入口地址c_int00处,
开始执行程序
mvkl .s2 _c_int00B0
mvkh .s2 _c_int00B0
B .s2 B0
nop 5 |