nandflash与norflash

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







 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值