最近在使用TI的J7系列的一块处理器,由于需要使用nor flash的XIP模式,就研究了一下代码重定位的问题,在网上找了很多资料,学到了很多,接下来做一个总结吧。
首先,代码重定位的目的:一个完整的程序分为.data、.text.、.bss、..stack等,如果使用xip运行的话只有.text在nor flash中run,.data在SRAM里边run,但是保存在nor flash中。在lds(链接文件)中如果直接把.data设置到SRAM的地址,生成的bin文件很很大,比如我现在用到的地址SRAM = 0x51c00000 ,length = 0x9000;nor flash的地址 = 0x600e0000 ,length = 0x20000,这样算下来生成的bin文件大小大概为:0x600e0000+0x20000-0x51c00000 = 0xe500000=240123904 = 229M;为什么这么大,我用的空间加起来也没这么多啊?因为在elf文件转bin的过程中是不管你实际使用的大小的,转换器只在意你的起始地址和结束地址,然后中间没用的地方补0,为了解决这个问题就使用到了重定位的概念。
重定位的概念简述:一段程序,.data/.text可以分为load address(加载地址)和run address(运行地址),如果使用XIP模式.txet段的load address = run address,但是.data段的load address必须在nor flash,run address必须要在SRAM中。重定位就是让程序在运行中使用的是run address,但是在存储的时候使用的是load address。
如何实现?
具体的实现分为两步:
1、lds文件进行设置
2、main函数之前或者main函数的第一条指令进行数据copy
具体例子实现:
由于使用的是TI的ARM处理器,使用的是CCS下边的CGT_ARM_V20.0.0.LTS(具体名字忘记了)编译器,所以需要根据TI的规则来编写lds文件,废话少说,直接上干货!!!!
SRAM : (RW) origin = 0x51c00000, length = 0x9000
XIP_FLASH : (R) origin = 0x600e0000,length = 0x20000
.data : LOAD = XIP_FLASH, RUN = SRAM, align(4)
{
.=align(4);
*(.data)
.=align(4);
} LOAD_START(_load_data_start),
LOAD_SIZE(_load_data_size),
LOAD_END(_load_data_end),
RUN_START(_run_data_start),
RUN_SIZE(_run_data_size),
RUN_END(_run_data_end),
align(4)
分析
.data : 定义一个.data段;
LOAD = XIP_FLASH, RUN = SRAM, align(4):.data段的load address在XIP_FLASH中,run address在SRAM中,以四字节方式对齐;
.=align(4):.data段使用4字节对齐的方式;
*(.data):程序中所有的全局变量都存储在data段;
.=align(4):同上;
LOAD_START(_load_data_start):LOAD_START是链接器的保留字,作用是在map文件中会生成_load_data_start字符,该字符的值代表.data段在nor flash中存储的起始地址,可以在代码中使用;
LOAD_SIZE(_load_data_size):同上,该字符的值代表.data段在nor flash中存储的空间大小;
LOAD_END(_load_data_end):同上,该字符的值代表.data段在nor flash中存储的结束地址;
RUN_START(_run_data_start):同上,该字符的值代表.data段在SRAM中运行的起始地址;
RUN_SIZE(_run_data_size):同上,该字符的值代表.data段在SRAM中运行的空间大小;
RUN_END(_run_data_end):同上,该字符的值代表.data在SRAM中运行的结束地址;
align(4):以4字节地址对齐;
以上,lds文件编写完成。进入第二步!!
第一步生成相应的地址字符之后就需要在代码中进行copy了。对应的copy代码如下:
/* Symbol variable */
extern uint32 _load_data_start;
extern uint32 _load_data_size;
extern uint32 _load_data_end;
extern uint32 _run_data_start;
extern uint32 _run_data_size;
extern uint32 _run_data_end;
/* Function prototype */
void Start_SectionDataCopy(uint32 *runStart, uint32 *loadStart, uint32 *loadEnd)
{
while(loadStart<loadEnd)
{
*runStart++ = *loadStart++
}
}
/* Call copy function */
Start_SectionDataCopy(&_run_data_start, &_load_data_start, &_load_data_end);
以上,记录一下!