levelx icon indicating copy to clipboard operation
levelx copied to clipboard

_lx_nor_flash_logical_sector_find costs a lot of time, making LevelX writes quite slow

Open HaoboGu opened this issue 2 years ago • 4 comments

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;
    }

HaoboGu avatar Aug 03 '23 08:08 HaoboGu

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 avatar Dec 01 '23 01:12 xiuwencai

@xiuwencai thanks, I'll try it

HaoboGu avatar Dec 01 '23 02:12 HaoboGu

@xiuwencai a quick question: when should I call lx_nor_flash_extended_cache_enable? before or after lx_nor_flash_open?

HaoboGu avatar Dec 07 '23 14:12 HaoboGu

Sorry for late reply. Call lx_nor_flash_extended_cache_enable after lx_nor_flash_open.

xiuwencai avatar Dec 22 '23 01:12 xiuwencai