S3C2440,nor/nand flash代码拷贝到SDRAM

1,nor flash 向SDRM代码搬运

nor flash属于可以任意读但是不可以任意写,  如果我是用norflash启动,此时的0地址在norflash,此时片内4Ksram的地址是0x4000,0000.norflash可以像内存一样读,但是不能像内存一样的写。如果程序中含有需要更改的全局变量或者静态变量,全局变量是包含在bin文件中,烧在norflash上面(局部变量是在栈中在SRAM上面,可读可写没有问题)但是全局变量由于可读不可写的特性,无法对其进行值修改。

#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++;         /* nor启动时, 此代码无效 */
		delay(1000000);
	}
 
	
	return 0;
}


链接文件

代码重定位的关键,

1,cpu运行程序的运行地址

2,文件中程序的加载地址

3,代码重定位就是将程序的加载地址搬运到程序的运行地址

SECTIONS
{
   ...
   secname start BLOCK(align)(NOLOAD) : AT ( ldadr )
   { contents} 
   ...
}
secname:描述输出文件的段(可以随意去),比如.text、.data,first,second都可以
start:规定输出段的运行地址,即规定CPU从哪个地址去取指令、数据
BLOCK(align):地址对齐,一般4Byte对齐,ALIGN(4)
AT(ldadr):段在输出文件中的物理地址可以直接影响文件的大小,如果没有使用AT(ldadr),加载地址=start
contents:, 1,start.o

                  2,start.o *(.text)

                  3,*(.text)

SECTIONS {

   .text   0  : { *(.text) }

   .rodata  : { *(.rodata) }

   .data 0x30000000 : AT(0x800) { *(.data) }

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

上面的{}里面的*表示所有文件的.text段,所有文件的.rodata段。其中的AT表示at,也就是全局变量运行的时候是在0x30000000,但是在bin文件中我们把它放在0x800这里

代码重定位选择将代码和数据段都进行重定位。

SECTIONS {
. = 0x00000000;
.init : AT(0){ head.o init.o nand.o}
. = 0x30000000;
.text : AT(4096) { *(.text) }
.rodata ALIGN(4) : AT((LOADADDR(.text)+SIZEOF(.text)+3)&~(0x03)) {*(.rodata*)} 
.data ALIGN(4)   : AT((LOADADDR(.rodata)+SIZEOF(.rodata)+3)&~(0x03)) { *(.data) }
__bss_start = .;
.bss ALIGN(4)  : { *(.bss)  *(COMMON) }
__bss_end = .;
}

将head,init,nand等二进制文件放在内存0地址,cpu启动后率先运行,进行时钟,看门狗等操作,同时将代码考入SDRAM,这也就是所谓的uboot.

将剩下的代码段的运行地址设置为0x30000000,即sdram的首地址,比如将main.o放在这个位置,也就是说main函数是在sdram中运行的,同时AT加载地址可知,main函数实在文件2k以外的位置。

上述的操作是重定位了一部分的代码,只将一部分主要运行的程序放入了sdram

如果将. = 0x00000000改为. = 0x30000000;意味着将所有的代码都重定位到了sdram.

此时运行地址是0x30000000,不再是0地址,那么程序为什么依旧可以运行?

不管是nand或者nor启动,cpu都是从0地址开始运行,需要重定位之前的代码时位置无关码,也就是说head.o,init.o,nand.o运行于位置无关。

位置无关码

函数的调运通过B/BL来调用,他的跳转第一依赖当前PC。

从0运行,那么当前的bl指令地址是0x5c,然后跳转到0x04798.如果是从0x32000000开始运行,那么bl指令地址是0x3200005c,然后跳转到0x32000478.所以这里的bl命令并不是跳转到这个地址,具体跳转到那里是取决于当前PC的位置。在反汇编里面写出这个值只是为了方便你去分析代码而已。总结:在反汇编文件里面,B/BL某个值,只是起一个方便查看的作用,并不是真正跳转到这个地址
想要跳转到重定位代码不能使用bl main ,而要使用ldr pc,=main

  1. 使用相对跳转命令,B/BL。
  2. 重定位之前不可使用绝对地址,不可访问全局变量/静态变量,不可访问有初始值的数组(因为初始值放在rodata里面,使用绝对地址来访问)。
  3. 重定位之后,使用ldr pc, =xxx跳转到运行地址

清楚bss段

存在字节对其问题,在各个段之前加入. = ALIGN(4);这样进行str4字节操作防止取值错误,strb不存在这种情况,但是会增加读写次数,不推荐。

SECTIONS
{
    . = 0x30000000;
    . = ALIGN(4);
    .text      : { *(.text) }
    . = ALIGN(4);
    .rodata : { *(.rodata) }
    . = ALIGN(4);
    .data : { *(.data) }
    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss) *(.COMMON) }
    _end = .;
}

 bin和elf文件不会包含bss段,我们通过链接脚本知道bss的运行地址,只需要将bss清空即可。灰白你文件并没拷贝bss的程序,代码重定位只定位了text,rodata,data,这也就是说代码重定位的终止地址是bss_start


