历史上的今天
返回首页

历史上的今天

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

2020年04月19日 | s3c2440之代码重定位

2020-04-19 来源:eefocus

1、几个概念

(1)运行地址、加载地址

① 运行地址<—>链接地址:他们两个是等价的,只是两种不同的说法。

运行地址:程序在SRAM、SDRAM中执行时的地址。就是执行这条指令时,PC应该等于这个地址,换句话说,PC等于这个地址时,这条指令应该保存在这个地址内。

② 加载地址<—>存储地址:他们两个是等价的,也是两种不同的说法。

加载地址:程序保存在Nand flash中的地址。


(2)位置无关码、位置有关码

① 位置无关码:B、BL、MOV都是位置位置无关码。

② 位置有关码:LDR PC,=LABEL等类似的代码都是位置有关码。


(3)程序段的划分

一个程序编译后,会有代码段、数据段、只读数据段、bss段和注释段


.text 代码段

.data 数据段

rodata 只读数据段(const全局变量)

-bss段 (初始值为0,无初始值的全局变量和静态局部变量)

commen 注释

Question:bss段为什么可以减小bin文件的大小?

答:bbs主要记录未初始化和初始化为0的全局(static 修饰的局部)变量的位置,而不会记录其具体数据,因为这部分的数值会默认设置为0。


2、s3c2440启动方式

s3c2440启动方式分为nor启动 和 nand启动


nor启动时:0地址即为nor的0地址,片内sram的0地址为0x4000,0000,sdram0地址为0x3000,0000

nand启动时:0地址为片内sram,sdram0地址为0x3000,0000,nand启动时会把其中前4k的代码(这部分放引导代码)拷贝到片内sram上(sram只有4k),这个过程由nand flash控制器完成


3、什么是代码重定位

概括来说,所谓的代码重定位指的是将原先烧写在Nor Flash或者Nand Flash上的代码拷贝到SDRAM上运行,避免由于Nor Flash只能读不能写造成的全局变量的写入限制,避免Nand Flash启动时候,Sram空间有限(4K)而程序较大导致的访问出错。


4、为什么需要代码重定位

S3C2440的CPU可以直接给SDRAM发送命令、给Nor Flash发送命令、给4K的片上SDRAM发送命令,但是不能直接给Nand Flsh发送命令


假如把程序烧写到Nand Flsh上,即向Nand Flsh烧入* bin* 文件,CPU是无法从Nand Flsh中取代码执行的。


为什还可以使用NAND启动?


上电后,Nand启动硬件会自动把Nand Flsh前4K复制到SRAM;

CPU从0地址运行SRAM;

如果我的程序大于4K怎么办?

前4K的代码需要把整个程序读出来放到SDRAM(即代码重定位)。

如果从Nor Flash启动,会出现什么问题?


将拨动开关拨到Nor Flash启动时,此时CPU认为的 0地址 在Nor Flash上面,片内内存SRAM的基地址就变成了0x40000000(Nand启动时片内内存SRAM的基地址基地址是0),

由于Nor Flash特性:可以像内存一样读,但不能像内存直接写,因此需要把全局变量和静态变量重定位放到SDRAM里。


总结:


当使用nand启动时,因为2440的特性,只能运行前4K的代码(前4K会自动拷贝到内部的sram里),因此想运行1个100K+ 的uboot.bin是不可以能的,所以需要把代码重定位到外部的SDRAM里。

当使用nor启动时,jz2440开发板上nor为2MB,足够放一般的程序。但是nor 可以随意读无法随意写,这意味着全局变量是不可修改的。因为修改全局变量需要把数值重新写到nor 里面,而nor 是不可随意写的,所以写数值会失败。因此也需要把代码重定位到外部的SDRAM里。(局部变量是可以修改的,因为局部变量是放在栈里面,栈一般会设置在2440内部的sram)


5、如何实现重定位

代码重定位依赖分布式链接脚步实现,具体来说,可分为两种链接方式,分别是:分体式链接脚 和 一体式链接脚本


通常我们在这里使用一体式链接脚本,因为:


分体式链接脚本适合单片机,单片机自带有flash,不需要再将代码复制到内存占用空间。而我们的嵌入式系统内存非常大,没必要节省这点空间,并且有些嵌入式系统没有Nor Flash等可以直接运行代码的Flash,就需要从Nand Flash或者SD卡复制整个代码到内存;

JTAG等调试器一般只支持一体式链接脚本;

代码重定位可分为两步:


把程序的“代码段(.text)”、“数据段(.data)”、只读数据段(.rodata)全部拷贝到SDRAM上(起始地址为0x3000,0000)

将PC指针设置到SDRAM上


6、一体式链接脚本

关于分布式链接脚本可以参见韦老师的博客:第013课 S3c2440代码重定位详解


