一、 硬件结构
主要涉及的芯片:S3C2410,K9S1208,74LVC16245。
采用NAND FLASH做为BOOT程序的存储载体,所以OM[1..0]的值为0b00。
系统上电或者复位后,S3C2410从K9S1208芯片内读取4KB数据到内部名为Steppingstone的SRAM存储区中,然后芯片执行读取到的指令段,实现全部程序的加载。(BOOT程序可以小于4KB,但是要求FLASH前8页只能保存BOOT LOADER程序,多余的存储空间理论上不相关。)
芯片读取指令数据时,每次读取512B,分为8次读取。若打开校验(ECC)功能,则每次只能读取512B。
由于NAND FLASH产商保证芯片block0不可能是坏块,所以数值写入时,无需考虑坏块的绕过问题,但BOOT LOADER程序必须建立坏块表来进行正确读写。
S3C2410有NAND FLASH接口,无需考虑访问规则的建立,命令、地址、数据各自对应特定的寄存器,在初始化时序等相关寄存器之后就可以直接访问,实现了无缝连接。
二、 BOOT程序功能分析
功能需求方面,程序应该包括以下几点:
(1) 初始化系统。
(2) 提示用户当前状态为程序Loading。
(3) 读取主题程序指令数据到特定的存储区。
(4) 运行主体程序。
下面对程序读取NAND FLASH页数据的程序作一分析,代码如下。
int NF_ReadPage(U32 block,U32 page,U8 *buffer)
{
int i;
register U8 * bufPt=buffer; //指令数据存储区。
unsigned int blockPage;
U8 ecc0,ecc1,ecc2; //用于程序校验。
U8 se[16];
//page=page&0x1f;
blockPage=(block<<5)+page; //块页地址->得到物理地址。
NF_RSTECC(); // Initialize ECC,将寄存器的bit12置1。
NF_nFCE_L(); //改变nFCE电平,使能芯片。
NF_CMD(0x00); //发送读命令到NAND FLASH。
NF_ADDR(0); // Column = 0
NF_ADDR(blockPage&0xff); //数据线为8位,地址需分多次写入
NF_ADDR((blockPage>>8)&0xff); // Block & Page num.
NF_ADDR((blockPage>>16)&0xff); //
for(i=0;i<5;i++); //wait tWB(100ns)
NF_WAITRB(); // Wait tR(max 12us)
//NAND FLASH在收到读取要求后,还需要等待约200us时间,将数据准备好。
#if 0
i=512;
while(i--!=0)
{
*bufPt++=NF_RDDATA(); // Read one page
}
#elif 0
//DMA doens't work.
rSRCPND=BIT_DMA0;
rDISRC0=0x4e00000c; //NF_RDDATA()
rDISRCC0=(1<<0); //arc=AHB,src_addr=fix
rDIDST0=(unsigned)bufPt;
rDIDSTC0=(0<<0); //dst=AHB,dst_addr=inc;
rDCON0=(1<<31)|(1<<30)|(1<<29)|(1<<28)|(1<<27)|(0<<23)|(1<<22)|(0<<20)|(512/4);
//Handshake,AHB,interrupt,(4-burst),whole,S/W,no_autoreload,byte,count=512;
rDMASKTRIG0=(1<<1)|(1<<0);
while(!(rSRCPND & BIT_DMA0));
rSRCPND=BIT_DMA0;
//上面的#if 0 … #elif 0 …的功能是什么?理论上应该是必定不会被执行的。
//Answer:屏蔽之
#elif 1
__RdPage512(bufPt); //读取一页数据,汇编指令在下面分析。
#endif
ecc0=rNFECC0;
ecc1=rNFECC1;
ecc2=rNFECC2; //读取校验数值
se[0]=NF_RDDATA();
se[1]=NF_RDDATA();
se[2]=NF_RDDATA(); //读取数据写入时获得的ECC校验值,与当前值比较。
NF_RDDATA();
NF_RDDATA();
se[5]=NF_RDDATA(); //这里应该是获得0xff
NF_nFCE_H(); //将芯片使能关闭。
if(ecc0==se[0] && ecc1==se[1] && ecc2==se[2] && se[5]==0xff)
{
//Uart_Printf("[ECC OK:%x,%x,%x]/n",se[0],se[1],se[2]);
return 1;
}
else
{
//Uart_Printf("[ECC ERROR(RD):read:%x,%x,%x, reg:%x,%x,%x]/n",
//se[0],se[1],se[2],ecc0,ecc1,ecc2);
return 0;
}
}
其中“__RdPage512(bufPt);”读取一页数据,汇编指令如下。
;*************************************************************
; NAME : NAND FLASH Subroutine for a410 bootstrap
; DATE : 18.FEB.2002
; DESC :
; 02.18.2002:purnnamu: modified for A410
; 04.24.2002:purnnamu: optimized for NAND flash bootstrap
;*************************************************************
A410_BASE_ADDR EQU 0x2000000
MACRO //宏程序
LDR4STR1 $src,$tmp1,$tmp2
ldrb $tmp1,[$src]
ldrb $tmp2,[$src]
orr $tmp1,$tmp1,$tmp2,LSL #8
ldrb $tmp2,[$src]
orr $tmp1,$tmp1,$tmp2,LSL #16
ldrb $tmp2,[$src]
orr $tmp1,$tmp1,$tmp2,LSL #24
MEND
AREA |C$$code|, CODE, READONLY
EXPORT __RdPage512
__RdPage512
;input:a1(r0)=pPage
stmfd sp!,{r1-r11} //保护r1—r11寄存器。
ldr r1,=0x4e00000c //NFDATA即将r1指向NAND FLASH的数据寄存器。
mov r2,#0x200 //一共要读取的字节数。
0
LDR4STR1 r1,r4,r3 //从r1寄存器中读取4B数据,依次保存在r4寄存器中,
//保存的顺序为D C B A。以下类推。
LDR4STR1 r1,r5,r3
LDR4STR1 r1,r6,r3
LDR4STR1 r1,r7,r3
LDR4STR1 r1,r8,r3
LDR4STR1 r1,r9,r3
LDR4STR1 r1,r10,r3
LDR4STR1 r1,r11,r3
stmia r0!,{r4-r11} //将数据保存到C程序提供的存储区中。
subs r2,r2,#32
bne %B0 //是否读取完毕?
ldmfd sp!,{r1-r11} //出栈
mov pc,lr //返回
END
from:http://jianli9188.bokee.com/viewdiary.13737210.html