历史上的今天
返回首页

历史上的今天

今天是:2025年04月19日(星期六)

2020年04月19日 | S3C2440 代码重定位实验(三)

2020-04-19 来源:eefocus

代码重定位

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

对于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,在读出来,如果写入则是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 copyBIN2SDRAM

bl clearBSS


//bl main

ldr lr, =halt

ldr pc, =main //这才是真正跳转到SDRAM中执行代码

halt:

b halt


relocate.c


extern unsigned int _text_start;

extern unsigned int _bss_start;

extern unsigned int _bss_end;


void copyBIN2SDRAM(void){

volatile unsigned int* srcStart = 0x0;

volatile unsigned int* destStart = &_text_start;

volatile unsigned int* destEnd = &_bss_start;


while(destStart < destEnd){

*destStart = *srcStart;

destStart++;

srcStart++;

}

}


void clearBSS(void){

volatile unsigned int* start = &_bss_start;

volatile unsigned int* end = &_bss_end;

while(start < end){

*start = 0;

start++;

}

}


relocate.lds


SECTIONS {

. = 0x30000000;

_text_start = .;

.text : {*(.text)}

.rodata : {*(.rodata)}

/*按4字节对齐*/

. = ALIGN(4);

.data : { 

_data_start = .;

*(.data)

_data_end = .;

}

/*按4字节对齐*/

. = ALIGN(4);

_bss_start = .;

.bss : { *(.bss) }

_bss_end = .;

}


Makefile


CROSS_COMPILE_PREFIX=arm-linux-

CFLAGS=-g

TARGET=relocate


$(TARGET).bin : CRT0.o uart.o myprintf.o Ctype.o sdram.o util.o $(TARGET).o main.o

@#$(CROSS_COMPILE_PREFIX)ld -Ttext 0x0000000 -Tdata 0x30000000 -o $(TARGET).elf $^

$(CROSS_COMPILE_PREFIX)ld -T $(TARGET).lds -o $(TARGET).elf $^

$(CROSS_COMPILE_PREFIX)objcopy -O binary -S $(TARGET).elf $@

$(CROSS_COMPILE_PREFIX)objdump -D $(TARGET).elf > $(TARGET).dis


CRT0.o : CRT0.S

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -c -o $@ $^

myprintf.o : myprintf.c stdarg.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $< 

uart.o : uart.c stdarg.h s3c2440_soc.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $< 

$(TARGET).o : $(TARGET).c 

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<

main.o : main.c

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<

Ctype.o : Ctype.c Ctype.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<

util.o : util.c util.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<

sdram.o : sdram.c sdram.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<


clean:

rm -f *.elf *.dis *.bin *.o


编译并烧写至开发板,选择Nor Flash方式启动,观察如下所示:

在这里插入图片描述

与本文上面的在Nor Flash中运行效果动态图对比,可以发现,在SDRAM中运行的速度有了明显的提升。

推荐阅读

史海拾趣

潮州三环(Three-circle)公司的发展小趣事

面对能源危机的挑战和新能源市场的崛起,三环集团决定进军新能源领域。在2004年,公司开始研发生产燃料电池电解质基片(SOFC),这是一种能够将化学能转化为电能的装置。通过多年的努力,三环集团成功实现了SOFC的量产,并将其应用于新能源汽车、分布式发电等领域。这一布局不仅展示了公司的前瞻性和创新能力,也为公司带来了新的发展机遇。

Boundary Devices公司的发展小趣事

作为一家有社会责任感的企业,Boundary Devices始终关注环境保护和可持续发展。公司在生产过程中积极采用环保材料和技术,减少对环境的影响。同时,公司还积极参与社会公益活动,为社会的发展贡献自己的力量。这些举措不仅提升了公司的社会形象,也为公司的长期发展奠定了坚实的基础。

以上五个故事基于Boundary Devices公司的发展背景和电子行业的一般趋势进行创作,旨在展示该公司在不同阶段的成长与发展。然而,实际的公司发展历程可能更加复杂和丰富,需要更多的资料和信息来深入了解。

Dynex公司的发展小趣事