.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

C语言实现代码复制和bss清除

方法一:将值传入r0,r1寄存器,调用c函数

方法二:直接通过extern 声明外部变量

````c
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;
	}
}

.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
	//bl sdram_init2	 /* 用到有初始值的数组, 不是位置无关码 */

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

	/* 清除BSS段 */
	bl clean_bss

	//bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
	ldr pc, =main  /* 绝对跳转, 跳到SDRAM */

halt:
	b halt
	

2,nand flash代码一直到SDRAM

NOR flash代码重定位只能定位2M的,大于2M的bin文件无法完全烧写。nand flash不存在这个问题,但是nand flash读写都需要命令nor flash的写需要先擦除再写,读可以直接读,因此从nand flash搬运需要先初始化nand flash。

链接脚本的变化并不大,依旧运行位置和加载位置。

实现难点:将nand flash4096处之后的代码搬运到SDRAM

代码复制函数

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;
	int len;

	len = ((int)&__bss_start) - ((int)&__code_start);

	if (isBootFromNorFlash())		//判断是nor还是nand启动
	{//nor启动
		while (dest < end)
		{
			*dest++ = *src++;
		}
	}
	else
	{//nand启动
		nand_init();
		nand_read(src, dest, len);//从nand复制代码到SDRAM
	}
}

nand flash操作函数

#include "s3c2440_soc.h"
#include "printf.h"

void nand_init(void)
{
#define  TACLS   0
#define  TWRPH0  1
#define  TWRPH1  0
	/*设置NAND FLASH的时序*/
	NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
	/*使能NAND FLASH控制器,初始化ECC,禁止片选*/
	NFCONT = (1<<4) | (1<<1) | (1<<0);
}

void nand_select(void)
{
	/*使能片选*/
	NFCONT &=~(1<<1);
}

void nand_deselect(void)
{
	/*禁止片选*/
	NFCONT |= (1<<1);
}

void nand_cmd(unsigned char cmd)
{
	volatile int i;
	NFCCMD = cmd;
	for(i=0; i<10; i++);
}

void nand_addr_byte(unsigned char addr)
{
	volatile int i;
	NFADDR = addr;
	for(i=0; i<10; i++);
}

unsigned char nand_data(void)
{
	return	NFDATA;
}

void nand_w_data(unsigned char val)
{
	NFDATA = val;
}


void wait_ready(void)
{
	while (!(NFSTAT & 1));
}

void nand_chip_id(void)
{ 
	unsigned char buf[5]={0};
	
	nand_select(); 
	nand_cmd(0x90);
	nand_addr_byte(0x00);

	buf[0] = nand_data();
	buf[1] = nand_data();	
	buf[2] = nand_data();
	buf[3] = nand_data();
	buf[4] = nand_data();	
	nand_deselect(); 	

	printf("maker   id  = 0x%x\n\r",buf[0]);
	printf("device  id  = 0x%x\n\r",buf[1]);	
	printf("3rd byte    = 0x%x\n\r",buf[2]);		
	printf("4th byte    = 0x%x\n\r",buf[3]);			
	printf("page  size  = %d kb\n\r",1  <<  (buf[3] & 0x03));	
	printf("block size  = %d kb\n\r",64 << ((buf[3] >> 4) & 0x03));	
	printf("5th byte    = 0x%x\n\r",buf[4]);

	
}


void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
	int i = 0;
	int page = addr / 2048;
	int col  = addr & (2048 - 1);
	
	nand_select(); 

	while (i < len)
	{
		/* 发出00h命令 */
		nand_cmd(00);

		/* 发出地址 */
		/* col addr */
		nand_addr_byte(col & 0xff);
		nand_addr_byte((col>>8) & 0xff);

		/* row/page addr */
		nand_addr_byte(page & 0xff);
		nand_addr_byte((page>>8) & 0xff);
		nand_addr_byte((page>>16) & 0xff);

		/* 发出30h命令 */
		nand_cmd(0x30);

		/* 等待就绪 */
		wait_ready();

		/* 读数据 */
		for (; (col < 2048) && (i < len); col++)
		{
			buf[i++] = nand_data();	//nand flash 同一页的数据可以连读,下一页要重新发送
                                    //行地址和列地址		
		}
		if (i == len)
			break;

		col = 0;
		page++;
	}
	
	nand_deselect(); 	
}


