uboot移植---代码重定位

上篇分析了lowlevel_init,但是没有初始化UART,NAND这次将初始化NAND,UART并将代码重定位到内存

由于我的重定位代码是用C语言写的所以需要设置栈,看看start.s中的代码,他已近帮我们设置了栈,并且清除了BSS段

ps:当发现程序运行不正确且串口未初始化时,可以在一些地方点亮LED看代码执行情况,4个LED有16种状态,每当程序执行到一个地方时可以让灯变换一种状态

skip_hw_init:
	/* Set up the stack						    */
stack_setup:
	ldr	r0, =CONFIG_SYS_UBOOT_BASE				/* base of copy in DRAM	    */
	sub	r0, r0, #CONFIG_SYS_MALLOC_LEN			/* malloc area                      */
	sub	r0, r0, #CONFIG_SYS_GBL_DATA_SIZE 		/* bdinfo                        */
	sub	sp, r0, #12												/* leave 3 words for abort-stack    */
	
/* 清除BSS段 */
clear_bss:
	ldr	r0, _bss_start		/* find start of bss segment        */
	ldr	r1, _bss_end		/* stop here                        */
	mov 	r2, #0			/* clear                            */
clbss_l:
	str	r2, [r0]		/* clear loop...                    */
	add	r0, r0, #4
	cmp	r0, r1
	ble	clbss_l
所以我们直接在后面调用我们的初始化和重定位代码即可,但是在重定位之前需要确认一下代码是否在MDDR中

stack_setup:
	ldr	r0, =CONFIG_SYS_UBOOT_BASE	/* base of copy in DRAM	    */
	sub	r0, r0, #CONFIG_SYS_MALLOC_LEN	/* malloc area                      */
	sub	r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo                        */
	sub	sp, r0, #12		/* leave 3 words for abort-stack    */

/* 清除BSS段 */
clear_bss:
	ldr	r0, _bss_start		/* find start of bss segment        */
	ldr	r1, _bss_end		/* stop here                        */
	mov 	r2, #0			/* clear                            */
clbss_l:
	str	r2, [r0]		/* clear loop...                    */
	add	r0, r0, #4
	cmp	r0, r1
	ble	clbss_l	
<span style="white-space:pre">	</span>/* 添加如下的判断代码 */
	adr r0,_start			/* 获得_start相对于pc指针的值,即现在运行的代码中_start在内存中的地址 */
	ldr r1,=_TEXT_BASE		/* 获得_start的连接地址,即编译连接时指定的地址 */
	cmp r0,r1			/* 比较两者是否相等,若相等则认为在MDDR中运行,不相等则在片内RAM中运行 */
	beq goto_start			/* 相等则转跳到goto_start执行 */

确认之后,若在片内RAM执行则进行重定位

skip_hw_init:
	/* Set up the stack						    */
stack_setup:
	ldr	r0, =CONFIG_SYS_UBOOT_BASE				/* base of copy in DRAM	    */
	sub	r0, r0, #CONFIG_SYS_MALLOC_LEN			/* malloc area                      */
	sub	r0, r0, #CONFIG_SYS_GBL_DATA_SIZE 		/* bdinfo                        */
	sub	sp, r0, #12												/* leave 3 words for abort-stack    */
	
/* 清除BSS段 */
clear_bss:
	ldr	r0, _bss_start		/* find start of bss segment        */
	ldr	r1, _bss_end		/* stop here                        */
	mov 	r2, #0			/* clear                            */
clbss_l:
	str	r2, [r0]		/* clear loop...                    */
	add	r0, r0, #4
	cmp	r0, r1
	ble	clbss_l

	<pre name="code" class="cpp"><span style="white-space:pre">	</span>adr r0,_start			/* 获得_start相对于pc指针的值,即现在运行的代码中_start在内存中的地址 */
	ldr r1,=_TEXT_BASE		/* 获得_start的连接地址,即编译连接时指定的地址 */
	cmp r0,r1			/* 比较两者是否相等,若相等则认为在MDDR中运行,不相等则在片内RAM中运行 */
	beq goto_start			/* 相等则转跳到goto_start执行 */

copy2ddr:
	bl	nandInit_ll 
	bl	uart0Init 
	mov	r0,#0x0
	ldr	r1,=__bss_start
	ldr	r2,=_start	
	sub	r2,r1,r2	
	ldr	r1,=CONFIG_SYS_PHY_UBOOT_BASE
	bl	nand2ddr
	
/*outcopy:	
	ldr r0,=__bss_start
	ldr r1,=_start
	sub r1,r0,r1
	ldr 	r0,=CONFIG_SYS_PHY_UBOOT_BASE
	bl	returnCopyCode
*/

goto_start:
#ifndef CONFIG_NAND_SPL
/* 转跳到start_armboot去执行,正常来说跳转过去后是不会回来了的,所以后面的代码不用看了 */
	ldr r0,=0x7f008824					/* 点亮1、3号LED调试 */
	mov r1,#0x05	
	str r1,[r0]

	ldr	pc, _start_armboot

_start_armboot:
	.word start_armboot
#else
	b	nand_boot
/*	.word nand_boot*/
#endif

 

nandInit_ll、uart0Init和copy2ddr在哪定义呢?

想一想每一款开发板使用的NAND是否是相同的?当然不一定相同啊!那NAND就应该是一个板级驱动,应该是放在

/board/samsung/smdk6410下的文件,为了简单讲uart.c也放在该目录下了

那我们新建一个nand_flash.c文件,里面代码如下

#define	NFCONF  	(*((volatile unsigned long*)0x70200000))
#define	NFCONT  	(*((volatile unsigned long*)0x70200004))
#define	NFCMMD  	(*((volatile unsigned long*)0x70200008))
#define	NFADDR  	(*((volatile unsigned long*)0x7020000C))
/* NFDATA以字节访问时要定义成 (volatile unsigned char*) */
/* NFDATA以字访问时要定义成 (volatile unsigned long*) */
#define	NFDATA  	(*((volatile unsigned char*)0x70200010))
#define	NFMECCD0  	(*((volatile unsigned long*)0x70200014))
#define	NFMECCD1  	(*((volatile unsigned long*)0x70200018))
#define	NFSECCD  	(*((volatile unsigned long*)0x7020001C))
#define	NFSBLK  		(*((volatile unsigned long*)0x70200020))
#define	NFEBLK	 	(*((volatile unsigned long*)0x70200024))
#define	NFSTAT  		(*((volatile unsigned long*)0x70200028))
#define	NFECCERR0  	(*((volatile unsigned long*)0x7020002C))
#define	NFECCERR1  	(*((volatile unsigned long*)0x70200030))
#define	NFMECC0  	(*((volatile unsigned long*)0x70200034))
#define	NFMECC1  	(*((volatile unsigned long*)0x70200038))
#define	NFSECC  		(*((volatile unsigned long*)0x7020003C))
#define MEM_SYS_CFG	(*((volatile unsigned long*)0x7E00F120))

#define NAND_DIS_CS	(NFCONT |= (1<<1))
#define NAND_CS 		(NFCONT &= ~(1<<1))
#define NAND_CMD(x)	(NFCMMD = (unsigned char)x)
#define NAND_ADR(x) 	(NFADDR = (unsigned char)x) 
#define NAND_GET		((unsigned char)NFDATA)
#define NAND_SEND(x) 	(NFDATA = (unsigned char)x)
#define NAND_PAGE_SIZE 4096

static void nandWaitReady(void)
{
	while ((NFSTAT & 0x1) == 0);
}

/* 具体的地址发送规则参考NAND的参考手册 */
static void nandSendAddr(unsigned int addr)
{
	unsigned int column_addr = addr%4096;
	unsigned char page_addr =  addr/4096%256;		
	unsigned int  block_addr =  addr/4096/256;
	//发送地址
	//分5次发送
	NAND_ADR(column_addr&0xff);									
	NAND_ADR((column_addr&0x1f00)>>8);
	NAND_ADR(page_addr);
	NAND_ADR(block_addr&0xff);									
	NAND_ADR((block_addr&0x300)>>8);
}

static void nandReset(void)
{
	//select nand flash
	NAND_CS;
	//send reset command
	NAND_CMD(0xff);
	//wait for nand R/B high
	nandWaitReady();
	//Release CS
	NAND_DIS_CS;
}

void nandInit_ll(void)
{
		MEM_SYS_CFG &= ~(1<<1);		//Xm0CSn2作为NAND片选
		NFCONF &= ~((7<<12)|(1<<30));
		NFCONF |= (6<<8)|(2<<4); 
		NFCONT &= ~(1<<16);
		NFCONT |= 1;
		nandReset();
}
	
/**********************************************************
**		NFDATA使用字节模式访问
***********************************************************/

static void nandRead_ll(unsigned int srcAddr,unsigned char* dstAddr,unsigned int len)
{
	unsigned int i;
	unsigned int count=0;
	NAND_CS;
	//页读命令
	while(count<len)
	{
		NAND_CMD(0x00);
		nandSendAddr(srcAddr+count);
		NAND_CMD(0x30);
		//等待就绪
		nandWaitReady();

		for(i=0;i<NAND_PAGE_SIZE&&count<len;i++)
			dstAddr[count++]=NAND_GET;
	}
	NAND_DIS_CS;
}



