第013课 S3c2440代码重定位详解

2020-03-23来源: eefocus关键字:S3c2440  代码重定位  

第001节段的概念重定位的引入


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里。 

这里写图片描述

例如执行如下几条汇编指令


 MOV R0, #0

 LDR R1, [R0] @读有效

 STR R1, [R0] @写无效


当程序中含有需要写的全局变量或静态变量时,假如是在Nand Flash可以正常操作,如果是在Nor Flash,修改无效。因此我们需要把全局变量和静态变量重定位 放到SDRAM


#include "s3c2440_soc.h"

#include "uart.h"

#include "init.h"


char g_Char = 'A';  //定义一个全局变量

const char g_Char2 = 'B'; //定义固定的全局变量

int g_A = 0;

int g_B;


int main(void)

{

    uart0_init();


    while (1)

    {

        putchar(g_Char); /*让g_Char输出*/

        g_Char++;         /* nor启动时, 此代码无效 */

        delay(1000000);

    }


    return 0;

}


编译运行查看是否有效果


查看sdram.dis文件 发现data数据段放在了0x00008474这个地址导致 程序太大


在makefile中加入这么一句话


arm-linux-ld -Ttext 0 ** -Tdata 0x700 ** start.o led.o uart.o init.o main.o -o sdram.elf


16进制的700就是十进制的2048 

这时我们的bin文件就变为2049


烧写程序:


烧写在NORFlash 和 烧写在NANDFlash观察这两种的效果。


设置成NANDFlash启动没有问题 显示ABCDE…


设置成NORFlash启动显示AAA…


对于NOR启动时g_Char++; /* nor启动时, 此代码无效 */


Disassembly of section .data:

00000700 <__data_start>:

 700:   Address 0x700 is out of bounds.  //数据段

Disassembly of section .rodata:

                            //放在只读数据段内

00000474 :         //const char g_Char2 = 'B';

 474:   Address 0x474 is out of bounds.


Disassembly of section .bss:    //bss段


00000804 :             //int g_A = 0;


 804:   00000000    andeq   r0, r0, r0


00000808 :             //int g_B;

 808:   00000000    andeq   r0, r0, r0

Disassembly of section .comment:


一个程序里面有


.text 代码段

.data 数据段

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

bss段 (初始值为0,无初始值的全局变量)

commen 注释

其中bss段和commen 注释不保存在bin文件中。


第002节_链接脚本的引入与简单测试

前面程序运行,发现从Nand Flash启动和从Nor Flash启动表现是不一样的。


设置成Nand Flash启动没有问题 显示ABCDE…


设置成NOor Flash启动则显示AAA…


这是什么原因呢?


假如现在是Nor启动: 

这里写图片描述

Nor Flash就被认为是0地址,g_Char被放在0x700后面。CPU上电后从0地址开始执行,它能读取Nor Flash上的代码,打印出A,当进行g_Char++的时候,写操作操作无效,下次读取的数据仍然是A。


假如现在是Nor启动: 

这里写图片描述

上电后,Nand Flash前4K代码就被自动的复制到SRAM里面,SRAM是CPU认为的0地址。CPU上电后从0地址开始执行,它读取SRAM上的代码,并g_Char++修改变量,下次读取的数据就依次增加了。

为了解决Nor Flash里面的变量不能写的问题,我们把变量所在的数据段放在SDRAM里面,看行不行。 

修改Makefile 指定数据段为0x30000000 -Tdata 0x30000000:


 arm-linux-ld -Ttext 0 -Tdata 0x30000000  start.o led.o uart.o init.o main.o -o sdram.elf


这样的话编译出来的bin文件 从0地址 到 0x30000000地址 文件大小有700多MB,代码段和数据段直接有间隔,称之为黑洞 

这里写图片描述

解决黑洞有两个办法:


第一个方法 

把数据段的g_Char和代码段靠在一起;

烧写在Nor Flash上面;

运行时把g_char(全局变量)复制到SDRAM,即0x3000000位置(重定位);

第二个方法 

让文件直接从0x30000000开始,全局变量在0x3……;

烧写Nor Flash上 0地址处;

运行会把整个代码段数据段(整个程序)从0地址复制到SDRAM的0x30000000(重定位);

这两个方法的区别是前者只重定位了数据段,后者重定位了数据段和代码段。


参考文档 

[http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html Using LD, the GNU linker]


第一种办法如何实现 

修改Makefile的代码段地址,使用链接脚本sdram.lds指定。


 #arm-linux-ld -Ttext 0 -Tdata 0x30000000  start.o led.o uart.o init.o main.o -o sdram.elf

 arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf


