详见文件:E:\_TimeUp\_soc\ARM\S3C6410\S3C6410_NAND_Flash驱动分析.doc
S3C6410 NAND Flash驱动分析文档
1. 目的意义
通过对6410下NAND Flash驱动的分析,了解以ARM11为内核的处理器下NAND Flash驱动的实现方式,并为SEP0718处理器中NAND Flash驱动的实现做准备。
2. 背景
该文档中的分析的NAND FLash驱动由华恒提供,其软硬件平台分别为:
硬件平台:S3C6410
软件平台:WinCE 6.0
3. 硬件原理
3.1 NAND Flash分类
从存储容量方面,NAND Flash包括两种:大容量NAND Flash和小容量NAND Flash,大容量为每页(2048+64)Byte,小容量为每页(512+16)Byte。
从组成存储单元的类型方面NAND Flash也包括两种:SLC(Single Level Cell) 与MLC(Multi Level Cell)。SLC技术与EEPROM原理类似,只是在浮置闸极(Floating gate)与源极(Source gate)之中的氧化薄膜更薄,其数据的写入是透过对浮置闸极的电荷加电压,然后可以透过源极,即可将所储存的电荷消除,采用这样的方式便可储存每1个信息位,这种技术的单一位方式能提供快速的程序编程与读取。MLC原理是将两个位的信息存入一个浮动栅(Floating Gate,闪存存储单元中存放电荷的部分),然后利用不同电位的电荷,透过内存储存格的电压控制精准读写。即一个Cell存放多个bit,现在常见的MLC架构闪存每Cell可存放2bit,容量是同等SLC架构芯片的2倍。这也使得MLC NAND Flash的存取速度较慢。MLC因较大容量和价格优势,具有较好的应用前景。
3.2 NAND Flash硬件结构
NandFlash用来保存大容量的数据,该系统中采用三星公司的K9G8G08,其要求电压范围为2.7~3.6V,总容量为(1G+32M)*8bit,其中1GB为数据区的容量,32MB为信息区的总容量,内部数据寄存器容量为(2K+64)Byte。对于NandFlash的结构组织如下图所示,整个结构采用层次构成方式,最小单位为页,容量为(2K+64)Byte,读写操作都是以页为单位的;由128页构成一块,容量为(256K+8K)Byte,擦除操作是以块为单位的;整个NandFlash器件是由4096块构成的。
图1 NAND Flash阵列组织结构图
表1 K9G8G08页内布局
表2 K9G8G08 Spare Area布局
K9G8G08有8个I/O端口,采用地址、数据线复用的方式,命令、地址和数据是通过将#WE和#CE置低被写到I/O端口的,它们都是在#WE 的上升沿被写入。CLE、ALE分别用来在8位的I/O端口上分别复用命令、地址。1GB的物理空间需要地址宽度为30位,对于字节级寻址需要要5个总线周期来寻址:两个周期用于column地址,三个周期用于row地址。页读和页编程在命令输入后需要相同的五个地址周期;而在块擦除操作中,仅需要使用三个row地址周期。
K9G8G08支持的命令有:
表3 K9G8G08命令集
通过命令集可以看出K9G8G08支持两片编程命令和两片擦除命令,这有助于提高编程速度和擦除速度。
K9G8G08每页在一次擦除操作后,仅允许部分页编程一次。一块内的页编程要顺序寻址,即仅能先编程序号小的页后编程序号大的页,而不能以随机的顺序编程。页编程由两个阶段组成:前一阶段是2112字节的数据从NAND Flash数据寄存器装载到页内的数据寄存器;第二阶段是页数据寄存器内的数据被编程到相对应的存储单元。K9G8G08的物理结构包括两片,每片分别对应一个2112的数据寄存器,这样在数据从NAND Flash的数据寄存器装载到页内两片对应的数据寄存器后,可以同步进行两片编程操作(第二阶段),加快了编程速度。
3.3 S3C6410 NAND Flash控制器原理
S3C6410 NAND Flash控制器支持的主要特征有:
a. 支持512字节页和2K字节页。
b. 软件模式:可以直接访问NAND Flash存储器。
c. 接口:支持8位NAND Flash接口。
d. 硬件ECC产生、检错和表示,软件纠错。
e. 支持SLC和MLC的NAND Flash存储器:1位、4位和8位的ECC(推荐对SLC NAND Flash使用1位ECC,对MLC NAND Flash使用4位和8位ECC)。
f. 支持对数据寄存器和ECC数据寄存器的字节、半字和字访问,对其它寄存器进行按字访问。
g. 对Steppingstone支持字节、半字和字访问。
3.4 S3C6410处理器与NAND Flash硬件连线
S3C6410处理器与NAND Flash硬件连线如下图所示:
图2 NAND Flash硬件连线图
I/O0- I/O7:8位命令、地址、数据复用总线。
CLE:命令锁存使能。
ALE:地址锁存使能。
/WE:写使能。在写命令输入后,数据在/WE脉冲的控制下依次从数据寄存器写入存储单元。
/WP:硬件写保护。在系统上电或断电时保持低电平。
/RE:读使能。在读命令输入后,数据在/RE脉冲的控制下依次从存储单元读出到数据寄存器。
CE:芯片使能,高电平有效。
R/B:芯片Ready/Busy状态检测。
4. 驱动的整体框架
WinCE下NAND Flash驱动是块设备驱动,属于流接口驱动,在系统启动时被设备管理器加载。应用程序和设备管理器可以通过文件系统调用对NAND Flash驱动进行访问。整体驱动架构如图11所示,NAND Flash本身驱动结构如图12所示。
NAND Flash驱动程序分为两层:FAL层和FMD层。FAL层将flash抽象成统一的接口提供给上层的文件系统,这些接口都以DSK为前缀;FMD层的驱动与底层硬件密切相关,接收FAL层的请求,实现对硬件的基本操作,FMD层向FAL层提供的接口均以FMD为前缀。
FAL层的驱动由微软实现,以lib库的形式提供,这部分代码是不可见的。华恒提供的BSP包中仅有与硬件密切相关的FMD层的驱动,该文档仅分析FMD层的驱动实现。
图3 NAND Flash整体驱动架构
图4 驱动整体架构
5. 驱动的功能
NAND Flash的FMD层驱动实现的主要功能有:
5.1 NAND Flash的初始化
函数实现为FMD_Init,该部分实现对NAND Flash操作之前的基本配置,包括控制寄存器、状态寄存器的配置,ID号的获取,及NAND Flash基本物理参数(如总块数、每块的页数等)的获取和配置。
5.2 NAND Flash的写操作
函数实现为FMD_WriteSector,支持对大页和小页的写操作,写操作中包括对数据主存储区的写和对Spare区的写。并对这两部分实现1位ECC校验,并将校验值写入对应的Spare区。
5.3 NAND Flash的读操作
函数实现为FMD_ReadSector,同样支持对大页和小页的读操作,包括对数据主存储区的读和对Spare区的读,将读出的值进行1位ECC校验,并可以实现1位的错误纠正。
5.4 NAND Flash的擦除操作
函数实现为FMD_EraseBlock,同样分大页的块擦除和小页的块擦除两种情况,两者实现步骤相同,只是与页容量相关的地址计算不同。
5.5 获取块状态
通过读取当前块第一页Spare区相应位保存的状态获取该块的状态。
5.6 设置块状态
如果该块为坏块将其标记为坏块。即将坏块标记0x0写入该块第一页的第一字节。
6. 驱动的实现逻辑
FMD层驱动的几个主要函数:
FMD_Init、FMD_Deinit、FMD_ReadSector、FMD_WriteSector、FMD_EraseBlock、FMD_PowerUp、FMD_PowerDown、FMD_OEMIoControl、FMD_GetInfo。
6.1 FMD_Init、FMD_Deinit:
FMD_Init和FMD_Deinit分别在NAND Flash设备驱动加载和卸载时调用。FMD_Init主要实现基本的初始化操作,包括对NAND Flash总线宽度的配置、芯片选择、基本时序的配置、控制寄存器的配置等。通过ReadFlashID读取所用芯片的厂商号MID和具体设备号DID。三星的MID固定为0xEC;DID表示具体芯片的型号,不同型号芯片对应特定的内部物理结构。根据DID和NAND Flash的芯片手册可以知道芯片的所有重要参数,在代码中根据获取的DID实现代码中所用参数的赋值。基本流程如图12所示。FMD_Deinit没有具体的操作,仅用于驱动卸载调用,输出卸载信息。
图5 初始化流程
图6 大页读操作流程图
6.2 FMD_ReadSector
参数:起始扇区地址,数据区Buffer,信息区Buffer,读扇区数
FMD_ReadSector即读扇区,根据flash页容量的不同,支持的读操作时序不一样,具体分大页和小页两种情况。大页即每页容量为2K字节,在读操作时每次仅读一页(4个扇区);小页每页容量为512字节(1个扇区),每次可以读多页。大页读时输入参数“读扇区数”必须为1,小页读时没有扇区数目的限制。大页读操作流程如图13所示,小页读操作流程如图14所示。
图7 小页读操作流程图
图8 擦除操作流程图
6.3 FMD_EraseBlock
参数:擦除块号
FMD_EraseBlock函数中根据flash容量的大小分别执行擦除大容量flash的函数FMD_LB_EraseBlock和擦除小容量flash的函数FMD_SB_EraseBlock,它们的流程如图15所示,只是擦除地址的计算不同,大容量擦除块号左移6转换成实际擦除地址,而小容量地址转换需要地址左移5,这与块内页数量有关。
6.4 FMD_WriteSector
图9 写操作流程图
参数:起始扇区地址,数据区Buffer,信息区Buffer,写扇区数
与扇区读一样,FMD_WriteSector的写操作函数同样支持对大页(2KB)的写和对小页(512B)的写两种情况。大页写时输入参数“写扇区数”不能大于1。大页写每次写主数据区(2048KB)时要分四次写,每次将512字节数据写入数据寄存器,同时对该512字节进行硬件ECC检验,将所得的ECC码先临时存放到buffer中,每次数据写时都要同时完成ECC校验,当完成四次数据传递后,向Spare区前6字节写入相应的数据,并对改部分的数据进行ECC校验,然后将主存储及Spare区的ECC值写到Spare区的对应位置,输入写确认命令0x10后,实现将数据(2112Byte)从数据寄存器写入具体的存储单元。小页写时数据区的数据可以一次传输完成, 512字节的数据在写入时也要进行ECC校验,Spare区数据直接写入,不进行ECC校验。
FMD_ReadSector、FMD_WriteSector、FMD_EraseBlock都是被FAL层函数调用,实现基本的读、写、擦除操作。
6.5 FMD_PowerUp、FMD_PowerDown
FMD_PowerUp、FMD_PowerDown这两个函数主要用于电源管理。FMD_PowerUp中实现对Flash的初始化配置,包括对控制寄存器和配置寄存器的初始化配置。FMD_PowerDown主要为电源管理设备断电提供了可调用的接口,该函数中输出FMD_PowerDown的信息。
6.6 FMD_OEMIoControl
参数:
DWORD dwIoControlCode:(In)IOCTL,可以是预定于或由OEM定义的设备特定的控制码,通常通过一个头文件导出。
PBYTE pInBuf:(In)与dwIoControlCode相关的额外信息的输入Buffer的指针。
DWORD nInBufSize:(In)由pInBuf的字节数。
PBYTE pOutBuf:(Out)调用者提供的输出Buffer的指针。
DWORD nOutBufSize:(In)pOutBuf的最大字节数。
PDWORD pBytesReturned:(In)pOutBuf返回的实际字节数。
当设备驱动或线程调用KernelIoControl内核线程来传递IOCTL时,内核通过调用OEMIoControl函数获取硬件平台信息,IOCTL控制码具有很大的平台依赖性和功能依赖性。该驱动中只有为控制码IOCTL_FMD_GET_INTERFACE实现了对应的功能,获取该驱动中的接口信息。
6.7 FMD_GetInfo
通过ReadFlashID函数读取所用flash的型号,根据对应的型号进行代码中所用重要参数(块数目、每块的页数、扇区大小等)的赋值。
7. 工程配置文件说明
7.1 Bat文件
Bat文件是平台相关的文件,存放路径为:
WINCE600\PLATFORM\SMDK6410
文件名是SMDK6410.bat。通过该文件中定义的设置为定制的操作系统添加驱动和示例程序等组件。Bat文件通过设置环境变量来过滤组件。
SMDK6410.bat文件中与NAND Flash驱动对应的环境变量为BSP_ NONANDFS,如果镜像中需要加载NAND Flash驱动,则在SMDK6410.bat文件中进行如下设置:
set BSP_NONANDFS=
如果镜像中不需要加载NAND Flash驱动,则在SMDK6410.bat文件中进行如下设置:
set BSP_NOMFC = 1
7.2 Dir文件
通过Dir文件将NAND Flash驱动源文件添加到编译目录树中,这样PB6.0在驱动编译前扫描目录树时就能够扫描到NAND Flash驱动源文件,然后对其进行编译。
WINCE600\PLATFORM\SMDK6410\Src\Common\Nandflash路径下的Dir文件为:
DIRS=\
FMD \
DLL
整个NAND Flash驱动由FMD文件夹和DLL文件夹中的源文件及配置文件构成。FMD层驱动的源代码均在FMD文件夹下,FAL层的的源代码不可见,微软提供了实现该部分功能的lib库,编译后链接时可以直接使用。
7.3 Source文件
Ø FMD文件夹中的sources文件定义了要编译的源文件及要生成的目标文件,内容为:
TARGETNAME=nandflash_lib
TARGETTYPE=LIBRARY
RELEASETYPE=PLATFORM
SYNCHRONIZE_BLOCK=1
WINCEOEM=1
WINCECPU=1
NOMIPS16CODE=1
ADEFINES=-pd "_TGTCPU SETS \"$(_TGTCPU)\"" $(ADEFINES)
LDEFINES=-subsystem:native /DEBUG /DEBUGTYPE:CV /FIXED:NO
INCLUDES=$(INCLUDES)
SOURCES=\
fmd.cpp
ARM_SOURCES=\
nand.s
内容解释:
TARGETNAME=nandflash_lib
TARGETTYPE=LIBRARY
RELEASETYPE=PLATFORM
TARGETNAME表示源文件编译生成的目标文件的名字,TARGETTYPE= LIBRARY表目标文件的类型为lib文件,RELEASETYPE指定编译输出路径,platform表示将输出存放在平台目录下。
SYNCHRONIZE_BLOCK=1
该宏的时装表示要求编译器首先编译该目录。
WINCEOEM=1
WINCECPU=1
WINCEOEM=1表示要使用Wince下公用的头文件和库文件。WINCECPU=1表示bulid.exe将生成的目标文件存放在_TGTCPU指定的子目录下,WINCECPU=0表示将目标文件存放在_CPUINDPATH指定的子目录下。
ADEFINES=-pd "_TGTCPU SETS \"$(_TGTCPU)\"" $(ADEFINES)
LDEFINES=-subsystem:native /DEBUG /DEBUGTYPE:CV /FIXED:NO
ADEFINES定义要被编译的汇编语言文件的路径;LDEFINES定义了链接器使用的标记。
SOURCES=\
fmd.cpp
ARM_SOURCES=\
nand.s
这几行表示要被编译的源文件和汇编源文件的文件名。
Ø DLL文件夹中的sources文件定义了生成目标文件要链接的.lib文件,及要NAND Flash驱动最终要生成的目标文件nandflash.dll,内容为:
TARGETNAME=nandflash
TARGETTYPE=DYNLINK
RELEASETYPE=PLATFORM
WINCEOEM=1
DEFFILE=nandflash.def
TARGETLIBS=\
$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib \
$(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\fal.lib \
$(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\fmdhooklib.lib \
$(_TARGETPLATROOT)\lib\$(_CPUINDPATH)\nandflash_lib.lib
SOURCES=
解释:
TARGETNAME=nandflash
TARGETTYPE=DYNLINK
RELEASETYPE=PLATFORM
这三行定义了生成目标文件名为nandflash,目标文件类型为dll文件,编译输出路径在platform路径下。
WINCEOEM=1
DEFFILE=nandflash.def
前一行表示用Wince下公用的头文件和库文件。后一行表示所用的def文件为nandflash.def。
TARGETLIBS=\
$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib \
$(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\fal.lib \
$(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\fmdhooklib.lib \
$(_TARGETPLATROOT)\lib\$(_CPUINDPATH)\nandflash_lib.lib
SOURCES=
这几行定义了生成目标文件所需要链接的lib库的路径及名称。该文件夹下没有源文件,因此SOURCES下文件名为空。
7.4 def文件
def文件定义了要导出的接口,这些接口是NAND Flash驱动对外的接口,对应接口的函数均在FAL层实现。
LIBRARY nandflash
EXPORTS DSK_Init
DSK_Deinit
DSK_Open
DSK_Close
DSK_Read
DSK_Write
DSK_Seek
DSK_IOControl
DSK_PowerDown
DSK_PowerUp
解释:
LIBRARY nandflash
表示向链接器声明创建一个DLL文件为nandflash.dll。EXPORTS声明了该驱动对外导出的标准流接口,nandflash驱动的标准接口以DSK为前缀。
8. 驱动的加载和调用流程
图10 NAND Flash驱动的加载过程
驱动的基本加载过程:
(1) 系统启动时内核加载设备管理器Device.exe。
(2) 由Divice.exe加载资源管理器,以从注册表读取一个可用资源列表。
(3) Divice.exe从HKEY_LOCAL_MACHINE\Drivers\RootKey加载注册表枚举器。注册表开始扫描注册表过程,以获得由RootKey下的子键加载的更多总线和设备。
(4) 在扫描到HKEY_LOCAL_MACHINE\Drivers\BuiltIn\NANDFLASH注册表项时,从该键下的DLL进入NAND Flash驱动的入口。
9. 重点难点
9.1 读、写、擦除时序中地址的设置:
页读操作时先读Spare区,后读数据区。在读命令0x00输入后,输入column地址为2048,row地址为要读的页号;在Spare区读完,开始读数据区时,输入第二个读命令0x30后,输入column地址为0。
页写操作时先写数据区,后写Spare区。在写命令0x80输入后,输入column地址为0,row地址为要读的页号;在数据区写完,开始写Spare区时,输入第二个写命令0x85后,输入column地址为2048。
擦除操作时,输入参数为要擦除的块号,但实际需要向地址寄存器中写入的擦除地址为要擦除块的第一页的地址,因此在擦除地址输入之前要进行参数转换。
9.2 1位ECC软件纠错的实现:
NAND Flash控制器本身支持硬件ECC检错,并将出错的字节和该字节中的出错位分别存放在ECC错误状态寄存器NFECCERR0的相应位中,在软件方式进行纠错时,可以直接从寄存器中读出出错的字节,将该字节的出错位与1进行异或运算,可实现错误纠正。
9.3 FMD_OEMIoControl函数作用:
该函数主要用于被应用程序或内核调用,用来获取硬件平台信息。通过对该函数进行修改,并增加用户自己定义的IOCTL,可以在应用程序下实现所需要的操作。
10. 问题总结
驱动中存在的一个问题是:开发板上焊接了MLC NAND Flash K9G8G08,在初始化时通过读取芯片的ID可知芯片的ID正是K9G8G08的ID 0xD3,由于初始化芯片参数数组中芯片ID与参数匹配的错误,导致对芯片的参数赋了错误的值(总块数和每块的页数错误,其它参数正确)。
华恒BSP包中的NAND Flash驱动是针对SLC NAND Flash驱动(包括大页和小页),要使驱动支持MLC NAND Flash,需要对代码进行进一步的修改。
11. 参考资料
1. S3C6410 USER’S MANUAL.pdf
2. K9G8G08 datasheet.pdf
3. NAND Flash Spare Area Assignment Standard.pdf
4. 《Windows CE 实用开发技术》张冬泉等编著