This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

s110 sd_flash_write works in debugger when stepping line by line, fails otherwise.

I have the following code to write 0x34 bytes to flash

void saveKeysToFlash(ble_gap_enc_info_t* keys,
    unsigned char **saveDataBuffer, unsigned short int *saveDataLength,
    unsigned char* cccdSet, unsigned short* noOfCccds)
{
    int i;
    unsigned char *keysDataBuffer = NULL;
    const char utech[8] = {'U', 'T', 'E', 'C', 'H', '_', 'P', 'O'};
    uint32_t err_code;

    uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
    uint32_t pg_num  = NRF_FICR->CODESIZE - 1;  // Use last page in flash
    uint32_t *addr;

    int size = 8 +                                      // space for key "UTECH_PO"
               sizeof(ble_gap_enc_info_t) +             // encryption info
               sizeof(unsigned short) +                 // number of cccds
               sizeof(unsigned char) * (*noOfCccds) +   // the cccds
               sizeof(unsigned short) +                 // length of save data buffer
               ((*saveDataBuffer != NULL) ? *saveDataLength * sizeof(unsigned char) : 0);   // the save data buffer
    // The writes are done in 4-byte hunks so we have to even out the length
    size = 4 + ((size >> 2) << 2);
    keysDataBuffer = calloc(1, size);
    
    // Now load all the data we want to save into this buffer
    uint8_t *ptr = keysDataBuffer;
    memcpy(ptr, utech, 8);                                      // Load identifier
    ptr = ptr + 8;
    memcpy(ptr, keys, sizeof(ble_gap_enc_info_t));              // load our encryption info
    ptr = ptr + sizeof(ble_gap_enc_info_t);
    memcpy(ptr, noOfCccds, sizeof(unsigned short));             // Load number of CCCDs
    ptr = ptr + sizeof(unsigned short);
    memcpy(ptr, cccdSet, sizeof(unsigned char) * (*noOfCccds)); // Load the cccdSet[]
    ptr = ptr + sizeof(unsigned char) * (*noOfCccds);
    memcpy(ptr, saveDataLength, sizeof(unsigned short));        // Load saveDataLength
    ptr = ptr + sizeof(unsigned short);
    if (*saveDataBuffer != NULL)
    {
        memcpy(ptr, saveDataBuffer, *saveDataLength * sizeof(unsigned char));  // Load the saveDataBuffer
    }
    // Now we have to write the data in hunks into flash
    // Each page is 1024 bytes, and a write is in 4-byte hunks
    // So we will find the number of 1024 byte pages to write, and then the number of 4-byte hunks left over.

    // Buffer to write to flash. Need to write it in four-byte hunks
    uint32_t *ptr32 = (uint32_t *)keysDataBuffer;
    while (true)
    {
        // Where to write
        addr = (uint32_t *)(pg_size * pg_num);
        // Erase page:
        while(true)
        {
            err_code = sd_flash_page_erase(pg_num);
            if (err_code == NRF_SUCCESS)
            {
                break;
            }
            if (err_code != NRF_ERROR_BUSY)
            {
                APP_ERROR_CHECK(err_code);
               // nrf_delay_ms(200);
            }
        }
       // nrf_delay_ms(200);
        i = (size >= pg_size) ? (pg_size >> 2) : (size >> 2);     // four-byte hunks to write; size is evenly divisible by four
        while(true)
        {
            err_code = sd_flash_write(addr, ptr32, i);
            if (err_code == NRF_SUCCESS)
            {
                break;
            }
            if (err_code != NRF_ERROR_BUSY)
            {
                APP_ERROR_CHECK(err_code);
               // nrf_delay_ms(200);
            }
        }
        size = size - pg_size;  // Subtract a page size from the total size
        if (size <= 0)          // if zero or less, all data has been written
        {
            break;
        }
        pg_num++;
        ptr32 = (uint32_t *)(keysDataBuffer + pg_size);
    }
    uint8_t *test = (uint8_t *)(pg_size * pg_num) + 8;
    ble_gap_enc_info_t testKeys;
    memcpy(&testKeys, test, sizeof(ble_gap_enc_info_t));
    free(keysDataBuffer);

    if(testKeys.ltk_len == keys->ltk_len)
    {
        if (testKeys.div == keys->div)
        {
            if (testKeys.auth == keys->auth)
            {
                if (testKeys.ltk[3] == keys->ltk[3])
                {
                    int i = 9;
                    return;
                }
            }
        }
    }
    
}

If I place a breakpoint in the debugger at the start of the erase/write code and step through it, the flash write works. If, on the other hand, I set a breakpoint at the end of the write loop, the flash write fails. The test loop at the end shows all values are just FF's or whatever it was. They are all the same.

I tried implementing delays but the Keil compiler gives an error on the nrf_delay.h file so I couldn't do that. THe question is what am I missing such that the flash write works when stepping through using the debugger, but fails when I 'run' though the code? Note that in both cases, the debugger is being run. It does fail when used normally (without using the debugger).

I have tried all kinds of combinations, using while loops on sd_evt_get() and looking for the NRF_EVT_FLASH_OPERATION_SUCCESS etc and they all work if one goes step by step in the debugger and they all fail if one lets them run 'freely' only hitting a break point after the writes are completed.

I invoke the write after the device disconnects from the peer. So there should be no radio competition here. What is going on? -  nrf51822 apparently ancient s110 ver 5/6.

Even tried calling it from a timer - same result.

I believe the flash write is broken. The previous version of this code used the bonding manager and the pstorage - and it had the same issue - failed encryption every time on a reconnect. The bonding manager is so complex I gave up trying to debug it. But if the basic sd_flash_write is broken it is understandable that the pstorage would be broken. (The bonding manager used flash storage to save the LTKs and other data one needs to support bonding - without LTKs, encryption will fail)

  • Have in mind that after calling a flash operation (e.g. sd_flash_write() or sd_flash_erase()), then you need for the NRF_EVT_FLASH_OPERATION_SUCCESS or  NRF_EVT_FLASH_OPERATION_ERROR event before you can call next flash operation or before you can read back data that may have been written to flash.

    I believe the flash is rated to minimum 20.000 flash operations for every page, which should be sufficient for an end product, but if you have used the nRF51822 for a long period of time during prototype development, then maybe you have had some test firmware that have been running flash operations in a loop (e.g. flash write->code assert->code restart...)

    Kenneth

  • I have tried that variation as I stated above - waiting for the NRF_EVT_FLASH_OPERATION_SUCCESS by making the call to write the flash, and then calling the sd_ method to poll for events.

    The results are the same. It works if I step through the writes line by line in the debugger. But if I let it run and put a break point after the writes, it fails. It also fails if not run in the debugger.

    If I take the line-by-line approach such that it succeeds, on a reconnect encryption actually works! So it is ths flash write that is blocking the completion of the implementation. Everything else works.

    In later SoftDevices one can disable SoftDevice before attempting the write. Since BTLE is done by this time, that is fine. Is the 'busy' loop the proper approach in this case?

Related