levelx
levelx copied to clipboard
_lx_nor_flash_logical_sector_find costs a lot of time, making LevelX writes quite slow
I'm using Filex with LevelX nor flash driver. I found that when writing a lot of data, _lx_nor_flash_logical_sector_find costs a lot of time(about 50ms each write), making LevelX write quite slow (about 5kb/s).
I checked the source code of LevelX, this while block costs the most of the time. Is there anyway to improve the write speed of LevelX?
while (total_blocks--)
{
/* Setup the block word pointer to the first word of the search block. */
block_word_ptr = (nor_flash -> lx_nor_flash_base_address + (i * nor_flash -> lx_nor_flash_words_per_block));
/* Determine if the minimum and maximum logical sector values are present in the block header. If these are
present, we can quickly skip blocks that don't have our sector. */
/* Read the minimum and maximum logical sector values in this block. */
#ifdef LX_DIRECT_READ
/* Read the word directly. */
min_logical_sector = *(block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET);
#else
status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 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 the error. */
return(status);
}
#endif
#ifdef LX_DIRECT_READ
/* Read the word directly. */
max_logical_sector = *(block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET);
#else
status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 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 the error. */
return(status);
}
#endif
/* Are the values valid? */
if ((min_logical_sector != LX_ALL_ONES) && (max_logical_sector != LX_ALL_ONES))
{
/* Now let's check to see if the search sector is within this range. */
if ((logical_sector < min_logical_sector) || (logical_sector > max_logical_sector))
{
/* Move to the next block. */
i++;
/* Determine if we have wrapped. */
if (i >= nor_flash -> lx_nor_flash_total_blocks)
{
/* Yes, we have wrapped, set to block 0. */
i = 0;
}
/* Start at the first sector in the next block. */
j = 0;
/* No point in looking further into this block, just continue the loop. */
continue;
}
}
/* Setup the total number of sectors. */
total_sectors = nor_flash -> lx_nor_flash_physical_sectors_per_block;
/* Remember the start of the search. */
search_start = j;
/* Now search through the sector list to find a match. */
while (total_sectors--)
{
/* Setup a pointer to the mapped list. */
list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j;
/* Read in the mapped list for this block. */
#ifdef LX_DIRECT_READ
/* Read the word directly. */
list_word = *(list_word_ptr);
#else
status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_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 the error. */
LOGE("sector find costs: %d", tx_time_get() - s);
return(status);
}
#endif
/* Determine if the entry hasn't been used. */
if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
{
/* Since the mapping is done sequentially in the block, we know nothing
else exists after this point. */
/* Determine if the search started at the beginning of the block. */
if (search_start == 0)
{
/* Yes, we started at the beginning of the block. We are now done with this block. */
break;
}
else
{
/* Setup the new total to the search start. */
total_sectors = search_start;
/* Clear search start. */
search_start = 0;
/* Start search over. */
j = 0;
continue;
}
}
/* Is this entry valid? */
if ((list_word & (LX_NOR_PHYSICAL_SECTOR_VALID | LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID)) == LX_NOR_PHYSICAL_SECTOR_VALID)
{
/* Decrement the number of mapped sectors. */
mapped_sectors--;
/* Do we have a valid sector match? */
if ((list_word & LX_NOR_LOGICAL_SECTOR_MASK) == logical_sector)
{
/* Determine if we care about the superceded bit. */
if (superceded_check == LX_FALSE)
{
/* Prepare the return information. */
*physical_sector_map_entry = list_word_ptr;
*physical_sector_address = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE);
/* Determine if the sector mapping cache is enabled. */
if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
{
/* Yes, update the cache with the sector mapping. */
/* Move all the cache entries down so the oldest is at the bottom. */
*(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);
/* Setup the new sector information in the cache. */
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 = *physical_sector_map_entry;
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = *physical_sector_address;
}
/* Remember the last found block for next search. */
nor_flash -> lx_nor_flash_found_block_search = i;
/* Remember the last found sector. */
nor_flash -> lx_nor_flash_found_sector_search = j+1;
/* Has this wrapped around? */
if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
{
/* Reset to the beginning sector. */
nor_flash -> lx_nor_flash_found_sector_search = 0;
}
/* Return success! */
LOGI("sector find costs: %d", tx_time_get() - s);
return(LX_SUCCESS);
}
/* Check for the superceded bit being clear, which means the sector was superceded. */
else if (list_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED)
{
/* Prepare the return information. */
*physical_sector_map_entry = list_word_ptr;
*physical_sector_address = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE);
/* No need to update the cache here, since this condition only happens during initialization. */
/* Remember the last found block for next search. */
nor_flash -> lx_nor_flash_found_block_search = i;
/* Remember the last found sector. */
nor_flash -> lx_nor_flash_found_sector_search = j+1;
/* Has this wrapped around? */
if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
{
/* Reset to the beginning sector. */
nor_flash -> lx_nor_flash_found_sector_search = 0;
}
/* Return success! */
LOGI("sector find costs: %d", tx_time_get() - s);
return(LX_SUCCESS);
}
}
}
/* Move to the next list entry. */
j++;
/* Check for wrap around. */
if (j >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
{
/* Yes, wrap around, go back to the beginning. */
j = 0;
}
}
/* Determine if there are any more mapped sectors. */
if (mapped_sectors == 0)
break;
/* Move to the next block. */
i++;
/* Determine if we have wrapped. */
if (i >= nor_flash -> lx_nor_flash_total_blocks)
{
/* Yes, we have wrapped, set to block 0. */
i = 0;
}
/* Start at the first sector in the next block. */
j = 0;
}
Hi @HaoboGu, in version 6.3.0, there are two new options that may improve the write speed: LX_NOR_ENABLE_MAPPING_BITMAP and LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE. Enable these two options and provide cache memory to the LevelX by calling lx_nor_flash_extended_cache_enable. The memory size should be one bit for each sector plus one byte for each block.
@xiuwencai thanks, I'll try it
@xiuwencai a quick question: when should I call lx_nor_flash_extended_cache_enable? before or after lx_nor_flash_open?
Sorry for late reply. Call lx_nor_flash_extended_cache_enable after lx_nor_flash_open.