1.简介
引导启动时,NAND FLASH存储器开始的4K字节将被加载到Steppingstone中并且执行加载到Steppingstone的引导代码。
通常引导代码会复制NAND FLASH的内容到SDRAM中,通过使用硬件ECC,有效地检查NAND FLASH的数据在赋值完成的基础上,将在SDRAM中执行主程序。
注:
当自动引导启动期间,ECC不会去检测,所以,NAND FLASH的开始4KB不应当包含位相关的错误。
2.原理图
3.启动的引脚配置
4.操作原理
1.原理图上NAND FLASH和S3C2440之间只有数据线,怎么传输地址?
DATA0~DATA7上既传输数据,又传输地址
ALE为高电平时传输的是地址
CLE为高电平时传输的是命令
ALE,CLE都为低电平时传输的是数据
RnB为高表示就绪,为低表示正忙
2.怎么操作NAND FLASH呢?
根据NAND FLASH的芯片手册,一般的过程是:
a.发出命令
选中芯片
CLE设为高电平
在DATA0~DATA7上出输出命令值
b.发出地址
选中芯片
ALE设为高电平
在DATA0~DATA7上输出地址值
发出一个写脉冲
c.发出数据/读数据
Ⅰ.发出数据
选中芯片
ALE,CLE设为低电平
在DATA0~DATA7上输出数据值
发出一个写脉冲
Ⅱ.读数据
选中芯片
发出读脉冲
读DATA0~DATA7的数据
5.ID与地址编码
1.选中芯片
2.发出命令 0x90
3.发出地址 0x00
4.读数据得到 0xEC
5.读数据得到device code (DAh)
6.读数据得到 (10h)
7.读数据得到页大小,块大小(15h)
8.读数据得到 (44h)
9.退出读ID状态
6.读NAND FLASH
1.选中芯片
2.发出0x00命令
3.发出地址 col(低8bit)
4.发出地址 col(高)
5.发出地址 page(低)
6.发出地址 page(中)
7.发出地址 page(高)
8.发出命令0x30
9.wait_ready
10.读数据
11.退出读状态
7.写NAND FLASH
1.选中芯片
2.发出0x80命令
3.发出地址 col(低8bit)
4.发出地址 col(高)
5.发出地址 page(低)
6.发出地址 page(中)
7.发出地址 page(高)
8.发出命令0x10
9.发出数据
8.擦除
擦除的最小单位(block)
1.选中芯片
2.发命令0x60
3.发出地址 page(低)
4.发出地址 page(中)
5.发出地址 page(高)
6.发命令0xD0
7.wait_ready
9.NAND FLASH的结构
1block = 64page
1page = 2048col
OOB为了解决Nand的位反转的缺点而存在的。
2k的page data CPU使用addr寻址。其后的64bit的OOB区CPU无法访问。
eg:
CPU访问Nand Flash上的第2048位数据实际上是访问Page1的第0位
10.寄存器解析
NFCONF:Nand Flash配置寄存器
被用来设置NAND FLASH的时序参数TACLS、TWRH0、WERPH1、设置数据位宽;还有一些只读位,用来指示是否支持其他大型的页。
NFCONT: Nand Flash控制寄存器
被用来使能/禁止NAND FLASH控制器、使能/禁止控制引脚信号nFCE、初始化ECC。它还有一些其他功能,在一般的应用中用不到,比如锁定NAND FLASH
NFCMMD: 命令寄存器
NFADDR: 地址寄存器
NFDATA: 数据寄存器
只用到了低8位,读写此寄存器将对NAND FLASH的读数据、写数据操作
NFSTAT: 状态寄存器
只用到了[0]:0:buys 1:ready
11.代码实现
nand_flash.h
#ifndef _NAND_FLASH_H
#define _NAND_FLASH_H
extern void nand_init(void);
extern void nand_flash_test(void);
#endif /* _NAND_FLASH_H */
nand_flash.c
#include "s3c2440_soc.h"
#include "my_printf.h"
void nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/*设置NAND FLASH的时序*/
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/*使能NAND FLASH控制器,初始化ECC,禁止片选*/
NFCONT = (1<<4) | (1<<1) | (1<<0);
}
void nand_select(void)
{
/*使能片选*/
NFCONT &=~(1<<1);
}
void nand_deselect(void)
{
/*禁止片选*/
NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCCMD = cmd;
for(i=0; i<10; i++);
}
void nand_addr_byte(unsigned char addr)
{
volatile int i;
NFADDR = addr;
for(i=0; i<10; i++);
}
unsigned char nand_data(void)
{
return NFDATA;
}
void nand_w_data(unsigned char val)
{
NFDATA = val;
}
void wait_ready(void)
{
while (!(NFSTAT & 1));
}
void nand_chip_id(void)
{
unsigned char buf[5]={0};
nand_select();
nand_cmd(0x90);
nand_addr_byte(0x00);
buf[0] = nand_data();
buf[1] = nand_data();
buf[2] = nand_data();
buf[3] = nand_data();
buf[4] = nand_data();
nand_deselect();
printf("maker id = 0x%x
",buf[0]);
printf("device id = 0x%x
",buf[1]);
printf("3rd byte = 0x%x
",buf[2]);
printf("4th byte = 0x%x
",buf[3]);
printf("page size = %d kb
",1 << (buf[3] & 0x03));
printf("block size = %d kb
",64 << ((buf[3] >> 4) & 0x03));
printf("5th byte = 0x%x
",buf[4]);
}
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int i = 0;
int page = addr / 2048;
int col = addr & (2048 - 1);
nand_select();
while (i < len)
{
/* 发出00h命令 */
nand_cmd(00);
/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 发出30h命令 */
nand_cmd(0x30);
/* 等待就绪 */
wait_ready();
/* 读数据 */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
if (i == len)
break;
col = 0;
page++;
}
nand_deselect();
}
int nand_erase(unsigned int addr, unsigned int len)
{
int page = addr / 2048;
/* addr是page的起始地址,长度为一个page的长度 */
if (addr & (0x1FFFF))
{
printf("nand_erase err, addr is not block align
");
return -1;
}
if (len & (0x1FFFF))
{
printf("nand_erase err, len is not block align
");
return -1;
}
nand_select();
while (1)
{
page = addr / 2048;
nand_cmd(0x60);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
nand_cmd(0xD0);
wait_ready();
len -= (128*1024);
if (len == 0)
break;
addr += (128*1024);
}
nand_deselect();
return 0;
}
void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
int page = addr / 2048;
int col = addr & (2048 - 1);
int i = 0;
nand_select();
while (1)
{
nand_cmd(0x80);
/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 发出数据 */
for (; (col < 2048) && (i < len); )
{
nand_w_data(buf[i++]);
}
nand_cmd(0x10);
wait_ready();
if (i == len)
break;
else
{
/* 开始下一个循环page */
col = 0;
page++;
}
}
nand_deselect();
}
void do_read_nand_flash(void)
{
unsigned int addr;
volatile unsigned char *p;
int i, j;
unsigned char c;
unsigned char str[16];
unsigned char buf[64];
/* 获得地址 */
printf("Enter the address to read: ");
addr = get_uint();
nand_read(addr, buf, 64);
p = (volatile unsigned char *)buf;
printf("Data :
");
/* 长度固定为64 */
for (i = 0; i < 4; i++)
{
/* 每行打印16个数据 */
for (j = 0; j < 16; j++)
{
/* 先打印数值 */
c = *p++;
str[j] = c;
printf("%02x ", c);
}
printf(" ; ");
for (j = 0; j < 16; j++)
{
/* 后打印字符 */
if (str[j] < 0x20 || str[j] > 0x7e) /* 不可视字符 */
putchar('.');
else
putchar(str[j]);
}
printf("
");
}
}
void do_erase_nand_flash(void)
{
unsigned int addr;
/* 获得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
printf("erasing ...
");
nand_erase(addr, 128*1024);
}
void do_write_nand_flash(void)
{
unsigned int addr;
unsigned char str[100];
int i, j;
unsigned int val;
/* 获得地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
printf("Enter the string to write: ");
gets(str);
printf("writing ...
");
nand_write(addr, str, strlen(str)+1);
}
void nand_flash_test(void)
{
char c;
while (1)
{
/* 打印菜单, 供我们选择测试内容 */
printf("[s] Scan nand flash
");
printf("[e] Erase nand flash
");
printf("[w] Write nand flash
");
printf("[r] Read nand flash
");
printf("[q] quit
");
printf("Enter selection: ");
c = getchar();
printf("%c
", c);
/* 测试内容:
* 1. 识别nand flash
* 2. 擦除nand flash某个扇区
* 3. 编写某个地址
* 4. 读某个地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 's':
case 'S':
nand_chip_id();
break;
case 'e':
case 'E':
do_erase_nand_flash();
break;
case 'w':
case 'W':
do_write_nand_flash();
break;
case 'r':
case 'R':
do_read_nand_flash();
break;
default:
break;
}
}
}