void nand2ddr(unsigned int srcAddr,unsigned char* dstAddr,unsigned int len)
{
	/*	先复制前8K的代码 ,再复制后面的所有代码 
	 *	因为在6410中,上电后6410会将NAND前8K内容复制到片内RAM
	 *	而6410并不是连续复制8K内容,而是将第1---4页中的每页前2K
	 *	内容复制到片内RAM组成8K内容。
	 */
	/* 复制前8K内容每次复制2K */
	unsigned int i;
	for(i=0;i<4;i++)
	{
		nandRead_ll(srcAddr+i*NAND_PAGE_SIZE,dstAddr+i*2048,2048);
	}
	len-=0x2000;	//	len = len - 8K
	nandRead_ll(srcAddr+0x4000,dstAddr+0x2000,len);
}

uart.c代码如下

#define ULCON0	(*((volatile unsigned long *)0x7f005000))
#define UCON0	(*((volatile unsigned long *)0x7f005004))
#define UFCON0	(*((volatile unsigned long *)0x7f005008))
#define UMCON0	(*((volatile unsigned long *)0x7f00500c))
#define UTRSTAT0	(*((volatile unsigned long *)0x7f005010))
#define UERSTAT0	(*((volatile unsigned long *)0x7f005014))
#define UFSTAT0	(*((volatile unsigned long *)0x7f005018))
#define UMSTAT0	(*((volatile unsigned long *)0x7f00501c))
#define UTXH0		(*((volatile unsigned char *)0x7f005020))
#define URXH0	(*((volatile unsigned char *)0x7f005024))
#define UBRDIV0	(*((volatile unsigned short*)0x7f005028))
#define UDIVSLOT0	(*((volatile unsigned short*)0x7f00502c))
#define UINTP0	(*((volatile unsigned long *)0x7f005030))
#define UINTSP0	(*((volatile unsigned long *)0x7f005034))
#define UINTM0	(*((volatile unsigned long *)0x7f005038))

void uart0Init(void)
{
	ULCON0 =0X03;
	UCON0 = 0X05|(2<<10);		//从EXT_CLK0切换到PCLK时 时钟位设置为 00  从EXT_CLK1切换到PCLK时设置为11
	UFCON0=0X01;
	UMCON0 = 0;
	UBRDIV0=35;
	UDIVSLOT0=0X01;
}

static void uart0TransByte(unsigned char ch)
{
	while(UFSTAT0&0x4000);
	UTXH0=ch;
}

void uart0TransString(unsigned char *str)
{
	while(*str++!='\0')
		uart0TransByte(*str);
}


void returnCopyCode(unsigned char* srcAddr,unsigned int size)
{
	unsigned int i;
	
	for(i=0;i<size;i++)
	{
		uart0TransByte(srcAddr[i]);
	}
}

文件是添加了,怎么才能让它编译进uboot呢?当然是Makefile了,在/board/samsung/smdk6410目录下的Makefile中

COBJS-y := smdk6410.o 改为 COBJS-y := smdk6410.o nand_flash.o uart.o

为了使nand_flash.c,uart.c的代码不至于链接到8K以外的空间而导致调用时程序跑飞的问题,将该目录下的链接脚本

	.text      :
	{
	  cpu/arm1176/start.o	(.text)
	  cpu/arm1176/s3c64xx/cpu_init.o	(.text)
	  board/samsung/smdk6410/lowlevel_init.o	(.text)
	  board/samsung/smdk6410/nand_flash.o	(.text)
	  board/samsung/smdk6410/uart.o	(.text)
	  *(.text)
	}

但是编译后会发现有错


提示我们没有找到nandInit_ll、copy2ddr和uart0Init的定义,可是明明定义了啊!还加入了Makefile,怎么回事呢?

那我们进入这个目录看看


发现都是链接文件,而且只有nand_flash.c和uart.c没有生成.o文件,而且这个目录下还有一个Makefile,打开看看

发现其中有这样一句 COBJS = nand_boot.o nand_ecc.o s3c64xx.o难道是这里没有加nand_flash.o uart.o的原因

加上试试COBJS = nand_boot.o nand_ecc.o s3c64xx.o nand_flash.o uart.o 编译,又出现新的情况


这肯定是Makefile中的规则出了问题,再次打开该目录下的Makefile,发现了这样的东西


仿照他的格式将nand_flash.c 和 uart.c加进去

# from board directory
$(obj)nand_flash.c:
	@rm -f $@
	@ln -s $(TOPDIR)/board/samsung/smdk6410/nand_flash.c $@