Boundary Devices公司成立于2003年,总部位于美国亚利桑那州。创立之初,公司便专注于嵌入式系统硬件的开发与生产,凭借对技术的深刻理解和市场需求的敏锐洞察,Boundary Devices迅速在行业中崭露头角。其推出的Boundary Devices插座和适配器,因其兼容性强、性能稳定而备受市场青睐。同时,公司与NXP/Freescale建立了紧密的合作关系,共同推进i.MX系列处理器的应用与发展,为公司的后续发展奠定了坚实的基础。

Electronic-Bauteile Goerlitz GmbH公司的发展小趣事

为了进一步提升公司的竞争力,Electronic-Bauteile Goerlitz GmbH公司积极实施国际化战略。公司通过与国外知名企业的合作,引进先进的技术和管理经验;同时,公司还在海外设立了研发中心和生产基地,以便更好地满足当地市场的需求。这些举措使得公司的业务范围不断扩展,国际影响力不断增强。

Cal Test Electronics公司的发展小趣事

随着客户需求的多样化,Cal Test Electronics开始关注小批量定制型测试电缆组件的市场。公司利用自身的技术优势和生产能力,为客户提供个性化的定制服务。这些定制产品不仅满足了客户的特殊需求,也提高了公司的市场竞争力。通过这一策略,Cal Test Electronics成功打开了一个全新的市场领域,为公司的发展注入了新的动力。

骏晔科技(DreamLNK)公司的发展小趣事

骏晔科技在无线通讯模块领域一直寻求创新。2019年,公司与硬之城达成战略合作,推出了高性能的FSK双向收发模块。这款模块采用了最新的FSK调制技术,极大地提高了数据传输的稳定性和效率。通过这一创新,骏晔科技在物联网设备通讯领域取得了显著的市场份额增长。

问答坊 | AI 解惑

12864液晶资料(非常详细)

希望对大家有帮助,欢迎一起学习讨论…

查看全部问答>

富士康 第十 大家如何看待

今天早晨看新闻有富士康的第十跳,觉得万般无奈,社会怎么了?人格怎么了?是变化太快还是无奈····…

查看全部问答>

求助各位大侠:谁有或知道有单片机控制的TT50通信模块的C语言初始化程序和收发信息程序

  求助各位TT50通信模块的初始化程序和短信收发的C语言程序,由单片机控制TT50,TT50的初始化程序,在接到单片机的控制指令时怎么发送消息?谢谢各位大侠!!!!…

查看全部问答>

蓝牙驱动选择问题

各位,目前我们的项目需要在一个WinCE板子上使用蓝牙功能,要求支持A2DP,DUN,SPP等功能。 目前就我所了解的主要提供蓝牙驱动的有widcomm,bluesoleil和windows自带的。 网上搜了一些文章,基本上说wincomm最稳定,想请教一下大家,windows自带的 ...…

查看全部问答>

linux下SD卡读写速度请教

目前正在调SD卡(2.0的协议)驱动,它的读写速度特别慢,写200KB/s左右,读大概是写的2-3倍.目前用的通信模式是SD模式,传输方式为DMA传输,时钟频率调高来试过了,可是速度还是一个样慢.我测试用的SD卡是标准卡,本身速度 ...…

查看全部问答>

关于DC脚本中时钟分频的处理

外部引脚的时钟400M ,2分频生成200M脚本:create_generated_clock -name clk_200 -source clk_400 -divide_by 2为何DC显示:Error:Required argument \'source_objects\' was not found (CMD-007)…

查看全部问答>

AMBA AXI及WISHBONE的一些问题

1 AMBA AHB字节选通的问题 比如我一个memory是32位宽的。ARM是字节对齐操作,通过地址低两位来选择哪个字节,也就是说我的控制器在接收ARM地址后先要右移两位来确定地址,因为最低两们并不代表实际地址。读的时候可以理解,不管你ARM要哪个字节, ...…

查看全部问答>

PADS2004怎么将完整的PCB封装打印出来?

           用PADS 把器件的封装做好了,想打印出来实物比较下。         只会用CAM一层一层的打印,怎么才可以把焊盘和丝印打印在一起形成一个完整的封装 ...…

查看全部问答>