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.

Parents Reply Children
  • Hi, 

    I am not. My application is only reading the flash at boot if the flash is already written:

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

    And it is writing flash if it is a brand new processor:

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

    I also have a function that overwrites the flash if I want to, but this function is usually commented and I am 90% certain that it was but here is the code portion:

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

    I tried using fds_record_update in this portion but I dont recall it was working I dont know why.

    Is it possible that the flash may have been corrupted in a hardware way ? For exemple by inversing poles or I dont know ?

    Thanks,

    Joel V.

  • I don't think I am. I am only using fds library for reading configurations at boot:

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

    I am writing only once when the processor is brand new:

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

    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:

    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),
        };
    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_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);
    }

    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.

    I am however wondering if it could be possible to corrupted the memory flash in a hardware way. (i.e.: inverting poles or idk)

    Thank you very much for your answers,

    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 

Related