链接脚本的语法:


SECTIONS {

...

secname start BLOCK(align) (NOLOAD) : AT ( ldadr )

  { contents } >region :phdr =fill

...

}


解释:


 secname  :段名

 start  :起始地址:运行时的地址(runtime addr);重定位地址(relocate addr)

 AT ( ldadr ) :可有可无(load addr:加载地址) 不写时LoadAddr = runtime addr

 { contents } 的内容: 

 start.o //内容为start.o文件

 *(.text)所有的代码段文件

 start.o *(.text)文件


本案例中的链接脚本为,保存为xxx.lds:

注意:.text等section和“:”之间要留有一个空格,否make时候会出现错误。


SECTIONS

{

. = 0x30000000;

_code_start = .;


. = ALIGN(4);

.text :

{

  *(.text)

}


. = ALIGN(4);

.rodata : { *(.rodata) }


. = ALIGN(4);

.data : { *(.data) }


. = ALIGN(4);

_bss_start = .;

.bss : { *(.bss) *(.COMMON) }

_end = .;


}


start.s:



.text

.code 32

.global _start


_start:

bl initconfig


initconfig:

//关闭看门狗

ldr r0, =0x53000000

ldr r1, =0

str r1, [r0]

//初始化时钟

ldr r0, =0x4C000000

ldr r1, =0xFFFFFFFF

str r1, [r0]


ldr r0, =0x4C000014

ldr r1, =0x5

str r1, [r0]


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

orr r0,r0,#0xc0000000  

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


ldr r0, =0x4C000004

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

str r1, [r0]


//设置启动方式

mov r1, #0

ldr r0, [r1] 

str r1, [r1] 

ldr r2, [r1] 

cmp r1, r2   

ldr sp, =0x40000000+4096

moveq sp, #4096  

streq r0, [r1]   

//初始化SDRAM

bl sdram_init

//代码重定位

bl copy2sram

//清除bss段

bl cleanbss


copy2sram:

mov r0,#0

ldr r1,= _code_start

ldr r2,= _bss_start

copy:

ldr r3,[r0]

str r3,[r1]

add r1,r1,#4

add r0,r0,#4

cmp r1,r2

ble copy


cleanbss:

mov r0,#0

ldr r1,= _bss_start

ldr r2,= _end

clean:

str r0,[r1]

add r1,r1,#4

cmp r1,r2

ble clean


ldr pc, =main  


halt:

b halt

.end


注意(要点):

(1)为何要清除 bss段?

bbs主要记录未初始化和初始化为0的全局(static 修饰的局部)变量的位置,而不会记录其具体数据,因为这部分的数值会默认设置为0


(2)程序如何跳转

①:相对跳转

b和bl在汇编语言中是相对跳转的意思

比如对于bl copy2sram

因为这条指令 bl copy2sram是位置无关码,虽然它的运行地址是在SDRAM中的0x30000000,但是它可以在Steppingstone中执行。这条指令的功能是跳转到标号copy2sram处执行,它是一个相对跳转,PC=当前PC的值+偏移量OFFSET

偏移量 = 跳转到地址 - 当前地址 - 8


如果调用C函数,在调用主函数之前应该全部使用bl命令,因为如果使用ldr pc,=XXX,程序就回不来了,无法继续执行start.S余下的代码。

对于main.c,一般是在start.S最后进行调用,可以直接使用ldr pc,=main进行调用,使程序跳转到sdram上运行,实现代码的重定位

对于中断向量表,第一句话 b Reset 应该使用b跳转(不返回),其余全部使用ldr pc,=XXX。第一句话不能使用ldr pc,=XXX,因为这句话会使程序跳转到链接地址处,而此时程序还没有完成代码的重定位,链接地址处并无代码可以执行。不使用bl命令是因为此处不能返回,一旦返回就会执行中断向量表,误触中断

②:绝对跳转


ldr pc,=main


执行这句话以后,程序依据链接地址去需要main所在的运行地址(0x3000,xxxx,在SRAM上),此时main代码已经被拷贝到sdram中,所以得以正确运行

Q:ldr pc,main是否可以正常执行?

A:基本上肯定不可以。ldr pc,=main将直接把main标签处的地址里面的值赋值给pc,而main出的地址所指向的值并不一定指向main函数,所以无法实现跳转。

这里面使用的是指针的思想ldr pc,do_swi 的反汇编是 ldr pc,[pc,#0]:把pc+0地址处的值赋值给pc


(3)重定位后代码的运行过程是怎么样的?


首先执行start.S中的位置无关码(这部分要放在start.S的最前面)


初始化时钟、启动方式等等

初始化SDRAM

拷贝代码到SDRAM上

清除BSS

最后执行绝对跳转指令,跳转到SDRAM上运行代码


执行main


(4)怎么样写位置无关码?


使用相对跳转命令 b或bl;

重定位之前,不可使用绝对地址,不可访问全局变量/静态变量,也不可访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问);

