我是靠谱客的博主 踏实蜗牛,最近开发中收集的这篇文章主要介绍SPI NAND FLASH坏块管理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

这个比较少见,因为有些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坏块管理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(68)

评论列表共有 0 条评论

立即
投稿
返回
顶部