Flash memory error : FDS_ERR_NO_PAGES

Hi DevZone !

I am using the nRF52840 for development and after some tests one of the chips could not access its memory flash in the function fds_init() in the library fds.c. I debugged it with Ozone and I saw the error FDS_ERR_NO_PAGES popping up. Can I repair the flash in some way ? Any suggestion on why it happened ?

Thank you !

Joel V.

  • Joel V said:
    I at first tried to use fds_record_update instead of delete/write but I dont recall it worked so I did this instead. I don'T know if it could cause such problem I don't think so.

    fds_record_write() will write a new record, while fds_record_update() will invalidate the old record, and then write a new record. Then, the garbage collector will free up the invalidated record when you call it. 

    rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
    if(rc == NRF_SUCCESS){
        //rc = fds_record_update(&desc, &config_record);
        rc = fds_record_delete(&desc);
        APP_ERROR_CHECK(rc);
        rc = fds_record_write(&desc, &config_record);
        APP_ERROR_CHECK(rc);
        if(rc != NRF_SUCCESS)
        {
            rc = fds_gc();
            APP_ERROR_CHECK(rc);
        }
        APP_ERROR_CHECK(rc);
    }

    The fds module is an async module, which means that you need to wait for the appropriate event to be generated in the callback handler for the process to complete. You should wait for the delete process to complete before calling the write function with the same descriptor, alternatively just call fds_record_update() which will do both.

    You should also call the garbage collector at the start of main()

    Joel V said:
    I however have a funtion to overwrite the flash when I want to make a modification right away but I usually comment it after dumping the program and the dump again. I am 90% certain that I did that but this is the code portion that does that:

    And you're erasing the flash between these calls?

    regards

    Jared

  • And you're erasing the flash between these calls?

    I use fds_record_delete, is this not erasing the flash page ? I should erase the flash between each update?

    Is there a way to recover the processor which its flash is full ?

  • Hi,

    Sorry for the poor explanation. Let me try again Slight smile

    The FDS module is an asynchronous module which means that operations doesn't complete when the function call is returned, a return with nRF_SUCESS only means that the operation was added successfully to the queue. You have to wait for the callback handler to be called with the appropriate event to be sure that the operation has completed. Updating or deleting a record does not free up the physical memory. It only invalidates the record. The physical memory is only freed once the garbage collector is called and frees up all of the invalidated memory. You therefore have to call the garbage collector before the memory is full.

    Assuming these two parts are the only FDS operations that you normally do in your application:

    1. Here you initialize the FDS module, run the garbage collector and read the record. Which is fine. You might want to wait for the garbage collector operation to complete first, but I don't think that it's crucial. 

    rc = fds_init();
    rc = fds_gc();
    APP_ERROR_CHECK(rc);
    
    
    fds_record_desc_t desc = {0};
    fds_find_token_t  tok  = {0};
    
    rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
    if (rc == NRF_SUCCESS){
        //rc = fds_record_delete(&desc);
        //APP_ERROR_CHECK(rc);
        fds_flash_record_t fds_config = {0};
    
        //Open the record and read its contents. 
        rc = fds_record_open(&desc, &fds_config);
        APP_ERROR_CHECK(rc);
    
        //Copy the configuration from flash into config.
        memcpy(&config, fds_config.p_data, sizeof(config_t));
    
        //Close the record when done reading.
        rc = fds_record_close(&desc);
        APP_ERROR_CHECK(rc); 

    2. Here you write a new record to the FDS. If it fails you run the garbage collector.

     fds_record_t config_record ={
                .file_id           = CONFIG_FILE,
                .key               = CONFIG_REC_KEY,
                .data.p_data       = &config,
                /* The length of a record is always expressed in 4-byte units (words). */
                .data.length_words = (sizeof(config_t)+3)/sizeof(uint32_t),
            };
            
    rc = fds_record_write(&desc, &config_record);
    if(rc != NRF_SUCCESS){
        rc = fds_gc();
        APP_ERROR_CHECK(rc);
        config_init(mesh_config);
    }
    APP_ERROR_CHECK(rc);

    Re-doing step 1 and 2 multiple times for example by reset the application will eventually fill up the flash. This is because you never invalidate a record by either deleting or updating a record, which means that the garbage collector will not do anything. Reset the application will effectively write a new record with the same record and same file ID to the FDS without deleting the old one, since FDS does not require uniqueness for file ID nor file record key. I would suggest that you try to use the fds_record_update() instead, which will invalidate the old record when you write a new record with the same record ID and record key. The garbage collector will then free up the memory when it's called. Note that you need to wait for the operation to be completed as stated earlier. 

    best regards

    Jared 

  • Hi Jared,

    I'm sorry but I understood correctly I was just searching for a solution to recover my processor which is not working because of the flash being full. I managed to do so with the nrfjprog.  :) 

    As for the step 1 and 2 mentionned here :

    Re-doing step 1 and 2 multiple times for example by reset the application will eventually fill up the flash.

    Step 1 will be done if the config page is already written (sucess when fds_record_find) 

    Step 2 will be done if the config page is not already written (fail when fds_record_find). In other words, this part of the function is never called at boot but when I just bought my nRF52840 and I first program it.

    So they wil never be done at same time and the Step 1 is usually done. 

    I changed the delete/write for an fds_record_update in my other function and it worked fine, i dont know why it didnt the first time. That should solve the problem if this function is not commented. 

    About this:

    You should also call the garbage collector at the start of main()

    One of your collegues already told me not to do so in a private ticket and I quote:

    « We highly discourage garbage collection on restart, because it will then do GC unnecessarily »

    Which one should I do ?

    Finally to solve the issues about the async module, what do you suggest as indicator to wait for before the fds_gc ?

  • Joel V said:

    I'm sorry but I understood correctly I was just searching for a solution to recover my processor which is not working because of the flash being full. I managed to do so with the nrfjprog.  :) 

    Aha, so you're were asking for how to erase the flash with nrfjprog --erase? 

    Joel V said:

    Step 1 will be done if the config page is already written (sucess when fds_record_find) 

    Step 2 will be done if the config page is not already written (fail when fds_record_find). In other words, this part of the function is never called at boot but when I just bought my nRF52840 and I first program it.

    So they wil never be done at same time and the Step 1 is usually done. 

    I changed the delete/write for an fds_record_update in my other function and it worked fine, i dont know why it didnt the first time. That should solve the problem if this function is not commented.

    I see, so you'll only write to the FDS once after programming the IC? How many resets before you get FDS_ERR_NO_PAGES from fds_init()? 

    I'm suspecting if you're getting alot of corrupted data that is caused by resetting the chip before the write operation has been completed. Also, if you're only writing to the chip if there is no found record then a fds_record_write() and fds_record_update() will do effectively do the same. 

    Joel V said:

    One of your collegues already told me not to do so in a private ticket and I quote:

    « We highly discourage garbage collection on restart, because it will then do GC unnecessarily »

    Which one should I do ?

    I read Terjes response to your other ticket and I do agree with him on a second thought. My understanding of your application was different then how I understand it now. 

    Joel V said:
    Finally to solve the issues about the async module, what do you suggest as indicator to wait for before the fds_gc ?

    FDS_EVT_GC will be generated by the callback handler. You can set a flag in the callback handler which you can wait for with a while(flag) {}  loop. 

    regards

    Jared 

Related