# from board directory
$(obj)uart.c:
	@rm -f $@
	@ln -s $(TOPDIR)/board/samsung/smdk6410/uart.c $@

编译,,OK,但是发现烧到板子上还是不能运行。。。。。。。进过检查发现犯了一个低级错误UART0的引脚未初始化

在uart.c宏定义中添加一句

#define GPACON (*((volatile unsigned long *)0x7f008000))

uart0Init中添加一句

GPACON=0x220022;

改后UBOOT能打印出东西了,uboot的移植已经成功了一半


下一步使NAND能不识别和使用!

ps:如果你有时编译不通过的,又实在找不到错误原因可以执行 make distclean然后重新配置 编译,看错误是否存在

下面说一下重定位:

关于NAND的初始化就省了,主要说一说大页NAND FLASH的重定位问题

先看一看nand2ddr的代码

/**********************************************************
**	NFDATA使用字节模式访问
***********************************************************/
/* nand每次读完一页需要重新发送下一页的地址,但是在同一页内的数据可以连续读取 */
static void nandRead_ll(unsigned int srcAddr,unsigned char* dstAddr,unsigned int len)
{
	unsigned int i;
	unsigned int count=0;
	NAND_CS;
	//页读命令
	while(count<len)
	{
		NAND_CMD(0x00);
		nandSendAddr(srcAddr+count,0);
		NAND_CMD(0x30);
		//等待就绪
		nandWaitReady();

		for(i=0;i<NAND_PAGE_SIZE&&count<len;i++)
			dstAddr[count++]=NAND_GET;
	}
	NAND_DIS_CS;
}



void nand2ddr(unsigned int srcAddr,unsigned char* dstAddr,unsigned int len)
{
	/*	先复制前8K的代码 ,再复制后面的所有代码 
	 *	因为在6410中,上电后6410会将NAND前8K内容复制到片内RAM
	 *	而6410并不是连续复制8K内容,而是将第1---4页中的每页前2K
	 *	内容复制到片内RAM组成8K内容。
	 */
	/* 复制前8K内容每次复制2K */
	unsigned int i;
	for(i=0;i<4;i++)
	{
		nandRead_ll(srcAddr+i*NAND_PAGE_SIZE,dstAddr+i*2048,2048);
	}
<span style="white-space:pre">	</span>/* 复制完前8K后将长度减8K */
	len-=0x2000;	//	len = len - 8K
<span style="white-space:pre">	</span>/* 再次读取的地址从NAND的第五页开始 */
	nandRead_ll(srcAddr+0x4000,dstAddr+0x2000,len);
}

ps:我的NAND的一页有4096字节,至于一页是2048及以下的NAND要不要这样做,没试过,也没有东西试,感觉应该是不需要的,直接读就行了

nand2ddr将uboot从NAND中读出来的代码分成了两步

第一步:将前4页的每页前2K代码复制到内存

第二部:将从第5页开始的剩下部分连续读到内存

为什么要这样做呢?

请参照http://blog.csdn.net/girlkoo/article/details/8115849

我就不详述了,补充一个UBOOT存放在NAND中的图

所以我们不能连续复制NAND前8K的内容到内存,连续复制的话就相当于将前四页中的空数据(也不能说空,就是无用数据吧)也复制到了内存,这时一些链接时的函数地址就会发生错乱,不能执行。

假设uboot的链接基地址是57e00000,而start_armboot的入口地址原本在uboot.bin的偏移地址为2k地方,当正确复制NAND的时候是这样的情况

此时执行 ldr pc,= _start_armboot时就会跳到start_armboot中去执行

而如果是连续复制NAND数据到内存中呢?那情况就是这样的

此时执行 ldr pc,= _start_armboot会跳转到无效数据区域,因为_start_armboot的地址在链接是就已经确定,是一个固定地址在uboot.bin的偏移量为2k的地方

这样程序就会跑飞了。


怎么确定你的程序是否从NAND中正确复制到了内存呢?你可以将这里的注释去掉

/*outcopy:	<span>			</span>
	ldr r0,=__bss_start<span>		</span>
	ldr r1,=_start
	sub r1,r0,r1
	ldr 	r0,=CONFIG_SYS_PHY_UBOOT_BASE
	bl	returnCopyCode
*/

void returnCopyCode(unsigned char* srcAddr,unsigned int size)
{
	unsigned int i;
	
	for(i=0;i<size;i++)
	{
		uart0TransByte(srcAddr[i]);
	}
}

使用串口将你拷贝过去的数据发送到电脑上与uboot.bin进行对比。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值