重定位 (二)

重定位二:
上一节说了:
为什么需要重定位?
重定位是什么?
怎么实现重定位?(只是实现了某个全局变量或者说数据段的一个字节重定位)

本节目标:
实现整个代码的重定位:

这里只列举nor启动重定位全部代码,因为nor可以想内存一样直接读取;
nand 读取方式是通过io外接接口和内存读写方式不同,之后介绍了nand读写方式后再补充;
ok分析思路:
首先:
1、我们要重定位代码那这个函数的参数肯定要先确定:
整个代码的起始位置(src)和重定位后的位置(dest) 还有就是拷贝多长的数据(length);于是我们得到了函数的原型如下:

define vui  (volatile unsigned int )
void relacate_to_sdram(vui  * src,  vui * dest, int length);

2、得到了函数原型后函数很容易实现:

void relacate_to_sdram(vui  * src,  vui * dest, int length)
{
	int i =0; 
	for (i=0; i<length; i+=sizeof(vui *))
	{
		*dest++ = *src ++;
	}	
}	

3、参数从哪里来呢??
src:代码起始位置,这个我们知道,arm起始地址都是0
dest:目标位置是sdram的起始位置0x30000000
length:长度如何确认呢?
上一节我们提到过连接脚本:
如下来自上一节的连接脚本:

SECTIONS {
   .text 0 : {*(.text)}  //指定地址为0 开始存放代码段,所有文件代码段放在这里
   .rodata : {*(.rodata)} //所有文件只读段放在代码段之后
   .data 0x30000020 : AT(0xf80){*(.data)} //指定所有文件数据段 加载地址为0xf80(即烧写到0xf80的位置, 运行的时候在0x30000020处操作)
   .bss : {*(.bss) *.commen}
}

现在我们修改连接脚本使用变量:

SECTIONS {
   . = 0x30000000; //指定当前地址为sdram的起始地址
   _sdram_start = . //定义一个变量_sdram_start 赋值为当前地址0x30000000
   .text : {*(.text)}  //所有文件代码段放在这里
   .rodata : {*(.rodata)} //所有文件只读段放在代码段之后
   .data : {*(.data)} //指定所有文件数据段 
   _bss_start = . // 同理定义变量_bss_start 为bss段的起始地址
   .bss : {*(.bss) *.commen}
   _bss_end = .  //定义——bss——end为bss段的结束地址
}

引入了一个问题: 如何使用这些变量?
有两种方式: (这个知识点不解释了,比较繁琐还挺多可以查询sysbol table 相关知识)
a) 在汇编文件中可以直接访问变量
如: ldr r0, =_sdram_start //r0=0x30000000
b)在c语言文件中使用:
1、使用extern 外部声明变量 (存放在 sysbol table中)不占据 c文件空间
如: extern _bss_end, _bss_start;
2、怎么使用这些外部声明变量: 使用取地址符
volatile unsigned int *src = (volatile unsigned int * ) _bss_end;
volatile unsigned int *dest = (volatile unsigned int * )& _bss_start;
这样一来我们定义的函数可以优化了:变量直接定义在连接脚本,然后c语言里使用就好了;

ok下面是代码实现:
连接脚本:

//关闭看门狗:
ldr r0, =0x53000000
 ldr r1, =0
 str r1, [r0]
 
 //设置时钟: 设置锁相环 MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m
 //LOCKTIME(0x4C000000) = 0xFFFFFFFF
 //设置CPU工作于异步模式
 //设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 
 // m = MDIV+8 = 92+8=100
 //p = PDIV+2 = 1+2 = 3
 //s = SDIV = 1
 //FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
 //设置 栈 方便调用c函数
 mov r1, #0
 ldr r0, [r1] /* 读出原来的值备份 */
 str r1, [r1] /* 0->[0] */ 
 ldr r2, [r1] /* r2=[0] */
 cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
 ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
 moveq sp, #4096  /* nand启动 */
 streq r0, [r1]   /* 恢复原来的值 */
 //中间这些代码上节都有所以省略了:
bl sdram_init  //sdram的初始化 这里调用,不然重定位到sdram后也没办法访问
bl copy2sdram 
bl clean_bss  
//bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
 ldr pc, =main  /* 绝对跳转, 跳到SDRAM   这个很重要,我在写的时候就找了很久错误 */

copy2sdram
clean_bss
sdram_init 三个函数的实现:

void clear_bss ()  // start  length 
{
 extern  _bss_start, _bss_end;
 volatile unsigned int *start = (volatile unsigned int *)&_bss_start;
 volatile unsigned int *end = (volatile unsigned int *)&_bss_end;
 while(start != end)
 {
  *start = 0;
  start++;
 }
}
void relacate_to_sdram(void)  // src   dest  length 
{
 extern _run_start;
 extern _bss_start;
 extern _start;
 //使用连接脚本中的变量 通过取地址获取;
 volatile unsigned int *src = (volatile unsigned int * )0;
 volatile unsigned int *dest = (volatile unsigned int * )&_run_start;
 volatile unsigned int *bss_start = (volatile unsigned int *)&_bss_start;
 while (dest <= bss_start )
 {
  *dest++ = *src++;
 }
}

void sdram_init()
{
 BWSCON= 0x02000000;
 BANKCON6 = 0x00018001;
 REFRESH |= ((0x4f5<<0)|(01<<18)|(1<<23));
 BANKSIZE = 0xb0;
 MRSRB6 = 0x20;
}

之后烧写至开发板:设置为nor启动,速度挺快;

这里重点说下这个语句:

汇编文件中最后跳转到main函数执行:
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main 绝对跳转, 跳到SDRAM 这个很重要
个人理解:
因为代码已经重定位了,所以执行的顺序是:
代码开始是在0地址启动运行,之后调用了 relacate_to_sdram 这个只是把nor中烧写的数据拷贝到了sdram,但是运行还是在nor中运行;
因为我们代码可能涉及对全局变量、静态变量值修改,所以要跳转到内存sdran中执行;
bl main ;执行这个指令时还是在nor中,跳转到pc+(代码执行的0 到 当前地址的偏移量) 实际上还是在 nor中执行;没有按我们想的跳转到内存中执行

ldr pc, =mian //绝对跳转,代码转到main的运行地址;就是在连接脚本中指定起始地址为0x30000000;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值