转载一篇文章: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;
}