Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
HaoboGu opened this issue Aug 3, 2023 · 4 comments
Assignees
Labels
bug Something isn't working

Comments

@HaoboGu
Copy link

HaoboGu commented Aug 3, 2023

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 HaoboGu added the bug Something isn't working label Aug 3, 2023
@xiuwencai xiuwencai self-assigned this Dec 1, 2023
@xiuwencai
Copy link
Contributor

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.

@HaoboGu
Copy link
Author

HaoboGu commented Dec 1, 2023

@xiuwencai thanks, I'll try it

@HaoboGu
Copy link
Author

HaoboGu commented Dec 7, 2023

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

@xiuwencai
Copy link
Contributor

Sorry for late reply. Call lx_nor_flash_extended_cache_enable after lx_nor_flash_open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants