Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
ECC
FSMC_FMC_外扩总线
nandflash驱动关键配置参数说明
发布于 2021-01-26 09:36:34 浏览:3683
订阅该版
**一,nand flash简介** **1,相关名词的解释** 1.1 (bad)Block Management(坏)块管理 Nand Flash由于其物理特性,只有有限的擦写次数,超过那个次数,基本上就是坏了。在使用过程中,有些Nand Flash的block会出现被用坏了,当发现了,要及时将此block标注为坏块,不再使用。于此相关的管理工作,属于Nand Flash的坏块管理的一部分工作。 **1.2 Wear-Leveling 负载平衡** Nand Flash的block的管理,还包括负载平衡。正是由于 Nand Flash的block,都是有一定寿命限制的,所以如果你每次都往同一个block擦除然后写入数据,那么那个block就很容易被用坏了,所以我们要去管理一下,将这么多次的对同一个block的操作,平均分布到其他一些block上面,使得在block的使用上,相对较平均,这样相对来说,可以更 能充分利用Nand Flash。关于wear-leveling这个词,再简单解释一下,wear就是穿(衣服)等,用(东西)导致磨损,而leveling就是使得均衡,所以放在一起就是,使得对于Nand Flash的那么多的block的使用磨损,相对均衡一些,以此延长Nand Flash的使用寿命或者说更加充分利用Nand Flash。 **1.3 ECC错误校验码** Nand Flash物理特性上使得其数据读写过程中会发生一定几率的错误,所以要有个对应的错误检测和纠正的机制,于是才有此ECC,用于数据错误的检测与纠正。Nand Flash的ECC,常见的算法有海明码和BCH,这类算法的实现,可以是软件也可以是硬件。不同系统,根据自己的需求,采用对应的软件或者是硬件。 相对来说,硬件实现这类ECC算法,肯定要比软件速度要快,但是多加了对应的硬件部分,所以成本相对要高些。如果系统对于性能要求不是很高,那么可以采用软件实现这类ECC算法,但是由于增加了数据读取和写入前后要做的数据错误检测和纠错,所以性能相对要降低一些,即Nand Flash的读取和写入速度相对会有所影响。(使用软件的话,可以参照yaffs2中ecc算法); **1.4 OOB/Spare Area** 每个页,相应另一块区域。叫做空暇区域(SpareArea)。在Linux系统中。一般叫做OOB(Out of Band)。 数据在读写的时候相对easy错误,所以为了保证数据的正确性。必需要有相应的检測和纠正机制,此机制叫做ECC/EDC,所以设计了多余的区域,用于存放数据的校验值。OOB的读写是随着随着页的操作一起完毕的。 OOB的详细用途包含下面几个方面: ► 标记所处的block是否为坏块 ► 存储ECC数据 ► 存储一些和文件系统相关的数据。如jaffs2就会用到这些空间存储一些特定信息。而yaffs2文件系统。会在oob中存放非常多和自己文件系统相关的信息 **1.5容量计算** NAND芯片内部分为die, plane,block, page Chip是指芯片,一个封装好的芯片就是一个chip Die(internal chip)是晶圆上的小方块,一个芯片里可能封装若干个die,因为flash的工艺不一样,技术不一样。由此产生了die的概念。常见的有Mono Die,a Die。 b die等,一个chip包括N个die。 Plane是NAND可以依据读、写、擦除等命令进行操作的最小单位,一个plane就是一个存储矩阵。包括若干个Block。 Block是NANDFlash的最小擦除单位,一个Block包括了若干个Page。 Page是NANDFlash的最小读写单位。一个Page包括若干个Byte。 Chip >Die > plane > block > page pageSZ*blockPN*planeBN*diePN*dieNumber=ChipSZ; 已华邦的W29N02GVSIAA为例: ![b69faedf-ff68-4b93-8408-6cc0f0fd5ae2.png](https://oss-club.rt-thread.org/uploads/20210126/2c9aa66c3a163f8a77dc567a72f064b2.png) 2K*64*1024*2*1= 256MB; **1.6 芯片ID** 每个厂家的ID内容都有一些不同,但是关键参数基本是一致的,所以在做兼容的时候需要以芯片厂家为单位做区分和自动识别; nandflash 有两个ID地址 00h(device identifier code) 20h(ONFI identifier code);一般只看00h地址中的ID,它包含了芯片组成结构的基本信息,用于初始化nandflash的驱动;00h可以读出5个字节(通常只使用4个字节) ![捕获.PNG](https://oss-club.rt-thread.org/uploads/20210126/25fde487ad0985ca8baec13d6ff1fb27.png) **1.7 读写地址计算** 假设,我们要访问其中的第100个块中的第64页中的1208字节处的地址,此时,我们就要先把具体的 地址算出来: 物理地址 =块大小×块号 + 页大小×页号 + 页内地址 =128K×100 + 2K×64 + 1208 =0xCA04B8 但是nandflash地址需要做一些转换,因每个页里面都有oob区域,读数据时需要跳过这些地址,要读oob时则需要选择到oob的页内偏移,所以nand把地址分为了页内地址和页地址 ![123.png](https://oss-club.rt-thread.org/uploads/20210126/98c013234be60c213921b00213ed3e31.png) 对应的就是: Column address(列地址,页内地址)=1208=0x4b8 RowAddress(行地址,页地址)=(128K/2K)*100 + 64 = 0x1940 所以发送的地址为0x194004b8。 oob如何访问呢?oob是连续在main区域的后面所以只需要知道页地址就可以了即: oob区域的地址: Column address(列地址,页内地址)=page size=2048=0x800 RowAddress(行地址,页地址)=(128K/2K)*100 + 64 = 0x1940 **1.8 ECC校验** ECC的全称是Error Checking and Correction,是一种用于Nand的差错检测和修正算法。如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。ECC能纠正1个比特错误和检测2个比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。 ECC一般每256字节原始数据生成3字节ECC校验数据,包含列校验和行校验。对每个待校验的bit位求异或,若结果为0,则表示有偶数个1,反则有奇数个1。这三字节共24比特分成两部分:6比特的列校验和16比特的行校验,多余的两个比特置1,如下图所示: ![7929_12924282529qYY.png](https://oss-club.rt-thread.org/uploads/20210126/b349a0279edc78e605ef6b3404410d9d.png) ECC的列校验、行校验和生成规则如下图所示: ![7929_1292428254jjKq.png](https://oss-club.rt-thread.org/uploads/20210126/e2f25d244e69c0972f40878bf0b3595f.png) 列校验用数学表达式表示为: P1`=D6(+)D4(+)D2(+)D0 表示第0、2、4、6列的异或操作 P1=D7(+)D5(+)D3(+)D1表示第1、3、5、7列的异或操作 P2`=D5(+)D4(+)D1(+)D0 表示第 0、1、4、5列的异或操作 P2=D7(+)D6(+)D3(+)D2表示2、3、6、7列的异或操作 P4`=D3(+)D2(+)D1(+)D0表示0、1、2、3列的异或操作 P4=D7(+)D6(+)D5(+)D4表示4、5、6、7列的异或操作 这里(+)表示“位异或”操作 行验算用数学表达式表示为: P8'=BYTE0+BYTE2+BYTE4....+BYTE254 表示对第0、2、4、6、8、....254字节进行异或操作 P8=BYTE1+BYTE3+BYTE5....+BYTE255 表示对第1、3、5、7、9、....255字节进行异或操作 P16'=BYTE0+BYTE1+BYTE4+BYTE5....BYTE252+BYTE253 表示对第0字节开始间隔2字节进行异或操作 P16=BYTE2+BYTE3+BYTE6+BYTE7....BYTE254+BYTE255 表示对第2字节开始间隔2字节进行异或操作 P32 表示间隔4字节进行异或 P64 表示间隔8字节进行异或 P128 表示间隔16字节进行异或 P256 表示间隔32字节进行异或 P512 表示间隔64字节进行异或 P1024 表示间隔128字节进行异或 **二,MTD层** MTD是Memory Technology Device的缩写,它是底层硬件和上层软件之间的桥梁。对底层来说,它无论对NOR型或是NAND型都有很好的驱动支持;对上层来说,它抽象出文件系统所需要的接口函数。rt-thread环境中没有那么复杂,去掉了Linux MTD中很多无法使用的属性,只剩下了必要的与硬件相关的参数: 在mtd_nand.h中定义了这些属性: ``` struct rt_mtd_nand_device { struct rt_device parent; rt_uint16_t page_size; /* The Page size in the flash */ rt_uint16_t oob_size; /* Out of bank size */ rt_uint16_t oob_free; /* the free area in oob that flash driver not use */ rt_uint16_t plane_num; /* the number of plane in the NAND Flash */ rt_uint32_t pages_per_block; /* The number of page a block */ rt_uint16_t block_total; /* Only be touched by driver */ rt_uint32_t block_start; /* The start of available block*/ rt_uint32_t block_end; /* The end of available block */ /* operations interface */ const struct rt_mtd_nand_driver_ops *ops; }; struct rt_mtd_nand_driver_ops { rt_err_t (*read_id)(struct rt_mtd_nand_device *device); rt_err_t (*read_page)(struct rt_mtd_nand_device *device, rt_off_t page, rt_uint8_t *data, rt_uint32_t data_len, rt_uint8_t *spare, rt_uint32_t spare_len); rt_err_t (*write_page)(struct rt_mtd_nand_device *device, rt_off_t page, const rt_uint8_t *data, rt_uint32_t data_len, const rt_uint8_t *spare, rt_uint32_t spare_len); rt_err_t (*move_page)(struct rt_mtd_nand_device *device, rt_off_t src_page, rt_off_t dst_page); rt_err_t (*erase_block)(struct rt_mtd_nand_device *device, rt_uint32_t block); rt_err_t (*check_block)(struct rt_mtd_nand_device *device, rt_uint32_t block); rt_err_t (*mark_badblock)(struct rt_mtd_nand_device *device, rt_uint32_t block); }; ``` 在底层驱动中实现这些函数和填写这些变量即可。 **三、STM32 FMC时序设置** FMC中操作nandflash的时序参数名能和nandflsh中的参数一一对应,需要做一些调整。 tCLR CLE to #RE Delay tAR ALE to #RE Delay FMC还有两个不同时序空间的时序控制: 通用存储器空间的时序寄存器: FMC_PMEM ,这个寄存器是我们读写nand常用的时序空间 特性存储器空间的时序寄存器: FMC_PATT,这个是一些特殊nand需要设置某些参数时需要用到时序空间; 他们有4个值分别是: MEM_SET, ATT_SET MEM_HIZ, ATT_HIZ MEM_WAIT, ATT_WAIT MEM_HOLD, ATT_HOLD 时序解释如下图所示: ![456.PNG](https://oss-club.rt-thread.org/uploads/20210126/ba99c981e581084eefa1d687bb83b7d6.png) CE无关访问中的tWB=ATT_HOLD;其他的两个空间可以保持一致; 数据手册的原话:要克服该时序限制,可使用特性存储器空间,将其时序寄存器编程为满足 tWB(WE HIGH to Busy) 时序的ATTHOLD 值,并将 MEMHOLD 保持为其最小值。 MEM_SET:建立时间(地址或者数据建立时间);定义使能命令(NWE,NOE)前建立地址所需的时间,可以等价与nandflash中的tWHR(WE HIGH to #RE LOW) MEM_HIZ:数据总线高阻态时间(只对写入时序有效);定义在对 NAND Flash 的通用存储空间开始执行写访问之后,数据总线保持高阻态所持续的 时间,可以等价与nandflash中TADL(ALE to Data Loading Time); MEM_WAIT:等待时间;定义使能命令(NWE、NOE)所需的 持续时间的最小值,可以等价于nandflash中tWP(WE Pulse Width),tRP(RE Pulse Width); MEM_HOLD:保持时间;定义禁止命令(NWE、NOE)之后保持地址(和写访问数据)的写入访问的时间和读取访问的时间;可以等价与nandflash中tCH (CE Hold Time),tDH(Data Hold Time),如果为了兼容CE无关操作,控制器自动等待NWAIT,则使用tWB(WE hight to busy)。 **四,FMC 硬件ECC** 软件ECC纠错算法生成的字节表为 :byte0(rp0~rp7);byte1(rp8~rp15);byte2(11 cp0~p5)(见1.8的表) 经过实际验证FMC 硬件ECC生成数据(ECC页长度为256,一共只有21位) FMC ECC tab:bit0~bit5:cp0~cp5;bit6~bit13:rp0~rp7;bit14~bit21:rp8~rp15; 软件ecc与硬件ECC是取反的关系,且排布顺序不一致,因此需要做相应的修改才能兼容软件ECC的校验值。软件ECC为什么要取反呢,因为Nandflash写入0是有效的,写入1是无效,所以在全是0xff的位置硬件ECC的校验值为0x000000,;如果把这个值接接写入nand的oob区,那么这个区域就对应的oob区就无法修改了(因为全是0了),下次写入校验时肯定就报ECC错误。所以要把ECC校验值取反在存储。 FMC的ECC值需要做以下变化才能和软件ECC兼容:变化过程 HECC = ~HECC;HECC=(HECC <<2)|0x03;HECC= (HECC<<16)|(HECC>>16)(byte0与byte2交换位置,伪代码); **五, nandflash芯片识别** 最开始的设想也是想着使用ID号去做芯片参数的自识别,然后多看了几家nandflash厂家的芯片 datasheet,发现某些位不同厂家定义不完全相同,不同工艺的nand相同的位置代表的意思也有出入,所以用ID号去自识别是一个不靠谱的做法。最后只能参照uboot做法将要支持的nand写入配置表中: ``` const struct nand_flash_dev nand_flash_id[]= { {"F59L1G81MB 1Gb 3.3v 8bit",{.id={0xC8,0xD1,0x80,0x95}},2048,128,128*1024,4,64}, {"F59L2G81XA 2Gb 3.3v 8bit",{.id={0x2C,0xDA,0x90,0x95}},2048,256,128*1024,4,128}, {"F59L4G81XB 4Gb 3.3v 8bit",{.id={0x2C,0xDC,0x80,0xA6}},4096,512,256*1024,4,256}, {"W29N01HV 1Gb 3.3v 8bit",{.id={0xEF,0xF1,0x00,0x95}},2048,128,128*1024,4,64}, {"W29N02KV 2Gb 3.3v 8bit",{.id={0xEF,0xDA,0x10,0x95}},2048,256,128*1024,4,128}, {"W29N04GV 4Gb 3.3v 8bit",{.id={0xEF,0xDC,0x90,0x95}},4096,512,128*1024,4,64}, }; ``` 结构体如下代码: ``` struct nand_flash_dev { char *name; //nandflash名称 _nand_id_t u_nand_id; //nandflash ID unsigned long pagesize; //页大小 unsigned long chipsize; //器件总容量(MB) unsigned long erasesize; //擦除大小(块大小) //unsigned long options; //设置量,单片机只提供一种读写方法,不会在做读写上的优化,不适用; uint16_t id_len; //ID长度 uint16_t oobsize; //oob区大小 // struct { // uint16_t strength_ds; // uint16_t step_ds; // } ecc; //ECC格式 For example, the "4bit ECC for each 512Byte",单片机不需要 // int onfi_timing_mode_default; //the default ONFI timing mode entered after a NAND reset. Should be deduced from timings described in the datasheet. }; /*nand属性结构体*/ struct nand_attriute { uint8_t u8IdInfIndex; //id 消息索引 uint32_t u32ChipSize; //容量,单位MB uint32_t u32PageSize; //页大小 uint32_t u32BlockSzie; //块大小 uint32_t u32BlockPageNum;//块包含的页数 uint32_t u32BlockNum; //块的数量 uint32_t u32oobSize; //oob区大小 }; ``` 在初始化完FMC接口后,去读取nand的ID号,与自己的tab比较就知道现在焊接的是什么nand芯片了。 ![856.png](https://oss-club.rt-thread.org/uploads/20210126/27c4f5a05bc57f3457061723ae526082.png)
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
yoyotansa
这家伙很懒,什么也没写!
文章
1
回答
12
被采纳
0
关注TA
发私信
相关文章
1
SRAM进行初始化,找不到这三个函数(已解决)
2
rtthread fsmc驱动LCD代码无错误,且背光显示,但是无现象
3
rtthread操作LCD异常,不知道是哪里错误
4
undefined symbol FSMC_NORSRAM是什么错误?怎么修改?
5
RTT有没有关于FSMC的资料?
6
求助STM32 FSMC如何使用
7
工程缺少是stm32F1xx_ll_fsmc.c文件
8
STM32F103VE指南者使用ILI9341求助
9
RT-Thread如何能完全运行在片外NorFlash
10
RT-Thread Studio移植TFTLCD(FSMC)
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
RTT_逍遥
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部