×
单片机 > 单片机程序设计 > 详情

S3C2440 代码重定位实验(三)

发布时间:2024-05-20 发布时间:
|

代码重定位

我们现在来解决代码重定位实验(一)所引入的代码重定位问题。

对于S3C2440来说:


BIN文件小于4KB时:

若是Nand方式启动,则不存在任何问题

若是Nor方式启动,则我们可以只重定位.data段即可

当BIN文件大于4KB时:

若是Nand方式启动,则需要重定位整个程序,包括代码段和数据段

若是以Nor方式启动,则依然只重定位.data段即可

只重定位.data段和清零.bss段

对于重定位.data段的代码,正常情况下应该是使用汇编来编写的,我为了简便起见,使用了C语言来编写。由于此时尚未重定位数据段和清零BSS段,是不应该调用C函数的。但是我保证了这两个函数不访问全局变量,所以只要正确设置了栈指针,调用也可以正常工作。

使用的链接脚本relocate.lds如下:


SECTIONS {

.text 0 : {*(.text)}

.rodata : {*(.rodata)}

_data_offset = .;

.data 0x30000000 : AT(_data_offset) {

_data_LMA = LOADADDR(.data);

_data_start = .;

*(.data)

_data_end = .;

}

_bss_start = .;

.bss : { *(.bss) }

_bss_end = .;

}


relocate.c


extern unsigned char _data_offset;

extern unsigned char _data_start;

extern unsigned char _data_end;

extern unsigned char _bss_start;

extern unsigned char _bss_end;


void copyDataSection(void){

volatile unsigned char *dataLMA = &_data_offset;

volatile unsigned char *start = &_data_start;

volatile unsigned char * end = &_data_end;


while(start <= end){

*start = *dataLMA;

start++;

dataLMA++;

}

}


void clearBSS(void){

volatile unsigned char* start = &_bss_start;

volatile unsigned char* end = &_bss_end;

while(start < end){

*start = 0;

start++;

}

}


CRT0.S


.text

.global _start

_start:

/* 1.关闭看门狗 */

ldr r0, =0x53000000

ldr r1, =0

str r1, [r0]

/* 2.设置时钟 */

/* 2.1 设置LOCKTIME(0x4C000000)=0xFFFFFFFF */

ldr r0, =0x4C000000

ldr r1, =0xFFFFFFFF

str r1, [r0]

/* 2.2 设置CLKDIVN(0x4C000014) = 0x5 FCLK : HCLK : PCLK = 400m : 100m : 50m*/

ldr r0, =0x4C000014

ldr r1, =0x5

str r1, [r0]

/* 2.3 设置CPU处于异步模式 */

mrc p15,0,r0,c1,c0,0

orr r0,r0,#0xc0000000 /* #R1_nF:OR:R1_iA */

mcr p15,0,r0,c1,c0,0

/* 2.4 设置MPLLCON(0x4C000004)=(92<<12) | (1 << 4) | (1 << 0)

* m = MDIV + 8 = 100

* p = PDIV + 2 = 3

* s = SDIV = 1

* Mpll = (2 * m * Fin) / (p * 2 ^ s)= (2 * 100 * 12) / (3 * 2 ^ 1) = 400MHZ

*/

ldr r0, =0x4C000004

ldr r1, =(92<<12) | (1 << 4) | (1 << 0)

str r1, [r0]

/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定

* 然后CPU工作于新的频率FCLK

*/


/* 3.设置栈

* 自动分辨NOR启动或者NAND启动

* 向0地址写入0,在读出来,如果写入则是NAND,否则是NOR

*/


ldr r0, =0

ldr r1, [r0] /* 读出原来的值备份 */

str r0, [r0] /* 向0地址写入0 */

ldr r2, [r0] /* 再次读出来 */

cmp r1, r2

ldr sp, =0x40000000 + 4096 /* nor启动 */

movne sp, #4096 /* nand启动 */

strne r1, [r0] /* 恢复原来的值 */


