本文主要对工作中调试C6678的emif flash启动做一个归纳
1.启动原理
在6678芯片内部,官方厂商已经固化了一段代码(RBL),用于识别boot mode,初始化相应的外设,之后执行用户的程序。
参考sprugy5c-KeyStone Architecture DSP Bootloader User’s Guide.pdf文档24页,如下图。
参考相关文档可知,EMIF启动中,RBL是直接跳转到EMIF挂载的FLASH基地址上,需要FLASH是norflash才行。无需将程序预加载到RAM中,直接可以在FLASH中运行,不过性能会有所下降。上图中CS2指的是CE0片选。
因此需要程序直接烧写到FLASH基地址0x70000000,设置好boot的模式为emif启动,即可完成程序的固化和自启动。
2.程序设计
为了避免FLASH运行程序导致芯片性能下降,需要建立两个工程,将程序分为两部分,一部分(BootLoader)烧写在0x70000000,一部分(UserApp)烧写在0x70300000。(烧写地址可以自定,只要不相互重复即可)
BootLoader主要功能为设置CPU工作频率,初始化EMIF和DDR3,将UserApp整体搬移到DDR3中,然后跳转到DDR3中执行程序,从此程序就在DDR3中运行,不会因FLASH运行下降速度。
UserApp主要功能为一些测试程序,正式运行的程序。
3.BOOTMODE[12:0]
参考TMS320C6678.pdf 24页可知,BOOTMODE由[12:0]共12位决定。注意,BOOTMODE[12:0]对应的是GPIO[13:1],GPIO[0]是大小端(endian)配置。见下图。
为了设置为EMIF启动,BOOTMODE应该如下配置。
Boot Device[2:0]:000b;(0 = EMIF16 / No Boot)
Device Configuration[5:4]: 01b;(1 = EMIF16 boot),其他位设置为0。
我使用的板卡时钟为156.25.MHz,根据上图表可知:
PLL Mult I2C /SPI Ext Dev Cfg[12:10]:0b100。
至此,bootmode设置完成。
4.程序编码
为了实现emif启动,我创建了3个工程:BootLoader工程,APP工程,还有一个FLASH烧写工程。
4.1 BootLoader工程
BootLoader工程需要烧写到FLASH基地址0x70000000,因此需要配置cmd文件,将.text、.cinit等段放入FLASH中。cmd配置如下:
-stack 0x400
-heap 0x400
MEMORY
{
L2SRAM: o = 0x00800000 l = 0x00004000
MCSM: o = 0x0C000000 l = 0x00400000
FLASH_E: o = 0x70000000 l = 0x00000100
FLASH_P: o = 0x70000100 l = 0x0001FEE0
FLASH_I: o = 0x7001FFE0 l = 0x00000020
}
SECTIONS
{
.bootentry > FLASH_E
.bootsect > FLASH_P
.text > FLASH_P
.cinit > FLASH_P
.switch > FLASH_P
.const > FLASH_P
.blversion > FLASH_I
.cio > L2SRAM
.far > L2SRAM
.fardata > L2SRAM
.neardata > L2SRAM
.stack > L2SRAM
.sysmem > L2SRAM
.bss > L2SRAM
}
.blversion段用来存放一些BootLoader的版本信息。
BootLoader工程还需要有一段汇编,用来保证当前运行地址为0x70000000,跳转_c_int00。
.global BOOTLOADER_entry
.ref _c_int00
.sect ".bootentry"
; begin of BOOT_start
BOOTLOADER_entry:
b _c_int00
nop
nop
nop
nop
nop
nop
nop
.byte "Bootloader"
之后需要设置程序入口函数为BOOTLOADER_entry,设置编译会有警告,属正常现象。如下图:
下面为BootLoader的搬运APP的部分代码:
#define APP_IMAGE_POSITION 0x70300000
int main(void)
{
// 装入从APP_IMAGE_POSITION开始的应用程序boot-table
unsigned int *p = (unsigned int*)APP_IMAGE_POSITION;
FLASH_BOOT_FX entryAddr;
unsigned int len;
unsigned int* pos;
//获取程序入口地址
entryAddr = (FLASH_BOOT_FX)(*p++);
//每次搬运4字节
while(1) {
len = *p++;
len = (len+3)>>2;
if(len == 0)
break;
pos = (unsigned int*)*p++;
for(i=0; i<len; i++)
*pos++ = *p++;
}
//跳转APP的程序入口函数
entryAddr();
}
4.2 APP工程
APP工程虽然烧写到FLASH基地址0x70300000,但是BootLoader会把APP搬运到DDR3中,因此需要配置cmd文件,将.text、.cinit等所有段放入DDR3中。cmd配置如下:
-heap 0x800
-stack 0x1000
MEMORY
{
/* Local L2, 0.5~1MB*/
VECTORS: o = 0x00800000 l = 0x00000200
LL2_RW_DATA: o = 0x00800200 l = 0x0003FE00
/* Shared L2 2~4MB*/
MSM: o = 0x0C000000 l = 0x00200000
//BOOT: o = 0x70300000, l = 0x00000200
FLASH: o = 0x70300000, l = 0x003FFE00
/* External DDR3, upto 2GB per core */
DDR3_CODE: o = 0xF0000000 l = 0x01000000 /*set memory protection attribitue as execution only*/
DDR3_R_DATA: o = 0x81000000 l = 0x01000000 /*set memory protection attribitue as read only*/
DDR3_RW_DATA: o = 0x82000000 l = 0x06000000
}
SECTIONS
{
.Appentry > DDR3_CODE
.text > DDR3_CODE
.cinit > DDR3_CODE
.const > DDR3_CODE
.switch > DDR3_CODE
.vecs > DDR3_CODE
.stack > DDR3_CODE
GROUP
{
.neardata
.rodata
.bss
} > DDR3_CODE
.far > DDR3_CODE
.fardata > DDR3_CODE
.cio > DDR3_CODE
.sysmem > DDR3_CODE
}
APP工程不需要配置程序入口函数。
4.3 FLASH烧写工程
FLASH烧写工程主要用于将生成的image文件烧录入emif flash的相应地址。此工程需要把生成的两个image文件分别烧写到0x70000000和0x70300000。
如果想要开发在线更新的程序,可以将此工程整合进APP或者BootLoader工程中。
5. 生成image文件
工程编译生成的.out文件不能直接烧录到FLASH运行,需要转换成.bin文件烧写。当然,生成image文件不止一种方法,此处只是我使用的方法。
生成image文件需要工具如下:
elf2hex.txt和out2bin.bat为自己编写的命令处理文件,直接Windows的cmd命令行输入命令是一样的,就是会很累。
5.1 BootLoader工程
BootLoader工程与APP工程类似,只有elf2hex.txt不同。
下面为elf2hex.txt内容
..\debug_flash\bootloader.out
--outfile=debug\bootloader.hex
--map=debug\bootloader.map
--memwidth=32
--romwidth=32
--order=M
--intel
--fill=0xffffffff
下面为out2bin.bat内容
hex6x elf2hex.txt
@cd debug
hex2bin bootloader.hex
@cd ..\..
5.2 APP工程
BootLoader工程与APP工程类似,只有elf2hex.txt不同。
下面为elf2hex.txt内容
..\debug_flash\APP.out
--outfile=debug\APP.hex
--map=debug\APP.map
--memwidth=32
--romwidth=32
--order=M
--intel
--fill=0xffffffff
--boot
--bootorg=0x00000000
只要将生成的.bin文件烧写到指定地址,就可以实现emif的flash启动了。
6. boot table 格式
使用工具转换的.bin文件格式是boot table格式。
由于boot image 是用户编程实现,大小不定。为了使RBL搬移bootimage时知道搬移的目的地址和搬移量大小,TI定义了boot table 格式。
因为我的BootLoader工程不打算搬移到DDR或者内存中,因此生成.bin的命令缺少*–boot --bootorg=0x00000000*命令。因此BootLoader.bin不是严格的boot table格式。
APP工程生成的是严格的boot table格式,当出现问题时,可以根据生成的.map文件和.bin文件一一对照,查看问题原因。
boot table 包含一个32位的头地址、多个段和一个结束标志,如图 1所示。
- 32位头地址
在boottable的起始处,RBL在搬移完后,跳转到该头地址。该头地址也是加载到合适存储位置的boot image 的起始地址。 - 段
boot table 的主要部分是一个或多个段。每个段分为三部分:
- 32位的段字节数(要搬移的数据字节数)
- 32位的段地址(要搬移数据的目的起始地址)
- 要搬移的数据
- 结束标志
32位全0(可以理解为32位的段字节数为0)
如下图所示:
RBL 在搬移boot table 格式的boot image 过程中,不断地读取和搬移段数据,当遇到段字节数为0(其实就是结束标志),则跳转到 boot table 最开始指定的 32位头地址。核0 开始执行boot image 程序。(emif启动模式下,RBL不会搬运程序,如需要搬运程序,需要自己写汇编或者像我一样分为两段程序)
一个boot table 文件,只有一个32位的头地址和一个结束标志,可以有很多个段,根据段地址和段字节数搬运到不同的地址。
7. 仿真器调试
7.1 BootLoader调试
因为BootLoader不需要搬移,可以直接在flash上执行,因此我们只需要使用仿真器download一个初始化emif的工程,然后就可以跳转到FLASH上的BootLoader程序运行了。以此校验BootLoader生成的image是否正确。
如上图所示,将工程运行到初始化emif接口后,打开Disassembly窗口,输入0x70000000则下面显示flash上的汇编代码,右键鼠标选中Move To Line,点击右边的绿色箭头来单步调试或者直接resume运行。如果能够看见BootLoader的串口打印,则证明BootLoader烧写正确。
7.2 APP调试
可以直接将BootLoader工程改为在RAM中运行,单步调试,通过mem窗口查看APP程序是否成功从FLASH搬移到DDR中,最后程序跳转地址是否正确。