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缓存地址,方便在后面的各个函数的操作提供更高的命中率。这个在后面就可以看出来其重要性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值