重定位之后,使用ldr pc = xxx,跳转到/runtime地址;


(5)如果代码写成如下形式,是否可以工作?


.text

.code 32

.global _start


_start:

bl close_watchdog

bl clock_config

bl nandornor

//初始化SDRAM

bl sdram_init

//代码重定位

bl copy2sram

//清除bss段

bl cleanbss

ldr pc, =main

close_watchdog:

//关闭看门狗

ldr r0, =0x53000000

ldr r1, =0

str r1, [r0]

clock_config:

//初始化时钟

ldr r0, =0x4C000000

ldr r1, =0xFFFFFFFF

str r1, [r0]


ldr r0, =0x4C000014

ldr r1, =0x5

str r1, [r0]


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

orr r0,r0,#0xc0000000  

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


ldr r0, =0x4C000004

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

str r1, [r0]


nandornor:

//设置启动方式

mov r1, #0

ldr r0, [r1]

str r1, [r1]

ldr r2, [r1]

cmp r1, r2

ldr sp, =0x40000000+4096

moveq sp, #4096

streq r0, [r1]


copy2sram:

mov r0,#0

ldr r1,= _code_start

ldr r2,= _bss_start

copy:

ldr r3,[r0]

str r3,[r1]

add r1,r1,#4

add r0,r0,#4

cmp r1,r2

ble copy


cleanbss:

mov r0,#0

ldr r1,= _bss_start

ldr r2,= _end

clean:

str r0,[r1]

add r1,r1,#4

cmp r1,r2

ble clean


halt:

b halt

.end


答:当然是不可以的

在反汇编中,跳转到某个地址并不是由bl指令所决定,而是由当前pc值决定。反汇编显示这个值只是为了方便读代码。

so,看一下它的反汇编


做出以下修改之后就可以运行了,在每个label的最后添上这么一句话


mov pc,     lr      //返回


为什么第一个start.S中直接写bl,label最后没有添加这句话也可以执行?

因为之前那个是顺序执行,不涉及返回的问题


bl sdram_init

//代码重定位

bl copy2sram

//清除bss段

bl cleanbss

ldr pc, =main


练习:写一个程序:在串口中打印出启动方式信息,代码段起始位置、bss段起始位置和程序最后终止位置,并且实现代码重定位


答案:

main.c:


#include uart.h

char g_char = 'A';


void main()

{

puts("nrhello,relocate!nr");

while(1){

putch(g_char++);

}

}


printf_message.c


//打印启动方式

void printf_nand_nor()

{


}

//打印代码起始位置信息

void print_code_start()

{


}

//打印bss起始位置信息

void print_bss_start()

{


}

//打印.end起始位置信息

void print_end_start()

{


}


start.S


.text

.code 32

.global _start


_start:

bl close_watchdog

bl clock_config

bl nandornor

//初始化SDRAM

bl sdram_init

//代码重定位

bl copy2sram

//清除bss段

bl cleanbss

ldr pc, =main

close_watchdog:

//关闭看门狗

ldr r0, =0x53000000

ldr r1, =0

str r1, [r0]

mov pc,lr //返回


clock_config:

//初始化时钟

ldr r0, =0x4C000000

ldr r1, =0xFFFFFFFF

str r1, [r0]


ldr r0, =0x4C000014

ldr r1, =0x5

str r1, [r0]


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

orr r0,r0,#0xc0000000  

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


ldr r0, =0x4C000004

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

str r1, [r0]

mov pc,lr //返回


nandornor:

//设置启动方式

mov r1, #0

ldr r0, [r1]

str r1, [r1]

ldr r2, [r1]

cmp r1, r2

ldr sp, =0x40000000+4096

moveq sp, #4096

streq r0, [r1]

mov pc,lr //返回


copy2sram:

mov r0,#0

ldr r1,= _code_start

ldr r2,= _bss_start

copy:

ldr r3,[r0]

str r3,[r1]

add r1,r1,#4

add r0,r0,#4

cmp r1,r2

ble copy


cleanbss:

mov r0,#0

ldr r1,= _bss_start

ldr r2,= _end

clean:

str r0,[r1]

add r1,r1,#4

cmp r1,r2

ble clean


halt:

b halt

.end

推荐阅读

史海拾趣

C-TON Industries公司的发展小趣事

随着公司规模的扩大,C-TON意识到单凭自己的力量难以在竞争激烈的电子行业中立足。于是,公司开始积极寻求与其他企业的战略合作。通过与一家知名的电子产品制造商建立合作伙伴关系,C-TON成功将其芯片技术应用于对方的产品中,进一步扩大了市场份额。这一合作不仅提升了C-TON的知名度,也为其带来了稳定的订单和收入来源。

