概述
这个比较少见,因为有些spi nand flash是提供了一个坏块表管理的.恰好我使用的这款是没有的.
第一步首先抽象出nand 驱动
typedef struct{
unsigned char (*readID)(unsigned char *rxID);
unsigned char (*init)(void);
unsigned char (*readPage)(unsigned long page,
unsigned char *data,
unsigned long data_addr,
unsigned long data_len);
unsigned char (*writePage)(unsigned long page,
unsigned char *data,
unsigned long data_addr,
unsigned long data_len);
unsigned char (*setPage)(unsigned long page,
unsigned char value,
unsigned long data_addr,
unsigned long data_len);
unsigned char (*movePage)(unsigned long destpage,
unsigned long srcpage,
unsigned char *data,
unsigned long data_addr,
unsigned long data_len);
unsigned char (*eraseBlock)(unsigned long page);
unsigned char (*ecc_error)(void);
}NandDriver_t;
第二步,根据资料spi nand flash定义参数:
在这里该flash是由1024个block构成,每个block有64页,每页含2048个字节数,及nor干个字节的spare区
#define PAGE_SIZE 2048
#define BLOCK_PAGES 64
#define BLOCKS_PER_ZONE 1024
#define MAX_PHY_BLOCKS_PER_ZONE BLOCKS_PER_ZONE
#define MAX_LOG_BLOCKS_PER_ZONE 1004
#define ERASE_BLOCKSZIE (BLOCK_PAGES*PAGE_SIZE)
#define GET_BYTE_FROM_ADDRESS(x) (x&(PAGE_SIZE-1))
#define GET_PAGE_FROM_ADDRESS(x) (x>>11)
#define GET_BLOCK_FROM_ADDRESS(x) (x>>17)
#define GET_PAGE_FROM_BLOCK(x) (x<<6)
#define GET_BLOCK_FROM_PAGE(x) (x>>6)
#define VALID_BLOCK (1<<12)
#define FREE_BLOCK (1<<13)
#define BACKBAD_BLOCK (1<<14)
#define BAD_BLOCK (1<<15)
uint16_t LUT[MAX_PHY_BLOCKS_PER_ZONE]
第三步,我们需要区分下哪些是bad block.通常厂家会在spare区放一个bad block的标志.另外还需提供一个mark bad block的API以供新的bad block.
uint8_t isBadBlock(unsigned long page)
{
//add your code here
}
uint8_t markBadBlock(unsigned long page)
{
//add your code here
}
第四步,建立坏块表.通常情况下业内允许2%的坏块数量,故上面保留了20个block用于替补
uint16_t GetFreeBlock()
{
uint16_t block;
for(block = MAX_LOG_BLOCKS_PER_ZONE; block < MAX_PHY_BLOCKS_PER_ZONE; block++){
if(LUT[block] & FREE_BLOCK)
return block;
}
else
return 0;
}
int BuildLUT()
{
uint16_t block;
uint16_t backblock;
//check free
for(block = MAX_LOG_BLOCKS_PER_ZONE; block < MAX_PHY_BLOCKS_PER_ZONE; block++){
if(IsBadBlock(GET_PAGE_FROM_BLOCK(block))){
//mark bad
LUT[block] = block | BAD_BLOCK;
}
else{
//mark free
[block] = block | FREE_BLOCK;
}
}
for(block = 0; block < MAX_LOG_BLOCKS_PER_ZONE; block++){
if(IsBadBlock(GET_PAGE_FROM_BLOCK(block))){
//mark bad
LUT[block] = i | BAD_BLOCK;
backblock = GetFreeBlock();
if(!backblock)
return -1;
LUT[backblock] = block | BACKBAD_BLOCK | VALID_BLOCK;
}
else{
//mark valid
LUT[block] = i | VALID_BLOCK;
}
}
return 0;
}
第五步,有了坏块表,那么需要进行逻辑转换,坏块指引到后备表.
uint16_t GetBackBlock(uint16_t badblock)
{
uint16_t i;
uint8_t zone;
uint16_t blockmark;
blockmark = badblock | BACKBAD_BLOCK | VALID_BLOCK;
for(block = MAX_LOG_BLOCKS_PER_ZONE; block < MAX_PHY_BLOCKS_PER_ZONE; block++){
if(LUT[block] == blockmark)
{
return block;
}
}
return 0;
}
#define LOG_CONVERT_PHY_FAILED_ADDR 0xFFFFFFFF
uint32_t LogicConvertPhyAddress(uint32_t address)
{
int ret;
uint16_t backblock;
uint16_t block;
if(LUT[block] & BAD_BLOCK){
backblock = GetBackBlock(block);
if(!backblock){
return LOG_CONVERT_PHY_FAILED_ADDR;
}
address = (address &(ERASE_BLOCKSZIE - 1)) | (backblock * ERASE_BLOCKSZIEe);
}
return address;
}
第六步,坏块表弄好了,不能每次都建立吧,万一里面有数据内,所以必须把坏块标志保存到flash的spare区中
uint8_t markBlock(unsigned long block, uint16_t mark){
//add your code here
}
void LUTMarkBlock()
{
uint16_t block;
for(block = 0; block < MAX_PHY_BLOCKS_PER_ZONE; block++){
//mark valid
markBlock(block, LUT[block]);
}
}
第七步,到了这一步,坏块表已经弄好了,逻辑转换也好了,那么还差什么呢?新坏块的管理,新坏块怎么出现的呢?
新坏块的往往是伴随着读操作,写操作,出现了ECC错误,ECC累计错误达到无法纠正时,就有必要进行坏块测试了.
所谓坏块测试,就是将本块重新擦除后,进行写入的操作,然后再读出数据,看看是否能继续触发ECC错误.(这个步骤叫做torture)
void MarkBadToBack(uint16_t backblock, uint16_t badblock)
{
//add your code
}
extern NandDriver_t NandDriverHandle;
void ECCErrorToRTure(uint16_t eccblock)
{
uint32_t i;
uint32_t eccpage;
uint32_t bkpage;
uint16_t backblock;
uint8_t usermark[2];
uint8_t backmark[5];
backblock = GetFreeBlock(eccblock);
eccpage = GET_PAGE_FROM_BLOCK(eccblock);
bkpage = GET_PAGE_FROM_BLOCK(backblock);
if(!NandDriverHandle->ecc_error()){
// not ecc error
return;
}
NandDriverHandle->eraseBlock(bkpage);
for(i = 0; i < BLOCK_PAGES; i++){
NandDriverHandle->movePage(bkpage+i, eccpage+i, NULL, 0, 0);
}
LOG(LOG_INFO, "move valid data from %d block to %d blockn", eccblock, backblock);
NandDriverHandle->eraseBlock(eccpage);
for(i = 0; i < BLOCK_PAGES; i++){
if(!NandDriverHandle->setPage(eccpage+i, 0xa5, 0, PAGE_SIZE)){
if(NandDriverHandle->readPage(eccpage+i, usermark, 0, 2))
{
if(NandDriverHandle->ecc_error()){
LOG(LOG_INFO, "detect block %d truely badn", eccblock);
markBadBlock(eccpage);
MarkBadToBack(bkpage,eccpage);
LUT[eccblock] = BAD_BLOCK | eccblock;
LUT[backblock] = eccblock | BACKBAD_BLOCK | VALID_BLOCK;
return;
}
}
}
}
//move back
NandDriverHandle->eraseBlock(eccpage);
for(i = 0; i < BLOCK_PAGES; i++){
NandDriverHandle->movePage(eccpage+i, bkpage+i, NULL, 0, 0);
}
LOG(LOG_INFO, "move back data from %d block to %d blockn", backblock, eccblock);
//clean
NandDriverHandle->eraseBlock(bkpage);
return;
}
好了到这边工作已经完成99%,剩下1%就是将这个坏块表保存在第一个block中,下次启动时直接加载.当然还要进行一次检查,以确保这个表是正确的.检查方式就是跟着建表逻辑一起来.我想到了这步基本都会了吧.
最后
以上就是踏实蜗牛为你收集整理的SPI NAND FLASH坏块管理的全部内容,希望文章能够帮你解决SPI NAND FLASH坏块管理所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复