Threadx LevelX NOR闪存的Sector Write
LevelX _lx_nor_flash_sector_write函数
UINT _lx_nor_flash_sector_write(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer)
此功能将逻辑扇区写入NOR闪存,其中logical_sector是用户也就是调用层需要写入的指定扇区号,buffer指向的是所要写入的内容。
我们很容易从输入的形参中看到没有指定的写入长度,也就是说明每次写入的其实都是一个LevelX规定的“虚拟扇区大小”。所以,同样要注意的就应该是这个buffer的实例需要至少拥有一个“虚拟扇区大小”的空间地址,否则极有可能写入一些非法数据。
在解析LevelX的基本函数时候,刚开始学习该代码的同学可能会对读写的一些函数有些混乱的感觉,比如:
_lx_nor_flash_sector_write:_lx_nor_flash_driver_write
_lx_nor_flash_sector_read:_lx_nor_flash_driver_read
_lx_nor_flash_sector_write是一个暴露出来给调用层使用的API,是一个调用底层的一个写操作,在融入一些算法实现写入的加速与均衡。
而_lx_nor_flash_sector_read是隐藏在内部仅LevelX可以调用的一个函数,其直接调用操作对应实例闪存的读接口,只不过其中加入了一些缓存和cache处理,实现最高效的找到那个使用概率最小的哪个缓存,达到对后期读取与查找的一个优化操作。
同理_lx_nor_flash_sector_read与_lx_nor_flash_driver_read也有这类似的调用结构与处理方法。
确定是否有少于两个Block的空闲扇区
i = 0;
while (nor_flash -> lx_nor_flash_free_physical_sectors <= nor_flash -> lx_nor_flash_physical_sectors_per_block)
{
/* 尝试回收一个物理块。 */
_lx_nor_flash_block_reclaim(nor_flash);
/* Increment the block count. */
i++;
/* 我们是否已超过系统中的块数?*/
if (i >= nor_flash -> lx_nor_flash_total_blocks)
{
/* Yes, break out of the loop. */
break;
}
}
当剩余的空闲虚拟扇区个数小于一个块中所拥有的虚拟扇区个数,则需要对过时的物理块尝试进行垃圾回收。从代码中可以看出,每次调用 _lx_nor_flash_block_reclaim()进行垃圾回收,最多尝试回收所整个物理闪存所拥有的物理块数量的次数。这个操作完全可以在别处认为调用,不过在写的时候就进行剩余空间检测可以自动的在空间不足时,一次性的回收所有垃圾块。
/* 看看是否可以在当前映射中找到该扇区。有可能这个逻辑扇区之前已经写入过数据,且未过时,*/
_lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &old_mapping_address, &old_sector_address);
/* 为本次写入分配一个新的物理扇区。无论之前有没有使用过该逻辑扇区,
想要写入都必须重新申请一个新的扇区使用(不考虑未过时的扇区追加写入的这种情况)*/
_lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
/*确定新扇区分配是否成功。*/
if (new_mapping_address)
{
/* 是的,我们能够分配一个新的物理扇区。*/
/* 更新空闲物理扇区的数量。*/
nor_flash -> lx_nor_flash_free_physical_sectors--;
/* 将扇区数据写入新的物理扇区。*/
status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, buffer, LX_NOR_SECTOR_SIZE);
/* 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);
#ifdef LX_THREAD_SAFE_ENABLE
/* Release the thread safe mutex. */
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
#endif
/* Return status. */
return(LX_ERROR);
}
/*是否有先前映射的扇区? */
if (old_mapping_address)
{
/*现在弃用旧的扇区映射。 */
/* 读取旧的扇区映射。*/
#ifdef LX_DIRECT_READ
/* Read the word directly. */
old_mapping_entry = *(old_mapping_address);
#else
status = _lx_nor_flash_driver_read(nor_flash, old_mapping_address, &old_mapping_entry, 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);
#ifdef LX_THREAD_SAFE_ENABLE
/* Release the thread safe mutex. */
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
#endif
/* Return status. */
return(LX_ERROR);
}
#endif
/*清除位30,该位指示该扇区已被取代即-过时了。 */
old_mapping_entry = old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED);
/*将值写回闪存以清除位30。*/
status = _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 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);
#ifdef LX_THREAD_SAFE_ENABLE
/* Release the thread safe mutex. */
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
#endif
/* Return status. */
return(LX_ERROR);
}
}
/* 现在建立新的映射Entry--写入逻辑扇区号、指示扇区有效、未过时、Entry写入未完成*/
new_mapping_entry = ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID) | logical_sector;
/* 写入新的映射Entry。*/
status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 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);
#ifdef LX_THREAD_SAFE_ENABLE
/* Release the thread safe mutex. */
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
#endif
/* Return status. */
return(LX_ERROR);
}
/* 现在清除无效位,以使该扇区映射有效。 这样做是因为额外字节本身的写入可能会中断,并且我们需要确保在再次打开闪存时可以检测到此错误。 清除bt29 表示Entry写入完成*/
new_mapping_entry = new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID);
/*清除无效位。*/
status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 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);
#ifdef LX_THREAD_SAFE_ENABLE
/* Release the thread safe mutex. */
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
#endif
/* Return status. */
return(LX_ERROR);
}
/*增加映射的物理扇区的数量。 */
nor_flash -> lx_nor_flash_mapped_physical_sectors++;
/* 是否有先前映射的扇区?*/
if (old_mapping_address)
{
/* 现在清除位31,该位表明该扇区已被废弃。*/
old_mapping_entry = old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID);
/* 将值写回闪存以清除第31位。 */
status = _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 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);
#ifdef LX_THREAD_SAFE_ENABLE
/* Release the thread safe mutex. */
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
#endif
/* Return status. */
return(LX_ERROR);
}
/*增加过时的物理扇区数。 */
nor_flash -> lx_nor_flash_obsolete_physical_sectors++;
/*减少映射的物理扇区数。*/
nor_flash -> lx_nor_flash_mapped_physical_sectors--;
/*使旧的扇区映射缓存Entry无效。 */
_lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector);
}
/*确定是否启用了扇区映射缓存。*/
if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
{
/*是的,已启用扇区映射缓存,请将此扇区信息放入缓存中。*/
/*计算该扇区Entry的扇区映射缓存的起始索引。*/
i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH;
/* 建立一个指向缓存Entry的指针。 */
sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i];
/* 向下移动所有缓存Entry,以便最旧的位于底部。*/
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr);
/*在缓存中设置新的扇区信息。*/
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector = (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID);
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry = new_mapping_address;
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = new_sector_address;
}
/* 表示写入成功。 */
status = LX_SUCCESS;
}
else
{
/* 表示写入失败。*/
status = LX_NO_SECTORS;
}
在拥有足够的空间时,我们需要找到形参所指定的那个“虚拟扇区”的实际物理地址,通过调用_lx_nor_flash_logical_sector_find,看看是否可以在当前映射中找到该扇区。有可能这个逻辑扇区之前已经写入过数据,且未过时。也就是说,如果该虚拟扇区中之前已经写入过数据,且没有过时,根据FLASH的特性以及我们之前每次都是写入一个虚拟扇区的字节数,那么就必须重新申请一个新的空闲扇区来存储我们想要写入的数据。在这里调用_lx_nor_flash_logical_sector_find主要就是找到之前已经使用的扇区,方便在写入新的虚拟扇区后进行旧虚拟扇区的状态设置。
上面的一部分代码很容易理解:
主要是在找到新的“虚拟扇区”地址后,将数据写入新的虚拟扇区,同时设置该虚拟扇区中的Flash Head处中的状态信息,如写入的逻辑扇区号、扇区有效且未过时、Entry尚未写入完成等。而如果之前使用过该虚拟扇区,也尚未过时,则也需要设置旧的扇区状态,以表示该扇区被“弃用”。主要是将扇区中mapping_entry的bit30、bit31清零,表示该扇区已经被取代,已经过时,而且已经被“废弃”。
下面的
/* 建立一个指向缓存Entry的指针。 */
sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i];
/* 向下移动所有缓存Entry,以便最旧的位于底部。*/
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr);
/* 在缓存中设置新的扇区信息。*/
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector = (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID);
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry = new_mapping_address;
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = new_sector_address;
主要是为了更新最新的mapping缓存地址,方便在后面的各个函数的操作提供更高的命中率。这个在后面就可以看出来其重要性。