nboot就是复制与跳转!
ARM2440A与NAND FLASH 引脚:
从上面这个时序就可以看出,当设置地址时第八位是不需要传进去的。
官方的文档是这样的:
因为arm对flash的支持,所以相应的
Cle,we,ale,re,arm会自动设置,不用我们自己设,(轻松啊!!!)
还是因为arm对flash的支持,所以只要对寄存器直接操作就行,相关时序,arm会帮你完成!
#define NF_CMD(cmd) {rNFCMD = (cmd); }
#define NF_ADDR(addr) {rNFADDR = (addr); }
(1)NF_CE_L();
#define NF_nFCE_L() {rNFCONT &= ~(1<<1); }
片选使能,使ce变为低电平。
(2)NF_CLEAR_RB();
#define NF_CLEAR_RB() {rNFSTAT |= (1<<2); }
对RB位写1来清除RB位使之为低电平表示现在busy,位读操作的ready作准备。
(3) NF_CMD(CMD_READ);
#define CMD_READ 0x00
对1st half Page:进行读操作,设置A[8]
(4)NF_ADDR(0x00);
设置column设置A[0:7],从0地址开始读
(5) NF_ADDR(blockPage&0xff);
设置A[9:16],其中A[9:13]设置page地址,A[14:16]block地址
(6)NF_ADDR((blockPage>>8)&0xff);
设置A[17:24],block地址
(7)NF_ADDR((blockPage>>16)&0xff);
设置A[25],block地址
(8) NF_DETECT_RB();
#define NF_DETECT_RB() {while(!(rNFSTAT&(1<<2)));}
设置完地址后,根据时序图,要等待RB位由低电平变为高电平,表示数据就绪可以读取了。这也是为什么我们之前做NF_CLEAR_RB();的原因。
(9) for(i=0;i<512;i++)
{
*bufPt++=NF_RDDATA();
}
因为我们是也读取,所以制定好相应的地址,flash就会从那个地址开始读数据,直到这一页读完。因为我们是从column的0地址开始读的,所以会返回一个完整的页的数据512bytes。所以这个函数的功能就是这样,把读出来的数据放到bufPt指针指向的内存中。
(10) NF_CE_H();
一切完成之后拉高CE电平,释放flash的控制。
到此,大功告成了!
最后就是让PC跳到bufPt指定内存执行就可以了。
所以main函数可以这样写
volatile unsigned char *downPt;
#define OS_START_ADDR_OFFSET 0x38000//(0x02040000) // 0x3020_0000
#define DOWNLOAD_ADDRESS (_RAM_STARTADDRESS+OS_START_ADDR_OFFSET)
void Main(void)
{
DWORD err; //, t0 = 0;
int i;
register page, block, blockcopy_count;
MMU_EnableICache();
Uart_Init();
Uart0_SendString("/n+-----------------------+/n");
Uart0_SendString("WinCE NAND Boot v1.001 2009/n");
Uart0_SendString("+-------------------------+/n");
rGPCDAT &=0;
rGPCDAT|=1;
rGPADAT|=(1<<16); //
rGPACON=rGPACON&~(0x1<<16); // nGCS[5] -> OUTPUT
downPt=(unsigned char *)DOWNLOAD_ADDRESS;
NF_Init();
NF_Reset();
NF_ReadID();
//err = ReadImageFromNand(dwEntry,0);
for(block=2;block<8;block++)
for(page=0;page<32;page++)
{
NF_ReadPage(block,page,(U8 *)downPt);
downPt+=512;
}
//NF_ReadPage(7,8,(U8 *)downPt);
///*
JumpAddr=DOWNLOAD_ADDRESS;//(unsigned char *)
Launch(JumpAddr);
Uart0_SendString("/nBoot ERROR:");
Uart0_SendDWORD(err, TRUE);
while (1);
}
所以当我们在转换地址时,需要把第八位单独以命令的方式设置。
#define CMD_READ 0x00 // Read
#define CMD_READ1 0x01 // Read1
NF_CMD(CMD_READ); //read the 1st half Page
或
NF_CMD(CMD_READ1); read the 2nd half Page
2.1.3
A0~A25的含义:
A[7:0]就是所谓的512BYTES的column address。其实column address是A[8:0],只不过A[8]必须由NF_CMD(CMD_READ); 或NF_CMD(CMD_READ1);来设置,比如你要读地址为256的数据,第八位的1就要用命令来传。这样A[7:0]加上命令设置就可以完成对512byte的PAGE的设置,一共9位!
A[13:9] 32个page的地址刚好5位表示0~31.
A[25:14] 4096个block的地址。
所以在blockPage=(block<<5)+page;中指设置了block和page的地址,column的地址会在后面设置。
因为和column无关,所以blockpage是用来设置A[9:25]的。
block<<5:左移5位,是为了设置A[25:14]。
Page是用来设置A[9:13]。
设置好地址,我们就可以根据时序来进行操作了!
2.2
下面是读的时序:
因为arm2440a是支持NAND FLASH的所以ARM已经定义好了引脚。
Flash中的7、8、9、16、17、18是控制脚,直接和arm相连,44~41、32~29是数据脚所以是8位的。
2、具体分析:
(1)blockPage=(block<<5)+page;
上面这句代码是对对地址进行转换,根据指定的块和页转换成相应的地址。
2.1.1以K9F1208U0A为例:
总的来说,flash是由block组成的,block由page组成,page有column组成,column就是寻址的最小单位。
一共4096block
1 block=16kbyte,
512Mbit=64Mbyte,
1block=32page,
1page=528byte=512byte(Main Area)+16byte(Spare Area)
用户数据保存在main area中。
NAND FLASH主要以页(page)为单位进行读写,以块(block)为单位进行擦除。FLASH页的大小和块的大小因不同类型块结构而不同,块结构有两种:小块和大块,小块NAND FLASH包含32个页,每页512+16字节;大块NAND FLASH包含64页,每页2048+64字。(因为我用的是K9F1208U0A是小块,所以下面以小块为例)
其中,512Byte(或1024B)用于存放数据,16 Byte(64B)用于存放其他信息(包括:块好坏的标记、块的逻辑地址、页内数据的ECC校验和等)。NAND设备的随机读取得效率很低,一般以页为单位进行读操作。系统在每次读一页后会计算其校验和,并和存储在页内的冗余的16B内的校验和做比较,以此来判断读出的数据是否正确。
大块和小块NAND FLASH都有与页大小相同的页寄存器,用于数据缓存。当读数据时,先从NAND FLASH内存单元把数据读到页寄存器,外部通过访问NAND FLASH I/O端口获得页寄存器中数据(地址自动累加);当写数据时,外部通过NAND FLASH I/O端口输入的数据首先缓存在页寄存器,写命令发出后才写入到内存单元中。
2.1.2又因为page又分为1st half Page和2nd half Page。所以1st half Page得寻址范围从0~255,2nd half Page寻址范围从256~511.从他们的二进制数字可以看出
1st half Page: 00000000b ~11111111b
2nd half Page:000000001b~111111111b
第八位是用来区分1st half Page和2nd half Page的。
所以nand FLASH 就专门设置了一个设置第八位的命令,而不允许用地址的方式去设置,