Example code to write to UICR

I am trying to store data to the UICR using SDK16, mesh SDK 4.2.0 and the nRF52840 chip.

I want to write to the UICR once on runtime and never again. To avoid messing with the softdevice, I am trying to write to the UICR before calling ble_stack_init() and after __LOG_INIT to get logs working.

This is the code:

NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}

uint32_t * consumer0 = (uint32_t *) 0x10001080;
*consumer0 = 0x00000005;

NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
__LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "%d\n", *consumer0);

NVIC_SystemReset();

So we put the NVMC in Write enable, wait for it to be ready, write to consumer0, put it in Read enable, wait for it to be ready and reset the device. I can see 5 logged to Segger console.

Then I comment what I don't need:

//NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
//while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}

uint32_t * consumer0 = (uint32_t *) 0x10001080;
//*consumer0 = 0x00000005;

//NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
//while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
__LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "%d\n", *consumer0);

//NVIC_SystemReset();

And I see -1 logged to Segger console, meaning the flash still has the 0xFFFF on that position. I can confirm it on Segger Memory explorer that 10001080 is never filled with the data I wanted. Am I doing something wrong?

Parents
  • Beware that sometimes Flash memory is cached by Segger and does not exactly show what you expect. I refer explicitly to 

    exec SetAllowFlashCache =0   

    Have you tried exiting Segger and starting it again to see if flash isn really set?

    In addition add a check in your coded to see what the value is.

    Finally choose your values wisely since if there PREVIOUS value of customer[0] was FFFFFFFF this will work BUT if the previous value was 05000000 it will not since write to flash can only reset bits. It is necessary to erase flash to set it to FFFFFFF

  • I tried exiting Segger and rechecking the memory, all was the same. I also added a small check for looking for the change in the flash like so:

    //NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
    //while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
    
    uint32_t * consumer0 = (uint32_t *) 0x10001080;
    //*consumer0 = 0x00000005;
    if(*consumer0 == 5){
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "*consumer0 = 5\n");
    }
    else {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Flash not correct, *consumer0 = %d\n", *consumer0);
    }
    
    //NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
    //while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
    
    //NVIC_SystemReset();

    As soon as I comment the lines commented in the code I get the second __LOG: Flash not correct, *consumer0 = -1

  • Hi,

    I see nothing in the code that would explain this, the way you write to UICR seems correct (and is the same as what you can see in gpio_output_voltage_setup() in components\boards\boards.c).

    Perhaps the issue is with how you test instead? When you flash the new firmware that should read the UICR, perhaps you erase the UICR during that operation? Can you explain how you program the second time?

    You could also avoid the whole issue using something like this (not tested, may be typos) so that you don't re-flash the firmware (potentially erasing UICR in the process):

    // Input address and wrod to write to that address.
    //Output:
    // - True if target word is written
    // - False if a different value is allraedy written to register (it is not correct or all FF's)
    // - Does not return if write has been performed (in that case a soft reset is triggered)
    bool write_word_to_uicr(uint32_t * addr, uint32_t word)
    {
        if (*addr == word)
        {
            // Allready set. Nothing more to do.
            return true;
        }
        else if (*addr != 0xFFFFFFFF)
        {
            // The register allready has another value. Aborting.
            // (Note: could improve by checking if only bits that should be flipped to 0 has been,
            // if so it would still be possible to write the target word.)
            return false;
        }
        else
        {
            // Register has default value. Ready to write...
            NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
            while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
    
            *addr = word;
    
            NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
            while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
    
            NVIC_SystemReset();
        }
    }

    Then you could call the above function like this:

    write_word_to_uicr((uint32_t *)0x10001080, 0x00000005);

Reply
  • Hi,

    I see nothing in the code that would explain this, the way you write to UICR seems correct (and is the same as what you can see in gpio_output_voltage_setup() in components\boards\boards.c).

    Perhaps the issue is with how you test instead? When you flash the new firmware that should read the UICR, perhaps you erase the UICR during that operation? Can you explain how you program the second time?

    You could also avoid the whole issue using something like this (not tested, may be typos) so that you don't re-flash the firmware (potentially erasing UICR in the process):

    // Input address and wrod to write to that address.
    //Output:
    // - True if target word is written
    // - False if a different value is allraedy written to register (it is not correct or all FF's)
    // - Does not return if write has been performed (in that case a soft reset is triggered)
    bool write_word_to_uicr(uint32_t * addr, uint32_t word)
    {
        if (*addr == word)
        {
            // Allready set. Nothing more to do.
            return true;
        }
        else if (*addr != 0xFFFFFFFF)
        {
            // The register allready has another value. Aborting.
            // (Note: could improve by checking if only bits that should be flipped to 0 has been,
            // if so it would still be possible to write the target word.)
            return false;
        }
        else
        {
            // Register has default value. Ready to write...
            NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
            while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
    
            *addr = word;
    
            NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
            while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
    
            NVIC_SystemReset();
        }
    }

    Then you could call the above function like this:

    write_word_to_uicr((uint32_t *)0x10001080, 0x00000005);

Children
Related