//初始化SDRAM内存控制器

bl sdram_init

bl copyDataSection

bl clearBSS


bl main


halt:


b halt


main.c:


#include "myprintf.h"

#include "uart.h"

#include "util.h"


char gCh = 'A';

char gCh1;


int main(void) {

uart0_init();

printf("%snr", "NorFlash Relocate Test.");

while(1) {

gCh++;

printf("%c(0x%x)", gCh, gCh);

wait(800000);

}

return 0;

}


将编译生成的BIN文件烧写至NorFlash中,启动开发板,发现可以main函数正常修改全局变量了,如下图所示:

上述代码效率改进

从拷贝函数中可以看出,我们每次只拷贝一个字节。而JZ2440的SDRAM是32位的,这样拷贝的话效率就很低。为此,我们一次拷贝4个字节的数据。我们修改relocate.c文件如下所示:

relocate.c


extern unsigned int _data_offset;

extern unsigned int _data_start;

extern unsigned int _data_end;

extern unsigned int _bss_start;

extern unsigned int _bss_end;


void copyDataSection(void){

volatile unsigned int *dataLMA = &_data_offset;

volatile unsigned int *start = &_data_start;

volatile unsigned int * end = &_data_end;


while(start <= end){

*start = *dataLMA;

start++;

dataLMA++;

}

}


void clearBSS(void){

volatile unsigned int* start = &_bss_start;

volatile unsigned int* end = &_bss_end;

while(start < end){

*start = 0;

start++;

}

}


再次编译烧写至开发板,以Nor方式启动,并上电观察。

通过上述动态图可以看出,全局变量gCh本来是等于‘A’,对应0x41。但是现在却是0了,被清零了。这是为什么呢?为什么清零.bss段的时候,会把.data段的值给清掉呢?

我们观察清零.bss段的反汇编,如下所示:


Disassembly of section .data:


30000000 <_data_start>:

30000000: 41 .byte 0x41

Disassembly of section .bss:


30000001 :

...


00000d2c :

