ILD

NAND
作者:Herbert Yuan 邮箱:yuanjp89@163.com
发布时间:2017-10-15 站点:Inside Linux Development

NAND flash在嵌入式设备中作为ROM存储程序和数据。NAND Flash的显著特点是不支持XIP (Excute In Place)、存在坏块、数据不可靠,需要ECC等。本文介绍一款NAND Flash和控制器,并给出实战代码。


nand flash支持的操作包括读、写、擦除等。因为写的时候只能把1变成0。要想把0变成1,只能通过擦除。这也是nand flash在每次写之前需要擦除的原因。nand flash的读写单位是页(page),擦除单位是块(block)。每个页还包括 spare数据区,通常用来存储ECC等。


1 K9F2G08U0C

页大小是(2K+64)Byte,每个块包含64页 (128K+4K)Byte,共有2048个块(256M+8M) x 8bit。页读写时间250us,块擦除时间2ms。在ECC的支持下可以达到100K次擦写周期。


引脚描述

Pin namedescription
I/O0-I/O7

Data Inputs/Ouputs

数据输入输出引脚。用于输入命令、地址和数据;读时,输出数据。

CE

Chip Enable

芯片使能引脚。

CLE

Command Latch Enable

指令锁存使能引脚。高电平时,在WE引脚的上升沿将指令锁存到指令寄存器。

ALE

Address Latch Enable

地址锁存使能引脚。高电平时,在WE的上升沿将地址锁存到地址寄存器。

RE

Read Enable

读使能引脚,用于串行数据输出控制,数据在RE的下降沿有效,同时内部列地址加1。

WE

Write Enable

写使能引脚,指令、地址和数据在WE引脚的上升沿锁存。

WP

Write Protect

写保护引脚,在电压过度阶段提供误擦写保护。

R/B

Ready/Busy Ouput

状态指示引脚,低电压表示正在program/erase or random read,高电平表示完成。

VccPower
Vss
Ground
N.CNo Connection


命令列表如下:

Function1st cycle2nd cycleAcceptable command during busy
Read00h30h
Read for Copy back00h35h
Read ID90h-
ResetFFh-
Page Program80h10h
Copy-Back Program85h10h
Two-Plane Page Program80h--11h81h-10h
Block Erase60hD0h
Random Data Input85h-
Random Data Output05hE0h
Read Status70h-
Read Status 2F1h-


出厂坏块 Initial invalid blocks

正常的块在出厂时被擦除为FFh(包括spare区)。出厂坏块不被擦除,但是第1页或第二页的spare数据区的第一个字节被置为非FFh。


读写操作错误 Error In Write Or Read Operation

在NAND的整个生命周期中,可能产生额外的坏块。在写或擦除时,必须检查状态,如果失败,执行必要的块替换。在读取时,必须校验ECC,并执行ECC correction。


ID表

读ID命令,读取5个字节,如下:

1st ByteMaker Code, DAh
2nd ByteDevice Code, 10h
3nd Byte

0:1 Internal Chip Number 00:1, 01:2, 10:4, 11:8

2:3 Cell Type, 00:2 Level cell, 01:4, 10:8, 11:16

4:5 Number of simulataneously programmed pages, 1,2,4,8

6    Interleave program between multiple chips, 0: no suport, 1: support

7    Cache program, 0: no support, 1: support

4th Byte

0:1 Page size, 1/2/4/8KB

2    Redundant area size, 0: 8bytes/512byte, 1: 16bytes/512bytes

4:5 Block size, 64/128/256/512KB

6    Organization, 0:x8, 1:x16

3,7 Serial access minimum, 00:50ns/30ns, 10:25ns, 01/11 reserved

5th Byte

2:3 Plane Number, 1/2/4/8

4:6 Plane size, 64Mb/128Mb/256Mb/512Mb/1Gb/2Gb/4Gb/8Gb


Page read

首先发出00h指令,然后发送5个周期的地址,然后发送30h指令,所选page 2112字节的数据将在40us内拷贝到数据寄存器,可以通过R/B引脚检测拷贝是否完成。之后数据就可以顺序访问,同时也支持随机访问该页内的数据。


Page program

通常一次写一个整页,但是也允许multiple partial page programming,页内的每个局部数据可以是4个字节到2112个字节,同一个页连续的局部页写不能超过4次,否则需要擦除才能再次写。

首先发出80h指令,然后发送地址和数据,然后发送10h开始执行写操作,读取R/B引脚检测写完成,然后发出70h指令,在读取状态数据,IO0为0,表示成功,1表示失败。


地址周期

读操作有5个地址周期,2个列地址周期,3个行地址周期。

