ThreadX LevelX NOR闪存的open流程
LevelX NOR闪存的数据管理结构
想要了解LevelX对NOR闪存的数据管理方式,首先必须要知道LevelX的管理数据在NOR闪存(NAND一样)。NOR闪存是由通常被512字节平均分割的块组成的。NOR闪存中没有闪存页的概念(太绝对)。
此外,NOR闪存中没有备用字节,因此LevelX必须使用NOR闪存本身的所有管理信息。在NOR闪存中可以直接读取访问。写入访问通常需要一个特殊的操作序列。NOR闪存可以被多次写入,前提是位正在被清除。NOR闪存中的位只能通过擦除块操作设置一次。
LevelX将每个NOR闪存块划分为512字节的逻辑扇区(这里时LevelX划分的,只需要在底层驱动部分做适配只与地址相关即可)。 此外,LevelX使用每个NOR闪存块的前“ n”个扇区来存储管理信息。 LevelX NOR闪存管理信息的格式为:
BYTE OFFSET | CONTENTS |
---|---|
0 | [Block Erase Count] |
4 | [Minimum Mapped Sector] |
8 | [Maximum Mapped Sector] |
12 | [Free Sector Bit Map] |
m | [Sector 0 Mapping Entry] |
… | … |
m+4*(n-1) | [Sector “n” Mapping Entry] |
… | … |
s | [Sector 0 Contents] |
… | … |
s+512*(n-1) | [Sector “n” Contents] |
也许这张表不能够直接反应管理信息直接在NOR闪存中的存储格式,下面更改为这样的一张表:
Addr0 (physicalSector0) | BlockHeader | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
Block Erase Count | Minimum Mapped Sector | Maximum Mapped Sector | Free Sector Bit Map0 | Free Sector Bit Map1 | … | Sector "0" Mapping Entry | … | Sector "n" Mapping Entry | … | |
Addr s | Sector "0" Contents | |||||||||
Sector "1" Contents | ||||||||||
Sector "2" Contents | ||||||||||
... | .... | |||||||||
Addr (s+512*(n-1)) | Sector "n" Contents |
LevelX NOR闪存的BlockHeader信息
我们通过上述的描述以及表格中的存储结构可以看出,LevelX规定每512字节/128字为一个逻辑扇区,LevelX的最小管理单元是一个512字节/128字的扇区大小。这样做有利于提高数据管理的最小颗粒度,在小文件的管理上做到方便,快速以及提高空间利用率。
LevelX实际是把每块的第一或前几个扇区用来存储管理的元数据,也就是BlockHeader信息。BlcokHeader大小直接取决于一个闪存块的大小,闪存块越大,定义的每512字节/128字的逻辑扇区越多,所占用的[Free Sector Bit Map]、[Sector x Mapping Entry]也就越大。
我们来分析BlockHeader成员的意义:
- [Block Erase Count]:表示的是该物理块被删除的次数, 其中位31:1->未使用,0->已使用 ;
- [Minimum Mapped Sector]:表示该扇区中最小的逻辑扇区号,可以加速逻辑扇区的搜索;
- [Maximum Mapped Sector]:表示该扇区用最大的逻辑扇区号,可以加速逻辑扇区的搜索;
- [Free Sector Bit Map]:是该扇区中空闲扇区的位图,每个bit代表扇区使用状态1:未使用,0:已经使用;每个字最多表示32个扇区,所以定义的块中的逻辑扇区越多,Free Sector Bit Map占用的字空间也就越多;
- [Sector “n” Mapping Entry]:表示该块中的每个扇区使用状态,所以定义的块中的逻辑扇区越多,Sector Mapping Entry占用的字空间也就越多;
其中,Sector Mapping Entry的格式如下表所示:
Offset(bit) | Meaning |
---|---|
31 | 有效标志。 置位且逻辑扇区不全为1时,表示映射有效。 |
30 | 过时标志。清除后,此映射要么已过时,要么正在过时。 |
29 | 该位为0时,映射Entry写入完成。 |
0-28 | 逻辑扇区映射到该物理扇区(不是全部)。 |
提醒一下,实际使用的NOR闪存实际扇区大小很可能不是512字节大小,LevelX在实际操作到物理扇区时,采用的数绝对地址来进行的,所以在底层的驱动层中,只需要做到输入操作地址的形参是绝对地址即可做到任意分区。
数据管理初始化流程
想要了解LevelX的数据管理流程,最先需要看的数LevelX对NOR闪存的初始化操作,这样,我们就必须查看lx_nor_flash_open;
函数;
LevelX 对NOR 闪存的初始化
UINT _lx_nor_flash_open(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *))
底层驱动绑定
nor_driver_initialize:是底层的NOR闪存驱动代码,由用户自己实现。
/* Clear the NOR flash control block. */
TX_MEMSET(nor_flash, 0, sizeof(LX_NOR_FLASH));
/* Call the flash driver's initialization function. */
(nor_driver_initialize)(nor_flash);
TX_MEMSET(nor_flash, 0, sizeof(LX_NOR_FLASH));
对实例直接进行全部赋值为0;
// `
(nor_driver_initialize)(nor_flash);
将底层驱动读写擦操作与LevelX读写擦绑定,即初始化LevelX驱动;
计算BlockHeader信息
/* 设置空闲位图的偏移量。*/
nor_flash -> lx_nor_flash_block_free_bit_map_offset = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG);
/* 计算空闲物理扇区位映射中所需的位数。减去 1 以考虑闪存块Header本身。
下面将处理某些尺寸的闪存设备需要多个物理扇区的情况。*/
sectors_per_block = (nor_flash -> lx_nor_flash_words_per_block / LX_NOR_SECTOR_SIZE) - 1;
/* 计算扇区映射数阵列所需的字数。*/
sector_map_words = sectors_per_block;
/* 计算我们需要的空闲物理扇区位图的字数。*/
bit_map_words = (sectors_per_block + 31)/ 32;/*这里是默认每个字是4字节*/
/*保存位图需要的字数。*/
nor_flash -> lx_nor_flash_block_bit_map_words = bit_map_words;
/* 设置物理扇区映射数组的偏移量(单位:字) */
nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset = nor_flash -> lx_nor_flash_block_free_bit_map_offset + bit_map_words;
/*计算闪存块Header所需的总字数。*/
total_header_words = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG) + bit_map_words + sector_map_words;
/*确定是否需要更多的物理扇区,这可能发生在大型设备上。*/
if (total_header_words <= LX_NOR_SECTOR_SIZE)
{
/*舍入到 1 个物理扇区的大小。*/
total_header_words = LX_NOR_SECTOR_SIZE;
}
else
{
/*否则计算需要多少个头扇区*/
header_sectors = (total_header_words-1)/LX_NOR_SECTOR_SIZE;
/* 四舍五入到下一个扇区。*/
header_sectors++;
/*计算总的Header字数,四舍五入到下一个扇区。 */
total_header_words = header_sectors * LX_NOR_SECTOR_SIZE;
/*调整每块的扇区数。*/
sectors_per_block = sectors_per_block - (header_sectors - 1);
}
/* 将偏移量保存到扇区区域。*/
nor_flash -> lx_nor_flash_block_physical_sector_offset = total_header_words;
/* 保存每块物理扇区和总物理扇区。*/
nor_flash -> lx_nor_flash_physical_sectors_per_block = sectors_per_block;
nor_flash -> lx_nor_flash_total_physical_sectors = nor_flash -> lx_nor_flash_total_blocks * sectors_per_block;
/*为小于 32 位的位映射部分生成可用位映射掩码。 */
if ((sectors_per_block % 32) != 0)
{
bit_map_mask = (ULONG)(1 << (sectors_per_block % 32));
bit_map_mask = bit_map_mask - 1;
}
else
{
/*位映射掩码正好有 32 个扇区。*/
bit_map_mask = LX_ALL_ONES;
}
/*将空闲的位图掩码保存在控制块中。*/
nor_flash -> lx_nor_flash_block_bit_map_mask = bit_map_mask;
上面的代码先是根据用户初始化时的块大小,扇区大小等计算出block_free_bit_map_offset的偏移地址,好方便后面查找Free bit map,然后计算出实际的Free bit map占用大小,得到physical_sector_mapping_offset,方便以后找到对应扇区,查看对应扇区的使用情况。
计算出整个BlockHeader的大小,得到physical sector offset 。最后计算出最后一个不足32整数的扇区掩码。方便后面的初始化或异常校验。
找到整个闪存的最大、最小擦除计数
/*循环浏览各块,以确定最小和最大擦除次数。*/
for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
{
/*拾取该块的第一个字。如果flash管理器之前已经执行过,这个字包含了该块的擦除次数。
否则,如果这个字是0xFFFFFFFF,则说明这个闪存块已经被擦除,或者是第一次使用。*/
#ifdef LX_DIRECT_READ
/* Read the word directly. */
block_word = *block_word_ptr;
#else
status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &block_word, 1);
/*检查闪存驱动程序是否出错。驱动程序不应返回错误。 */
if (status)
{
/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
#endif
/*块被抹去了吗?*/
if (((block_word & LX_BLOCK_ERASED) != LX_BLOCK_ERASED) && (block_word != LX_BLOCK_ERASE_STARTED))
{/* 不,有效的区块。 隔离已擦除的计数。 */
erased_count = (block_word & LX_BLOCK_ERASE_COUNT_MASK);
/* 这是新的最小值吗?*/
if (erased_count < min_erased_count)
{/* 是的,记住新的最小值。*/
min_erased_count = erased_count;
}
/*这是新的最大值吗? */
if (erased_count > max_erased_count)
{/*是的,记住新的最大值。*/
max_erased_count = erased_count;
}
}
/* 移至下一个Flash块。*/
block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block);
}
这里 LevelX通过遍历NOR 闪存的存储块,读取每个块的第一个字,通过 (block_word & LX_BLOCK_ERASED) != LX_BLOCK_ERASED) && (block_word != LX_BLOCK_ERASE_STARTED)
操作找出已经被使用过且擦除计数不为0的块,读取其中的0~30位,获取每块的擦除计数,通过块之间比较,获得整个NOR闪存的最大擦除计数与最小擦除计数,这里获取整个闪存的最大和最小擦除计数主要是为了后续的擦除计数分配,最小计数是为了初始化那些没使用过的闪存,最大计数是在检测到异常块时(擦除或写入时出现断电等)的异常情况导致的原擦除计数丢失,直接对该块的擦除计数重新赋值位当前闪存最大擦除计数,也是为了安全考虑。
闪存第一次使用或之前完全擦除过
/* 如果我们尚未发现任何擦除计数,我们可以假设闪存已完全擦除,需要首次设置。 */
if (min_erased_count == LX_ALL_ONES)
{
/*说明这是初始格式。*/
nor_flash -> lx_nor_flash_diagnostic_initial_format = LX_TRUE;
/*将块字指针设置为第一个块的第一个字,这实际上是闪存基地址。*/
block_word_ptr = nor_flash -> lx_nor_flash_base_address;
/*循环浏览各个块以首次设置闪存 */
for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
{
/* 设置与该块中的空闲物理扇区相对应的空闲位图。注意,我们只需要设置空闲位图中没有扇区关联的部分 */
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1);
/*检查闪存驱动程序是否出错。驱动程序不应返回错误。*/
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
/*将初始擦除次数设置为1。*/
block_word = ((ULONG) 1);
/*写入该块的初始擦除计数。*/
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &block_word, 1);
/*检查闪存驱动程序是否出错。驱动程序不应返回错误。*/
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
/*更新整体最小和最大擦除次数。 */
nor_flash -> lx_nor_flash_minimum_erase_count = 1;
nor_flash -> lx_nor_flash_maximum_erase_count = 1;
/*更新空闲物理扇区的数量。 */
nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + sectors_per_block;
/* 移至下一个Flash块。 */
block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block);
}
}
上一个步骤通过遍历所有闪存块得到的最大和最小擦除计数,如果最后的min_erased_count值为0xFFFFFFFF,则表明整个NOR 闪存在使用LevelX之前进行过Chip Erase 或者第一次使用,那么LevelX则对NOR闪存进行一次真正的初始化。通过扇面的代码我们可以看出,LevelX在检测到NOR闪存为第一次使用时,遍历所有的闪存块,将每个闪存块bit31设置为0,表示有效,bit0~bit30设置为1,表示该扇区擦除计数为1.
闪存之通过LevelX使用过
如果闪存之前被使用过,则对每个块、每个块中的每个扇区进行数据正确性校验,防止之前在操作时出现异常现象如异常断电。如果检测到有异常处,则通过异常处理,尽可能使该块重新可用。
这部分代码比较多,进行分段讲解。整个过程主要分为:计算当前空闲扇区数、计算过时扇区并找到块中空闲扇区替代过时扇区、初始化异常扇区、
计算空闲扇区数
/*根据空闲扇区位图计算空闲扇区数。 */
free_sectors = 0;
for (j = 0; j < bit_map_words; j++)
{
/*读取此字的空闲扇区位图。*/
#ifdef LX_DIRECT_READ
/* Read the word directly. */
block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j);
#else
status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j), &block_word, 1);
/* Check for an error from flash driver. Drivers should never return an error.. */
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
#endif
/*计算设置的位数(空闲扇区)。 */
for (k = 0; k < 32; k++)
{
/* Is this sector free? */
if (block_word & 1)
{
/*是的,该扇区是空闲的,请增加空闲扇区的数量。*/
free_sectors++;
/*确定我们是否需要更新搜索指针。 */
if (nor_flash -> lx_nor_flash_free_block_search == nor_flash -> lx_nor_flash_total_blocks)
{/*记住带有空闲扇区的块。*/
nor_flash -> lx_nor_flash_free_block_search = i;
}
}
/*向下移动空闲扇区。 */
block_word = block_word >> 1;
}
}
/*更新空闲物理扇区的数量。 */
nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + free_sectors;
通过读取每块的Free bit map获取到当前块中的空闲扇区数,外循环所有块,得到整个闪存空闲扇区数free_physical_sectors。
异常扇区检测
/*我们现在需要检查映射列表。 */
/*计算有多少非空闲扇区--这包括有效和过时的扇区。*/
used_sectors = sectors_per_block - free_sectors;
/*现在浏览逻辑-物理扇区映射列表。*/
for (j = 0; j < sectors_per_block; j++)
{
/*读取扇区映射列表的字。 */
#ifdef LX_DIRECT_READ
/* Read the word directly. */
block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j);
#else
status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1);
/* Check for an error from flash driver. Drivers should never return an error.. */
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
#endif
/*确定我们是否需要找到一个已使用的扇区。 因为扇区是按照从前到后的方式分配的,所以该块的前used_sectors块一定是我们想找的那些被分配使用了的块*/
if (used_sectors)
{
/* 是的,我们希望使用该Entry。*/
/* 这个扇区是否正在使用中? */
if ((block_word & LX_NOR_LOGICAL_SECTOR_MASK) != LX_NOR_LOGICAL_SECTOR_MASK)
{/*是的该扇区bit0~bit28已经映射了一个逻辑扇区号,即这个扇区正在使用中*/
/*确定是否设置了有效位并且清除了覆盖的位。 这表明该块即将过时。*/
if ((block_word & LX_NOR_PHYSICAL_SECTOR_VALID) && ((block_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) == 0))
{/*bit31 为1是有效的,但是bit30 为0表示该位已经或正在过时了*/
/*增量已过时的计数。 */
nor_flash -> lx_nor_flash_diagnostic_sector_being_obsoleted++;
/*保存当前映射的物理扇区。*/
temp = nor_flash -> lx_nor_flash_mapped_physical_sectors;
/*指示所有物理扇区均已映射,以进行此搜索。 */
nor_flash -> lx_nor_flash_mapped_physical_sectors = nor_flash -> lx_nor_flash_total_physical_sectors;
/* 是的,这个Block(???应该是Sector才对)即将被淘汰。 搜索同时设置了这两个位的逻辑扇区Entry。*/
_lx_nor_flash_logical_sector_find(nor_flash, (block_word & LX_NOR_LOGICAL_SECTOR_MASK), LX_TRUE, &new_map_entry, &new_sector_address);
/*恢复映射的物理扇区数量。 */
nor_flash -> lx_nor_flash_mapped_physical_sectors = temp;
/*确定新的逻辑扇区Entry是否存在。 */
if (new_map_entry)
{/*是的,使当前Entry过时,改为新Entry。 */
block_word = block_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID);
status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1);
/* Check for an error from flash driver. Drivers should never return an error.. */
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
/* Is this the first time? */
if (nor_flash -> lx_nor_flash_diagnostic_sector_obsoleted)
{/*不,这是一个潜在的格式错误,因为在一个给定的NOR闪存格式中,这应该只发生一次。 */
_lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT);
/* Return an error. */
return(LX_ERROR);
}
/*增加过时的计数。*/
nor_flash -> lx_nor_flash_diagnostic_sector_obsoleted++;
}
}
}
/*确定该扇区是否空闲。*/
else if (block_word == LX_NOR_PHYSICAL_SECTOR_FREE)
{/*到了这里说明该扇区已经被使用/分配了,但是存储的逻辑扇区却是默认值(0xFFFFFFFF),说明在将新的逻辑扇区号写入这里的时候发生了断电,没有实际写入,所以这里的值仍然是默认值*/
/*当仍有使用的扇区时,Free Entry意味着扇区已被分配,并且在将新的逻辑扇区号写入列表之前发生了电源中断。*/
/* Is this the first time? */
if (nor_flash -> lx_nor_flash_diagnostic_mapping_invalidated)
{
/* 不,这是一个潜在的格式错误,因为在一个给定的NOR闪存格式中,这应该只发生一次。*/
_lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT);
/* Return an error. */
return(LX_ERROR);
}
/*将0写入此Entry以使扇区Entry无效。 */
block_word = 0;
status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1);
/* Check for an error from flash driver. Drivers should never return an error.. */
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
/*增加映射无效的数量。*/
nor_flash -> lx_nor_flash_diagnostic_mapping_invalidated++;
}
/*是的,现在确定该扇区是否已过时。*/
if ((block_word & LX_NOR_PHYSICAL_SECTOR_VALID) == 0)
{
/*增加过时的扇区数。 */
nor_flash -> lx_nor_flash_obsolete_physical_sectors++;
}
/*确定此扇区映射是否仍然有效。 */
else if (block_word & LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID)
{/*到了这里,意味这扇区已经被使用,也就是说该扇区对应的Entry已经写入完成,那么bit 29 就应该为0表示Entry写入完成,但是现在该bit却是1,则说明在写入Entry的时候,发生了断电*/
/*是的,在写入扇区映射Entry时发生了电源中断或复位。 */
/*增加过时的扇区数。*/
nor_flash -> lx_nor_flash_obsolete_physical_sectors++;
/*增加中断的映射计数器。*/
nor_flash -> lx_nor_flash_diagnostic_mapping_write_interrupted++;
/*使该Entry无效--清除有效位、被覆盖位和逻辑扇区。*/
block_word = 0;
status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1);
/* Check for an error from flash driver. Drivers should never return an error.. */
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
}
else
{/*增加映射的物理扇区的数量。 */
nor_flash -> lx_nor_flash_mapped_physical_sectors++;
}
/*减少使用的扇区数。*/
used_sectors--;
}
else
{/* 此闪存块中不再有已使用的扇区。 */
/*在这种情况下,Entry必须是空闲的,否则就会有严重的NOR闪存格式错误存在。*/
if (block_word != LX_NOR_PHYSICAL_SECTOR_FREE)
{/* Increment the sector not free diagnostic. */
nor_flash -> lx_nor_flash_diagnostic_sector_not_free++;
/* NOR flash format. */
_lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT);
/*将 0s 写入此Entry以使扇区Entry无效。*/
block_word = 0;
status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1);
/* Check for an error from flash driver. Drivers should never return an error.. */
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
}
#ifdef LX_FREE_SECTOR_DATA_VERIFY
/* Pickup address of the free sector data area. */
sector_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_block_physical_sector_offset) + (j * LX_NOR_SECTOR_SIZE);
/* Determine if the data for this sector is free. */
for (k = 0; k < LX_NOR_SECTOR_SIZE; k++)
{
#ifdef LX_DIRECT_READ
/* Read the word directly. */
sector_word = *(sector_word_ptr);
#else
status = _lx_nor_flash_driver_read((sector_word_ptr), §or_word, 1);
/* Check for an error from flash driver. Drivers should never return an error.. */
if (status)
{/* Call system error handler. */
_lx_nor_flash_system_error(nor_flash, status);
/* Return an error. */
return(LX_ERROR);
}
#endif
/* Determine if this word is not available. */
if (sector_word != LX_NOR_PHYSICAL_SECTOR_FREE)
{/* Increment the sector data not free diagnostic. */
nor_flash -> lx_nor_flash_diagnostic_sector_data_not_free++;
/* This is a format error. */
_lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK);
/* Return an error. */
return(LX_ERROR);
}
/* Move to the next word in the sector. */
sector_word_ptr++;
}
#endif
}
}
这一部分的内容比较长,看起来太累了,一环套一环,没有一个逻辑图来的方便,废了半天画了一个逻辑图,怎么排版也不好看,放上来也难以看懂,反而不如代码来的清晰一下了,遂不放上来了。
这部分代码里面注释的也比较清晰了,主要就是检测各个扇区的使用情况,淘汰并统计过时、无效扇区使用新扇区进行替代等,做到一个空闲、使用、过时扇区的统计以及异常修复。
在整个块校验统计结束时,通过
/* 更新整体最小和最大擦除次数。 */
nor_flash -> lx_nor_flash_minimum_erase_count = min_erased_count;
nor_flash -> lx_nor_flash_maximum_erase_count = max_erased_count;
/*确定是否需要更新空闲扇区搜索指针*/
if (nor_flash -> lx_nor_flash_free_block_search == nor_flash -> lx_nor_flash_total_blocks)
{/* 就从头开始吧。 */
nor_flash -> lx_nor_flash_free_block_search = 0;
}
这部分代码得到当前闪存的第一个空闲的扇区索引 flash_free_block_search
,这样有利与下次的扇区的分配,后面的 _lx_nor_flash_logical_sector_find 、_lx_nor_flash_block_reclaim、_lx_nor_flash_physical_sector_allocate、_lx_nor_flash_next_block_to_erase_find
等等需要进行扇区分配的都需要它,flash_free_block_search 与flash_found_block_search、flash_found_sector_search 的调度也是LevelX做到均衡磨损的关键过程。
/* 启用扇区映射缓存。 */
nor_flash -> lx_nor_flash_sector_mapping_cache_enabled = LX_TRUE;
/*初始化最后找到的块和扇区标记。*/
nor_flash -> lx_nor_flash_found_block_search = 0;
nor_flash -> lx_nor_flash_found_sector_search = 0;
/*锁定中断。 */
TX_DISABLE
/* 至此,NOR闪存已成功打开。 将NOR闪存控制块放置在当前打开的NOR闪存的链接列表上。 */
/* 将NOR闪存状态设置为打开。*/
nor_flash -> lx_nor_flash_state = LX_NOR_FLASH_OPENED;
/*将NOR闪存控制块放在打开的NOR闪存列表上。 首先,检查一个空列表。*/
if (_lx_nor_flash_opened_count)
{
/* 列表不为空-其他NOR闪存已打开。 拾取尾部指针。*/
tail_ptr = _lx_nor_flash_opened_ptr -> lx_nor_flash_open_previous;
/* 将新的NOR闪存控制块放置在列表中。*/
_lx_nor_flash_opened_ptr -> lx_nor_flash_open_previous = nor_flash;
tail_ptr -> lx_nor_flash_open_next = nor_flash;
/*设置此NOR闪存的打开的链接。*/
nor_flash -> lx_nor_flash_open_previous = tail_ptr;
nor_flash -> lx_nor_flash_open_next = _lx_nor_flash_opened_ptr;
}
else
{
/*打开的NOR闪存列表为空。 将NOR闪存添加到空列表。 */
_lx_nor_flash_opened_ptr = nor_flash;
nor_flash -> lx_nor_flash_open_next = nor_flash;
nor_flash -> lx_nor_flash_open_previous = nor_flash;
}
/*增加打开的NOR闪存计数器。 */
_lx_nor_flash_opened_count++;
/*恢复中断。*/
TX_RESTORE
在open流程最后,初始化最后找到的块和扇区标记,用户后续查找,并将open成功的该NOR闪存的实例nor_flash插入到LevelX管理的闪存控制链表中,这样可以做到LevelX挂载多个闪存分别做到均衡磨损。
整体的open流程不是很难,但是很烦,只要理解了LevelX的BlockHeader信息,可以顺着代码很容易的明白LevelX的open逻辑,不过套娃似的逻辑阅读着实吃力啊。真希望微软能够出个类似LevelX的软件设计报告或实现流程的文档,也不用扣代码来得到了。
下章分析_lx_nor_flash_sector_write
函数实现流程,一步步揭开LevelX实现均衡磨损思想。