int nand_erase(unsigned int addr, unsigned int len)
{
	int page = addr / 2048;

	if (addr & (0x1FFFF))
	{
		printf("nand_erase err, addr is not block align\n\r");
		return -1;
	}
	
	if (len & (0x1FFFF))
	{
		printf("nand_erase err, len is not block align\n\r");
		return -1;
	}
	
	nand_select(); 

	while (1)
	{
		page = addr / 2048;
		
		nand_cmd(0x60);
		
		/* row/page addr */
		nand_addr_byte(page & 0xff);
		nand_addr_byte((page>>8) & 0xff);
		nand_addr_byte((page>>16) & 0xff);

		nand_cmd(0xD0);

		wait_ready();

		len -= (128*1024);
		if (len == 0)
			break;
		addr += (128*1024);
	}
	
	nand_deselect(); 	
	return 0;
}

void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
	int page = addr / 2048;
	int col  = addr & (2048 - 1);
	int i = 0;

	nand_select(); 

	while (1)
	{
		nand_cmd(0x80);

		/* 发出地址 */
		/* col addr */
		nand_addr_byte(col & 0xff);
		nand_addr_byte((col>>8) & 0xff);
		
		/* row/page addr */
		nand_addr_byte(page & 0xff);
		nand_addr_byte((page>>8) & 0xff);
		nand_addr_byte((page>>16) & 0xff);

		/* 发出数据 */
		for (; (col < 2048) && (i < len); )
		{
			nand_w_data(buf[i++]);
		}
		nand_cmd(0x10);
		wait_ready();

		if (i == len)
			break;
		else
		{
			/* 开始下一个循环page */
			col = 0;
			page++;
		}
		
	}
	
	nand_deselect(); 	
}

void do_read_nand_flash(void)
{
	unsigned int addr;
	volatile unsigned char *p;
	int i, j;
	unsigned char c;
	unsigned char str[16];
	unsigned char buf[64];
	
	/* 获得地址 */
	printf("Enter the address to read: ");
	addr = get_uint();

	nand_read(addr, buf, 64);
	p = (volatile unsigned char *)buf;

	printf("Data : \n\r");
	/* 长度固定为64 */
	for (i = 0; i < 4; i++)
	{
		/* 每行打印16个数据 */
		for (j = 0; j < 16; j++)
		{
			/* 先打印数值 */
			c = *p++;
			str[j] = c;
			printf("%02x ", c);
		}

		printf("   ; ");

		for (j = 0; j < 16; j++)
		{
			/* 后打印字符 */
			if (str[j] < 0x20 || str[j] > 0x7e)  /* 不可视字符 */
				putchar('.');
			else
				putchar(str[j]);
		}
		printf("\n\r");
	}
}

void do_erase_nand_flash(void)
{
	unsigned int addr;
	
	/* 获得地址 */
	printf("Enter the address of sector to erase: ");
	addr = get_uint();

	printf("erasing ...\n\r");
	nand_erase(addr, 128*1024);
}


void do_write_nand_flash(void)
{
	unsigned int addr;
	unsigned char str[100];
	int i, j;
	unsigned int val;
	
	/* 获得地址 */
	printf("Enter the address of sector to write: ");
	addr = get_uint();

	printf("Enter the string to write: ");
	gets(str);

	printf("writing ...\n\r");
	nand_write(addr, str, strlen(str)+1);

}


void nand_flash_test(void)
{
	char c;

	while (1)
	{
		/* 打印菜单, 供我们选择测试内容 */
		printf("[s] Scan nand flash\n\r");
		printf("[e] Erase nand flash\n\r");
		printf("[w] Write nand flash\n\r");
		printf("[r] Read nand flash\n\r");
		printf("[q] quit\n\r");
		printf("Enter selection: ");

		c = getchar();
		printf("%c\n\r", c);

		/* 测试内容:
		 * 1. 识别nand flash
		 * 2. 擦除nand flash某个扇区
		 * 3. 编写某个地址
		 * 4. 读某个地址
		 */
		switch (c)		 
		{
			case 'q':
			case 'Q':
				return;
				break;
				
			case 's':
			case 'S':
				nand_chip_id();
				break;

			case 'e':
			case 'E':
				do_erase_nand_flash();
				break;

			case 'w':
			case 'W':
				do_write_nand_flash();
				break;

			case 'r':
			case 'R':
				do_read_nand_flash();
				break;
			default:
				break;
		}
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值