HN Electronic Components GmbH & Co Kg公司的发展小趣事

背景:2008年全球金融危机爆发,电子行业受到重创,市场需求大幅下降。

发展:面对严峻的市场环境,HN Electronics迅速调整战略,削减非核心业务,加强成本控制,并加大在研发领域的投入,以技术创新为突破口,寻找新的增长点。

关键事件:在金融危机期间,HN Electronics成功开发出具有更高性价比的物联网传感器芯片,满足了市场对智能家居、智慧城市等新兴领域的需求,从而实现了业绩的稳步增长。

Aptos Technology公司的发展小趣事

随着技术的不断进步,Aptos在微电子封装领域取得了重大突破。公司成功开发出了一系列高效、稳定的封装技术,并成功应用于多个电子产品中。同时,Aptos还积极拓展业务领域,将技术应用于智能穿戴、物联网等新兴领域,进一步提升了公司的市场影响力。

Advanced_Linear_Devices_Inc.公司的发展小趣事

在电子行业的快速发展中,ALD以其创新的CMOS技术崭露头角。1985年,公司创立之初,便致力于研发小功率线性集成电路,为线性工程师提供标准产品和定制方案。经过团队的不懈努力,ALD成功开发出了一系列具有高精度和低功耗特点的CMOS线性集成电路,这些产品在工业控制、电子仪器等领域得到了广泛应用,为公司的快速发展奠定了坚实基础。

ATP [ATP Electronics]公司的发展小趣事

面对工业应用领域的特殊需求,ATP敏锐地捕捉到了市场机遇。公司成功推出了业界首款工业级SLC e.MMC产品E800Pi,这款产品以其高达6万次的单元擦写耐久性和出色的耐高温、交叉温度能力,受到了工业领域的广泛认可。E800Pi的推出,不仅拓宽了ATP的产品线,也进一步提升了公司在工业存储解决方案领域的市场地位。

Hirel Systems Ltd公司的发展小趣事

ATP Electronics自创立之初,便以技术创新为核心竞争力。公司特有的SIP(Systems-In-Package:系统级封装)技术,在闪存卡制造领域引起了革命性的变革。通过SIP技术,ATP成功研发出超大容量的闪存卡,不仅满足了市场对于高性能存储的需求,也确立了公司在闪存卡市场的领先地位。这一技术的突破,为ATP带来了大量的订单和市场份额,奠定了公司持续发展的基础。

问答坊 | AI 解惑

元件封装

MAXIN元件封装…

查看全部问答>

做软件,北京,上海,深圳这三个地方你会选择那里?这三个地方有什么特点和优势?欢迎比较和发言!望呆过这几个地方的人多多发言!

做软件,北京,上海,深圳这三个地方你会选择那里?这三个地方有什么特点和优势?欢迎比较和发言!望呆过这几个地方的人多多发言!…

查看全部问答>

wince x86 下报:无法找到sqlceme30.dll

最近在研究wince 下的X86板子,是那种带内存条的板子,带有CF卡。这两天一直在测试,头疼的是,连接.sdf数据库时,用的sqlserverce.dll的底层,用sqllite也不行。总是报:无法找到sqlceme30.dll 网上也找了相关方法。总是不行。CAB也都装了。就是 ...…

查看全部问答>

感觉stm32F103+ENC28J60比stm32f107+PHY更方便。

反正是要两片芯片。总价位上差距也不大吧。…

查看全部问答>

vs1001k与vs1003有什么不同吗

由于vs1001k实在太贵了,所以想用vs1003代替有没有问题,两者有什么区别,两者编程时有什么不同的吗?望用过的指点下,谢谢…

查看全部问答>

op运放

本帖最后由 paulhyde 于 2014-9-15 09:22 编辑 op运放  …

查看全部问答>

关注MSP-EXP430FR5739套件抢购

     本人是MSP430单片机小白,因工作原因正准备学习,前一段时间错过了LaunchPad,肠子都悔青了,现急需一块MSP430的板子,希望能抢购成功。…

查看全部问答>

关于多路复用器的简单提问,求助啊

有三路频率信号(频率不同的近似方波幅值3V,频率范围1Khz-100Khz),我想做一个三选一处理,就是由单片机控制多路复用器选择这三个频率信号中的一个,我想问:三路频率信号在多路复用器这里会不会产生干扰,导致频率信号的频率改变或是波形恶化, ...…

查看全部问答>

UCOS移植到MSP430过程与心得体会《2》

以下是includes.h文件的内容. #include    #include    #include    #include    #include    #include    \"stdbool.h\" #include    #include    ...…

查看全部问答>