链接脚本的语法:


SECTIONS {

...

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

  { contents } >region :phdr =fill

...

}


我们需要依次排列 代码段、只读数据段、数据段、.bss段、.common。


其中数据段放在0x700,但运行时在0x3000000:


SECTIONS {

   .text   0  : { *(.text) }//所有文件的.text

   .rodata  : { *(.rodata) } //只读数据段

   .data 0x30000000 : AT(0x700) { *(.data) } //放在0x700,但运行时在0x3000000

   .bss  : { *(.bss) *(.COMMON) }//所有文件的bss段,所有文件的.COMMON段

}


重新编译后烧写bin文件,发现启动后显示乱码。原因是我们从0x30000000处获取g_Char,但在这之前,并没有在0x30000000处准备好数据。因此需要重定位数据段,将0x700的数据移动到0x30000000处,在start.S加入:


 bl sdram_init


 /* 重定位data段 */

 mov r1, #0x700 

 ldr r0, [r1]

 mov r1, #0x30000000

 str r0, [r1]


 bl main


上面的这种方法,只能复制0x700处的一位数据,不太通用,下面写一个更加通用的复制方法:


链接脚本修改如下:


SECTIONS {

   .text   0  : { *(.text) }

   .rodata  : { *(.rodata) }

   .data 0x30000000 : AT(0x700) 

   { 

      data_load_addr = LOADADDR(.data);

      data_start = . ;//等于当前位置

      *(.data)  //等于数据段的大小

      data_end = . ;//等于当前位置

   }

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

}


修改start.S


    bl sdram_init   


    /* 重定位data段 */

    ldr r1, =data_load_addr  /* data段在bin文件中的地址, 加载地址 */

    ldr r2, =data_start      /* data段在重定位地址, 运行时的地址 */

    ldr r3, =data_end        /* data段结束地址 */


cpy:

    ldrb r4, [r1] //从r1读到r4

    strb r4, [r2] //r4存放到r2

    add r1, r1, #1 //r1+1

    add r2, r2, #1 //r2+1

    cmp r2, r3 //r2 r3比较

    bne cpy //如果不等则继续拷贝


    bl main


第003节_链接脚本的解析

链接脚本的语法


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)文件


elf文件格式


1 链接得到elf文件,含有地址信息(load addr)


2 使用加载器


:: 2.1 对于裸板是JTAG调试工具


:: 2.2 对于APP,加载器也是APP 把elf文件解析读入内存的加载地址


3 运行程序


4 如果loadaddr != runtimeaddr程序本身要重定位


核心程序运行时应该位于 runtimeaddr(reloate addr)或者链接地址


bin文件


1 elf生成bin文件 


2 硬件机制启动


3 如果bin文件所在位置 不等于runtimeaddr ,程序本身实现重定位


bin文件/elf文件都不保存bss段 这些都是初始值为0 或者没有初始化的全局变量


程序运行时把bss段对应的空间清零


做个实验,把全局变量g_A以16进制打印出来


/* 0xABCDEF12 */

void printHex(unsigned int val)

{

    int i;

    unsigned char arr[8];


    /* 先取出每一位的值 */

    for (i = 0; i < 8; i++)

    {

        arr[i] = val & 0xf;

        val >>= 4;   /* arr[0] = 2, arr[1] = 1, arr[2] = 0xF */

    }


    /* 打印 */

    puts("0x");

    for (i = 7; i >=0; i--)

    {

        if (arr[i] >= 0 && arr[i] <= 9)

            putchar(arr[i] + '0');

        else if(arr[i] >= 0xA && arr[i] <= 0xF)

            putchar(arr[i] - 0xA + 'A');

    }

}


//打印初始值为0的变量

int g_A = 0;

int g_B;


int main(void)