Col Addr1Col. Addr2Row Addr1Row Addr2Row Add3
A0-A7A8-A11A12-A19A20-A27A28

列地址就是页内地址,12位列地址,则有4096个地址,0-2047是页内数据,2048-2111是oob数据。

行地址是页地址和块地址,一个块有64页,需要6位,所以A12-A17是块内的页地址。共有2048个块,需要11位,所以A18-A28是块地址。


2 NAND Flash Controller

要使用NAND Flash就需要按照其电路时序要求来输出电信号,这通常使用控制器来实现。下面是s3c2440的NAND Flash Controller。


只支持Software Moode,具有下列操作:

1. Writing to the command register = NAND flash command cycle

2. Writing to the address register = NAND flash address cycle

3. Writing to the data register = write cycle

4. Reading from the data register = read cycle

5. Reading main ECC register and Spare ECC registers = read cycle


Data register configuration

8-bit NAND Flash Memory Interface

NFDATA bit[7:0] 1st I/O[7:0]


ECC (ERROR CORRECTION CODE)

包含4个ECC模块,其中两个(一个用于data[7:0],一个用于data[15:8])用于最多2048字节的ECC Parity code generation。另外两个(一个用于data[7:0],一个用于data[15:8])用于最多16字节的ECC Parity code generation。


接口


下面是寄存器资料


1 NFCONF

地址:0x4E000000, R/W, nand flash configuration register, 复位:0x0000100x

控制BusWidth、AddrCycle、PageSize、AdvFlash、TWRPH1、TWRPH0、TACLS


2 NFCONT

地址:0x4E000004, R/W, nand flash control register, 复位:0x0384,这是个16位寄存器。

控制是否激活控制器、控制nFCE的值(接到CE),初始化ECC、main ECC lock、spare ECC lock、RB检测模式、RB是否产生中断、锁区域控制。


3 NFCMMD

地址:0x4E000008, R/W, nand flash command set register, 这是个8位寄存器。

写入命令。


4 NFADDR

地址:0x4E00000C, R/W, nand flash address set register, 这是个8位寄存器。

写入地址

5 NFDATA

地址:0x4E000010, R/W nand flash data register


6 NFMECCD0/1

地址:0x4e000014/8, main data erea ecc register

NFMECCD0: 1st and 2nd register for main data read

NFMECCD1: 3nd and 4th register for main data read


7 NFSECCD

地址: 0x4e00001c, spare area ecc register


8 NFSTAT

地址:0x4e000020, R/W, nand flash operation status register

RB状态


9 NFESTAT0/1

地址:0x4e000024/8 R/W nand flash ecc status register ofr I/O[7:0] I/O[15:8]


10 NFMECC0/1

地址:0x4e00002C/30 R, nand flash main data area ecc register for data[7:0] / data[15:8]


11 NFSECC

地址:0x4e000034 R nand flash spare area ecc register for I/O[15:0]


12 NFSBLK/NFEBLK

地址:0x4e000038/c R/W nand flash programmable start/end block address


3 实战

惯例,先展示代码:


init.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// watchdog
#define     WTCON           (*(volatile unsigned long *)0x53000000)
 
void disable_watchdog()
{
    WTCON = 0; 
}
 
void init_sdram()
{
    *(volatile unsigned long *)0x48000000 = 0x22011110;  // BWSCON
    *(volatile unsigned long *)0x48000004 = 0x00000700;  // BANKCON0
    *(volatile unsigned long *)0x48000008 = 0x00000700;  // BANKCON1
    *(volatile unsigned long *)0x4800000c = 0x00000700;  // BANKCON2
    *(volatile unsigned long *)0x48000010 = 0x00000700;  // BANKCON3
    *(volatile unsigned long *)0x48000014 = 0x00000700;  // BANKCON4
    *(volatile unsigned long *)0x48000018 = 0x00000700;  // BANKCON5
    *(volatile unsigned long *)0x4800001c = 0x00018005;  // BANKCON6
    *(volatile unsigned long *)0x48000020 = 0x00018005;  // BANKCON7
    *(volatile unsigned long *)0x48000024 = 0x008C07A3;  // REFRESH
    *(volatile unsigned long *)0x48000028 = 0x000000B1;  // BANKSIZE
    *(volatile unsigned long *)0x4800002c = 0x00000030;  // MRSRB6
    *(volatile unsigned long *)0x48000030 = 0x00000030;  // MRSRB7
}
 
typedef unsigned long uint32_t;
typedef unsigned char uint8_t;
 
