上篇分析了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进行对比。