d2c: e52db004 push {fp} ; (str fp, [sp, #-4]!)

d30: e28db000 add fp, sp, #0 ; 0x0

d34: e24dd00c sub sp, sp, #12 ; 0xc

d38: e59f3040 ldr r3, [pc, #64] ; d80

d3c: e50b300c str r3, [fp, #-12]

d40: e59f303c ldr r3, [pc, #60] ; d84

d44: e50b3008 str r3, [fp, #-8]

d48: ea000005 b d64

d4c: e51b200c ldr r2, [fp, #-12]

d50: e3a03000 mov r3, #0 ; 0x0

d54: e5c23000 strb r3, [r2]

d58: e51b300c ldr r3, [fp, #-12]

d5c: e2833001 add r3, r3, #1 ; 0x1

d60: e50b300c str r3, [fp, #-12]

d64: e51b200c ldr r2, [fp, #-12]

d68: e51b3008 ldr r3, [fp, #-8]

d6c: e1520003 cmp r2, r3

d70: 3afffff5 bcc d4c

d74: e28bd000 add sp, fp, #0 ; 0x0

d78: e8bd0800 pop {fp}

d7c: e12fff1e bx lr

d80: 30000001 .word 0x30000001

d84: 30000002 .word 0x30000002


由该段汇编代码可知,它对0x30000001地址开始,到0x30000002地址前一个字节为止,进行了清零操作。而数据段.data的地址位于0x30000000处,所以理论上好像不会被清掉。


我们再观察SDRAM的电路图可知:

CPU发出的地址线最低两位没有接到SDRAM芯片上,因为两片SDRAM组成了32位的数据总线,CPU对于内存是按照4字节寻址的,地址线的最低两位被忽略,默认为0。


所以若是单次访问4个字节的数据,无论是访问地址0x30000001、0x30000002或者0x30000003,CPU最终访问的都是0x30000000开始的四字节数据。所以数据段的数据就在清零.bss段时被清理掉了。


问题原因找到了,解决方法就简单了。我们在链接脚本中主动让数据段和BSS段都按4字节先对齐好,就不会有问题了。修改链接脚本如下所示:

relocate.lds:


SECTIONS {

.text 0 : {*(.text)}

.rodata : {*(.rodata)}

/*按4字节对齐*/

. = ALIGN(4);

_data_offset = .;

.data 0x30000000 : AT(_data_offset) {

_data_LMA = LOADADDR(.data);

_data_start = .;

*(.data)

_data_end = .;

}

/*按4字节对齐*/

. = ALIGN(4);

_bss_start = .;

.bss : { *(.bss) }

_bss_end = .;

}


重新编译,以Nor方式启动,上电观察,又恢复正常了。

重定位整个程序

当我们的开发板是以Nand方式启动,并且BIN文件超过4KB时,则需要重定位整个程序,包括代码段和数据段。为此,我们需要引入一个概念:位置无关代码。位置无关代码(PIC:Position Independent Code),就是无论被加载到任何地址空间都可以正常工作的代码。


那么怎么写位置无关的程序呢?


调用程序时使用b或者bl相对跳转指令

重定位之前,不能使用绝对地址,不可访问全局变量或者静态变量

不可访问有初始值的数组(因为这些初始值放置在.rodata段内,而.rodata段是位置相关的,不在栈中)

重定位之后,需要使用绝对跳转指令跳转到运行时地址(链接地址)处开始执行,比如ldr pc, =main

由于我们还没有涉及到Nand Flash的操作实验(下一篇文章),而Nand Flash不能像访问内存一样地读取数据,所以我们还是先将BIN文件烧写至Nor Flash,并以Nor Flash方式启动,进行整个程序的重定位。


跳转指令

在汇编文件中调用main函数时,注意必须使用绝对跳转指令:


ldr pc, =main


而不能使用bl main这种位置无关指令,否则仍旧是在Nor Flash或者是SRAM中运行。

又因为ldr pc, =main指令并不改变lr寄存器存储的返回地址,所以在跳转到main函数之前,要修改lr,使其指向halt,否则从main函数返回,会重新再一次执行ldr pc, =main指令,如下所示:


ldr lr, =halt

ldr pc, =main


全部文件如下所示:

我们列出最主要的几个源码文件:

CRT0.S


.text

.global _start


_start:

/* 1.关闭看门狗 */

ldr r0, =0x53000000

ldr r1, =0

str r1, [r0]

/* 2.设置时钟 */

/* 2.1 设置LOCKTIME(0x4C000000)=0xFFFFFFFF */

ldr r0, =0x4C000000

ldr r1, =0xFFFFFFFF

str r1, [r0]

/* 2.2 设置CLKDIVN(0x4C000014) = 0x5 FCLK : HCLK : PCLK = 400m : 100m : 50m*/

ldr r0, =0x4C000014

ldr r1, =0x5

str r1, [r0]

/* 2.3 设置CPU处于异步模式 */

mrc p15,0,r0,c1,c0,0

orr r0,r0,#0xc0000000 /* #R1_nF:OR:R1_iA */

mcr p15,0,r0,c1,c0,0

/* 2.4 设置MPLLCON(0x4C000004)=(92<<12) | (1 << 4) | (1 << 0)

* m = MDIV + 8 = 100

* p = PDIV + 2 = 3

* s = SDIV = 1

* Mpll = (2 * m * Fin) / (p * 2 ^ s)= (2 * 100 * 12) / (3 * 2 ^ 1) = 400MHZ

*/

ldr r0, =0x4C000004

ldr r1, =(92<<12) | (1 << 4) | (1 << 0)

str r1, [r0]

/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定

* 然后CPU工作于新的频率FCLK

*/

/* 3.设置栈

* 自动分辨NOR启动或者NAND启动

* 向0地址写入0,在

[1] [2]
S3C2440代码重定位Nand

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
单片机制作超级流水灯