struct NAND_REG
{
    uint32_t NFCONF;
    uint32_t NFCONT;
    uint8_t  NFCMMD;
    uint8_t  unused0[3];
    uint8_t  NFADDR;
    uint8_t  unused1[3];
    uint8_t  NFDATA;
    uint8_t  unused2[3];
    uint32_t NFMECCD0;
    uint32_t NFMECCD1;
    uint32_t NFSECCD;
    uint8_t  NFSTAT;
    uint8_t  unused3[3];
    uint32_t NFESTAT0;
    uint32_t NFESTAT1;
    uint32_t NFMECC0;
    uint32_t NFMECC1;
    uint32_t NFSECC;
    uint32_t NFSBLK;
    uint32_t NFEBLK;
};
 
volatile struct NAND_REG *s3c2440_nand = (struct NAND_REG *)0x4E000000; 
 
void nand_init()
{
#define TACLS   0
#define TWRPH0  3
#define TWRPH1  0
 
    s3c2440_nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
    s3c2440_nand->NFCONT = (1<<4)|(1<<1)|(1<<0);
}
 
static inline void nand_wait_idle()
{
    while ( !(s3c2440_nand->NFSTAT & 1))
        asm volatile ("nop");
}
 
static inline void nand_select_chip()
{
    s3c2440_nand->NFCONT &= ~(1<<1);
}
 
static inline void nand_deselect_chip()
{
    s3c2440_nand->NFCONT |= (1<<1);
}
 
static inline void nand_write_cmd(uint8_t cmd)
{
    s3c2440_nand->NFCMMD = cmd;
}
 
static inline void nand_write_addr(uint8_t addr)
{
    s3c2440_nand->NFADDR = addr;
}
 
static inline uint8_t nand_read_data()
{
    return s3c2440_nand->NFDATA;
}
 
// here is the api for K9F2G08U0C
// Page 2K + 64
// 64 pages per block
// total 2048 blocks
 
#define PAGES_PER_BLOCK     64
#define PAGE_SIZE           2048
#define PAGE_SPARE_SIZE     64
#define TOTAL_BLOCKS        2048
 
void nand_reset()
{
    nand_select_chip();
    nand_write_cmd(0xff);
    nand_wait_idle();
    nand_deselect_chip();
}
 
int nand_read_page(unsigned int block, unsigned int page, 
    unsigned char *buf, unsigned int off, unsigned int len)
{
    if (block >= TOTAL_BLOCKS || page > PAGES_PER_BLOCK)
        return -1;
 
    if (off >= PAGE_SIZE + PAGE_SPARE_SIZE
        || len > PAGE_SIZE + PAGE_SPARE_SIZE - off)
        return -1;
 
    nand_select_chip();
    nand_write_cmd(0x0);
    nand_write_addr((uint8_t)off);
    nand_write_addr((uint8_t)(off >> 8));
    nand_write_addr((uint8_t)(page|((block&3)<<6)));
    nand_write_addr((uint8_t)(block >> 2));
    nand_write_addr((uint8_t)(block >> 10));
    nand_write_cmd(0x30);
    nand_wait_idle();
 
    while ( len-- > 0)
        *buf++ = nand_read_data();
 
    return 1;
}
 
int nand_write_page()
{
    return 0;
}
 
int nand_erase_block()
{
    return 0;
}
 
void init()
{
    disable_watchdog();
    init_sdram();
    nand_init();
    nand_reset();
    nand_read_page(0, 2, (unsigned char *)0x30000000, 0, 2048);
}

nand_init()里面几个时间值是参考韦东山同学的。


start.S

1
2
3
4
5
6
7
8
9
    .text
    .global _start
_start:
    ldr sp, =4096
    bl init
    ldr lr,=loop
    ldr pc,=main
loop:
    b loop

这里跳转到main没有用bl,因为bl是相对pc跳转,只能跳转32M的范围,这里远远超过了。


bare.lds

1
2
3
4
5
6
7
8
9
10
11
12
SECTIONS
{
    rom 0x00000000 :
    {
        start.o init.o
    }
 
    ram 0x30000000 : AT(4096)
    {
        main.o
    }
}


main.c和第一节的led亮灯代码一样,做跑马灯操作。


编译后,烧录到样机,可以看到跑马灯效果,说明我们的NAND读取是成功的。

参考

[1] https://en.wikipedia.org/wiki/Execute_in_place

[2] Samsung K9F2G08U0C Data sheet.

[3] Samsung s3c2440 Data sheet.

[4] S3C2440对Nand Flash操作和电路原理(基于K9F2G08U0A). 

http://www.cnblogs.com/idle_man/archive/2010/12/23/1915303.html

[5] 深度分析NandFlash—控制器参数TACLS、TWRPH0和TWRPH1的确定. 

http://blog.csdn.net/huangmc198907/article/details/10531937



Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.