{

uart0_i

[1] [2] [3]
关键字:S3c2440  代码重定位   编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic492328.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:第012课 内存控制器与SDRAM
下一篇:第014课 Jz2400_ARM异常与中断体系详解

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

S3C2440芯片的时钟体系结构
下图是S3C2440芯片的整体架构图:其中,AHB BUS为高速设备的总线,H即为high的意思。APB BUS为低速设备的总线,P为英文单词peripheral(外围设备)。不同的总线,挂载在上面的设备运行的频率肯定是不一样的,在我们这款S3C2440芯片中:Fclk就是CPU的运行频率,最高可达400MHzHclk为高速设备的运行频率,最高可达136MHzPclk为低速设备的运行频率,最高可达68MHz那么这三种时钟频率是怎么得到的,他们又是什么关系呢?S3C2440这款芯片的时钟源为一个12M的晶振,再配合PLL(锁相环)就可以获得相应的频率。这里我们不对PLL的工作原理做详细,有兴趣的可以自查(好吧,我承认我不会)。下面
发表于 2020-04-02
S3C2440芯片的时钟体系结构
S5PV210的LED应用(一)
准备分析 看似就一个LED,但是S5PV210不同于S3C2440,不是在于LED,而是在于从NandFlash启动的过程中不一样。对于S3C2440,只要程序没有问题,想办法下载程序到NandFlash的0地址处一般都是可以正常运行的,下载的方法无非采用开发板厂家下载BOOTLOADER的方法来下载。S5PV210启动时从NandFlash的0地址拷贝时候会拷贝前16k,但是会读取前16byte进行与和校验值做比较。所以略显麻烦。资源工具1.开发板:tiny210v22.关于头信息使用:mkv210_image.c3.交叉工具链:arm-linux-gcc-4.5.14.LED1对应的管脚:GPI2_0着手写程序
发表于 2020-03-31
s3c2440处理IRQ过程
,=HANDLE_HandlerPabort ;地址是0x0cLDR PC,=HANDLE_HandlerDabort ;地址是0x10LDR PC,. ;地址是0x14LDR PC,=OS_CPU_IRQ_ISR ;地址是0x18LDR PC,=HANDLE_HandlerFIQ ;地址是0x1c在0X00000018是LDR PC,=OS_CPU_IRQ_ISR这条指令,那么CPU就转而到OS_CPU_IRQ_ISR地址处执行。下面就是OS_CPU_IRQ_ISR函数的设置了,该函数(严格来说不是函数,只是汇编文件的一个地址标号)主要完成以下几个功能:保存被中断的Supervisor模式下运行的寄存器(任务上下文的保护)到Supervisor
发表于 2020-03-29
S3C2440时钟体系结构与编程
的外设来说是没有自己的晶振的,那么就需要cpu给他们提供时钟,然而不同的外设需要的时钟不一样,cpu是怎么给这些外设提供不同的时钟呢?S3C2440处理器主要给外设提供了两种连接总线,AHB总线(Advanced high Performance Bus)和APB总线,这两种总线的时钟在没有初始化之前是一样的,频率等于外部晶振提供的频率,在初始化以后,它们就可以为外设提供不同的时钟了。AHP和APB总线的内容将放到后面章节作为补充知识了解。由于这两种总线初始化以后,时钟是不一样的,所以连接的外设也不一样,如下图,一些工作频率较高的外设挂接在AHB总线上,比如内存,nandflash,LCD控制器等,而工作频率比较低的外设则挂接在APB
发表于 2020-03-16
S3C2440时钟体系结构与编程
嵌入式arm学习总结(七)--中断-基于S3C2440
1.中断执行过程1)保存中断前PC值2)保持中断前CPSR到spsr3)修改CPSR的相应值,进入相应异常模式4)执行异常5)现将SPSR还原到CPSR6)还原PC   ARM硬件不支持相同异常中断嵌套   如果相同异常嵌套,必须通过软件来保存CPSR和PC的值   不同异常中断硬件支持嵌套2.s3c2440的中断共60个终端源,有五种中断触发方式3.ARM中断响应过程--详细过程中断初始化总共分为7步,只要初始化后,只要触发中断,那么程序直接跳入中断服务函数第一步:配置I/O口为中断功能第二步:配置外部中断触发模式第三步: EINTPEND外中断挂起寄存器,清除时要写
发表于 2020-03-13
tiny4412裸机程序——代码重定位
。链接地址是程序的链接地址,即程序运行时应该位于的运行地址。编译程序时,可以在链接脚本中指定程序的链接地址。对于tiny4412而言,前面我们已经说过:启动时BL1只会从sd等启动设备中拷贝14K的代码到iRAM中,那么当我们的程序超过14K怎么办?那就需要我们在前14K的代码中将整个程序完完整整地拷贝到LPDDR等其他更大存储空间,然后再跳转到LPDDR中继续运行我们的代码,这个拷贝然后跳转的过程就叫重定位。本章中讲解学习如何重定位,但是并不会涉如何使用到LPDDR,而是简单地将代码从iRAM的0x02023400处拷贝到iRAM的0x02026400处,然后跳转到0x02026400处继续运行我们的代码。程序文件介绍
发表于 2020-03-25
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved