代码重定位(S3C2440)

nand启动,需要重定位
S3C2440的CPU可以直接给SDRAM发送命令、给Nor Flash发送命令、给4K的片上SDRAM发送命令,但是不能直接给Nand Flash发送命令,CPU无法直接访问Nand Flash,因为之间还隔了个Nand Flash控制器。

:那为什么还可以nand启动?
:1.nand启动时,前4k代码由硬件自动复制到SRAM。2.此时SRAM的基地址为0地址,CPU从SRAM的0地址开始运行。

:那么问题来了,烧录至Nand Flash的bin文件大于4k怎么办?
:前4k代码需要把整个程序读出,复制到SDRAM。然后去SDRAM执行程序。

把程序从nand读出复制到SDRAM的过程就叫做重定位。
注:1.nand启动时,SRAM地址为0。
2.nor启动时,SRAM地址为0x40000000。

nor启动,也需要重定位
CPU可以像读内存一样直接读Nor Flash,但是不能像写内存似的直接写Nor Flash。
程序中的全局变量、静态变量,它们在bin文件中,写在Nor Flash中,若直接修改它们,则无效。所以,也需要重定位来把程序读出,复制到SDRAM。就可以直接读和写了。

综上所述,无论是nand启动还是nor启动,都需要代码重定位。

段的概念:
一个程序里面有

  • .text 代码段
  • .data 数据段
  • rodata 只读数据段(const全局变量)
  • bss段 (初始值为0,无初始值的全局变量)
  • commen 注释
    其中bss段和commen 注释不保存在bin文件中。

链接脚本
作用:将若干个输入文件(.o文件)按照一定的规则合并为一个输出文件。

SECTIONS
{
	. = 0x30000000;

	__code_start = .;

	. = ALIGN(4);
	.text      :
	{
	  *(.text)
	}

	. = ALIGN(4);
	.rodata : { *(.rodata) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	__bss_start = .;
	.bss : { *(.bss) *(.COMMON) }
	_end = .;
}

上面链接脚本就把程序的首地址定位0x30000000,0x30000000就是SDRAM的基地址。

编程
目的:把整个程序从NOR Flash 重定位至SDRAM。

把整个程序复制到SDRAM需要哪些技术细节:

  1. 把程序从Flash复制到运行地址,链接脚本中就要指定运行地址为SDRAM地址;
  2. 编译链接生成的bin文件,需要在SDRAM地址上运行,但上电后却必须先在0地址运行,这就要求重定位之前的代码与位置无关(是位置无关码);

将修改后的代码重新编译烧写在Nor Flash上,上电运行。 对本代码的启动情况进行分析:
在这里插入图片描述
在生成的bin文件里,代码保存的位置是0x30000000。随后烧写到NOR Flash的0地址,但代码的结构没有变化。之后再重定位到SDRAM。
怎么写位置无关码?

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

  • 重定位之前,不可使用绝对地址,不可访问全局变量/静态变量,也不可访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问);
  • 重定位之后,使用ldr pc = xxx,跳转到/runtime地址;
  • 写位置无关码,其实就是不使用绝对地址,判断有没有使用绝对地址,除了前面的几个规则,最根本的办法看反汇编。

重定位需要的几个函数:
sdram_init(); //初始化SDRAM,为后面复制代码过来做准备
copy2sdram(); //把Nor Flash中的代码复制到SDRAM
clean_bss(); //清除bss段
代码如下:

void sdram_init(void)
{
	BWSCON = 0x22000000;

	BANKCON6 = 0x18001;
	BANKCON7 = 0x18001;

	REFRESH  = 0x8404f5;

	BANKSIZE = 0xb1;

	MRSRB6   = 0x20;
	MRSRB7   = 0x20;
}


void copy2sdram(void)
{
	/* 要从lds文件中获得 __code_start, __bss_start
	 * 然后从0地址把数据复制到__code_start
	 */

	extern int __code_start, __bss_start;

	volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
	volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
	volatile unsigned int *src = (volatile unsigned int *)0;

	while (dest < end)
	{
		*dest++ = *src++;
	}
}


void clean_bss(void)
{
	/* 要从lds文件中获得 __bss_start, _end
	 */
	extern int _end, __bss_start;

	volatile unsigned int *start = (volatile unsigned int *)&__bss_start;
	volatile unsigned int *end = (volatile unsigned int *)&_end;


	while (start <= end)
	{
		*start++ = 0;
	}
}

这些函数要在程序运行开始的时候调用(就是调用main之前调用),在start.S调用。
所以start.S要增加代码为:

/*
 *文件:start.S
 */
.text
.global _start

_start:

	/* 关闭看门狗 */
	ldr r0, =0x53000000
	ldr r1, =0
	str r1, [r0]

	/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
	/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
	ldr r0, =0x4C000000
	ldr r1, =0xFFFFFFFF
	str r1, [r0]

	/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
	ldr r0, =0x4C000014
	ldr r1, =0x5
	str r1, [r0]

	/* 设置CPU工作于异步模式 */
	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
	mcr p15,0,r0,c1,c0,0

	/* 设置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
	 */
	ldr r0, =0x4C000004
	ldr r1, =(92<<12)|(1<<4)|(1<<0)
	str r1, [r0]

	/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
	 * 然后CPU工作于新的频率FCLK
	 */
	
	

	/* 设置内存: sp 栈 */
	/* 分辨是nor/nand启动
	 * 写0到0地址, 再读出来
	 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
	 * 否则就是nor启动
	 */
	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
	

	/* 重定位text, rodata, data段整个程序 */
	bl copy2sdram

	/* 清除BSS段 */
	bl clean_bss
	
	ldr pc, =sdram  /* 跳转到SDRAM执行 */
sdram:	
	bl main  

halt:
	b halt
	
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值