转载一篇文章:nandflash详解
先列出nandflash和norflash之间的区别:
从上面截取的图可以看到:
1.之所以NOR可以随机访问,片上运行,最主要的原因他的接口和RAM的接口是相同的。它的可靠性比较高,但是不易擦写。
下面根据uboot的代码搬移操作分析一下nandflash的操作过程:
#ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ beq clear_bss ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 <- size of armboot */ #if 1 bl CopyCode2Ram /* r0: source, r1: dest, r2: size */ #else
int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size) { unsigned int *pdwDest; unsigned int *pdwSrc; int i; if (bBootFrmNORFlash()) //判断是nand启动还是nor启动 { pdwDest = (unsigned int *)buf; pdwSrc = (unsigned int *)start_addr; /* 从 NOR Flash启动 */ for (i = 0; i < size / 4; i++) { pdwDest[i] = pdwSrc[i]; } return 0; } else { /* 初始化NAND Flash */ nand_init_ll(); /* 从 NAND Flash启动 */ nand_read_ll_lp(buf, start_addr, (size + NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP)); return 0; } } 下面分析nand_init_ll();
void nand_init_ll(void) { S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000; // 2410 S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000; // 2440 #define TACLS 0 #define TWRPH0 3 #define TWRPH1 0 /* 判断是S3C2410还是S3C2440 */ if (isS3C2410) { /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */ s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); } else { /* 设置时序 */ s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0); } /* 复位NAND Flash */ nand_reset(); }
时序的设置:
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
/* 设置时序 */
s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
下面解析一下,附上2440对nandflash的操作时序:
TACLS:表示在CLE/ALE使能后需要经过多久才能发出写使能信号
TWRPH0:表示写使能信号持续的时间
TWRPH1:写使能信号释放后到CLE/ALE释放的时间间隔
再附上nandflash的时序:
TACLS <===> tCLS - tWP 或者tALS - tWP
TWRPH0 <===> tWP
TWRPH1 <===> tCLH 或者 tALH
根据上图计算出相应的值:
TACLS <===> tCLS - tWP 或者tALS - tWP <====> 0ns
TWRPH0 <===> tWP <=========> 21或12ns
TWRPH1 <===> tCLH 或者 tALH <=========> 5ns
TACLS TWRPH0 TWRPH1 的单位:
从上面的datasheet可知:
Duration = HCLK x TACLS <===> 0 = HCLK * TACLS ====> TACLS = 0
Duration = HCLK x ( TWRPH0 + 1 ) <====> 21ns <= 100MHZ * (TWRPH0 + 1)
=====> 21ns <= 1000/100(ns) *(TWRPH0 + 1) ==> TWRPH0 >=2 ==> TWRPH0 =2????经过试验可以设置为2
Duration = HCLK x ( TWRPH1 + 1 ) <====> 5ns <=100MHZ * (TWRPH1 + 1 )
=====> 5ns <=1000/100(ns) * (TWRPH1 + 1 ) ===> TWROH1 = 0
复位:
nand_reset();
static void nand_reset(void) { /* 判断是S3C2410还是S3C2440 */ if (isS3C2410) { s3c2410_nand_reset(); } else { s3c2440_nand_reset();//2440的复位 } }
static void s3c2440_nand_reset(void) { s3c2440_nand_select_chip(); s3c2440_write_cmd(0xff); // 复位命令 s3c2440_wait_idle(); s3c2440_nand_deselect_chip(); }
发出命令:
static void s3c2440_write_cmd(int cmd) { S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000; volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD; *p = cmd;//直接将命令放在寄存器s3c2440nand->NFCMD中 }
发出命令后,等待nandflash就绪:
static void s3c2440_wait_idle(void) { int i; S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000; volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT; while(!(*p & BUSY)) for(i=0; i<10; i++); }
到这里nandflash就已经初始化了。
下面将uboot从nand搬移到sdram上,进行启动:
void nand_read_ll_lp(unsigned char *buf, unsigned long start_addr, int size) { int i, j; if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) { return ; /* 地址或长度不对齐 */ } /* 选中芯片 */ nand_select_chip(); for(i=start_addr; i < (start_addr + size);) { /* 发出READ0命令 ,等待发出地址*/ write_cmd(0); /* Write Address */ write_addr_lp(i); write_cmd(0x30);//发出read1指令,等待读出数据 wait_idle();//等待nand就绪 for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) { *buf = read_data(); buf++; } } /* 取消片选信号 */ nand_deselect_chip(); return ; }
写地址分析:
static void write_addr_lp(unsigned int addr) { /* 判断是S3C2410还是S3C2440 */ if (isS3C2410) { s3c2410_write_addr(addr); } else { s3c2440_write_addr_lp(addr); } }
/* 发出地址 */ static void s3c2440_write_addr_lp(unsigned int addr) { int i; S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000; volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR; int col, page; col = addr & NAND_BLOCK_MASK_LP; page = addr / NAND_SECTOR_SIZE_LP; *p = col & 0xff; /* Column Address A0~A7 */ for(i=0; i<10; i++); *p = (col >> 8) & 0x0f; /* Column Address A8~A11 */ for(i=0; i<10; i++); *p = page & 0xff; /* Row Address A12~A19 */ for(i=0; i<10; i++); *p = (page >> 8) & 0xff; /* Row Address A20~A27 */ for(i=0; i<10; i++); *p = (page >> 16) & 0x03; /* Row Address A28~A29 */ for(i=0; i<10; i++); }
每一页的大小为2048字节(1页) + 64字节(oob)
列地址就是页内地址范围在0-(2048+64-1),行地址表示是要访问哪一页 ==>对应12个引脚
256M的nand的页:256*1024*1024 / 2048 == 128K个页 ==2^18个页====》对应18个引脚
col = addr & NAND_BLOCK_MASK_LP; //计算出页内的地址
page = addr / NAND_SECTOR_SIZE_LP;//计算出第几页
*p = col & 0xff; /* Column Address A0~A7 */ //页内地址的低8位
for(i=0; i<10; i++);
*p = (col >> 8) & 0x0f; /* Column Address A8~A11 */ //页内地址的高4位
for(i=0; i<10; i++);
*p = page & 0xff; /* Row Address A12~A19 */
for(i=0; i<10; i++);
*p = (page >> 8) & 0xff; /* Row Address A20~A27 */
for(i=0; i<10; i++);
*p = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(i=0; i<10; i++);
读数据:
static unsigned char s3c2440_read_data(void) { S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000; volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA; return *p; }