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

  • Thank you, this helped me diagnose the problem. When I comment the lines I was using to write to the flash, the program is recompiled and when Segger flashes the program to the chip, the UICR is erased and flashed with default values. Is there any way I can avoid overwriting the UICR every time the device is reprogrammed?

  • The UICR page should not be deleted by default with the SDK example project files. Can you explain in detail how you program the device? Do you for instance use "Erase All" before you program? If so, the UICR page will also be deleted.

  • Although this does not answer the original question,
    In the code provided by Einar,
    I believe the line

    if (*addr != 0xFFFFFFFF)

    should actually be

    if ( (*addr & word) != word )

              { /* UICR has bits cleared that word needs set. Erase is required */

         ....

              }
                   

    This is a problem with nrfjprog as reported in

    nrfjprog --memwr-0x10001080 --val 0x01010101 shouldn't fail second-time

    As a test, to assist with diagnosing,  you tried using nrfjprog?

    Is it necessary to restore NVM to read state using 

    NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);

  • Hi All,

    I am experiencing the same problem, meaning I write to the UICR but when I flash the firmware again using SES or nRF Programmer the content of the UICR is also erased. Can you please help?

    If rmarques has resolved his problem it would be good if the solution is shared on this platform.

    Thank you.

    Kind regards
    Mohamed
Reply Children
  • Hi Mohamed,

    The UICR page is only erased when you do a full chip erase (eraseall) / recover. Do you by any chance use access port protection (APPROTECT) / readback protection? If so, a recover is needed, and the programmer will do it for you (it should ask by default, though). Is that what is happening here here?

  • Hi Thorsrud,

    Yes, you hit the nail on the head!

    That is exactly what I am doing at every boot-up to check if I am running a release or a debug version of the code.

    void system_SetDebugPortAccess(void)
    {
    #if (CONFIG_DEBUG_PORT_ACCESS)
    
        /* Prevent chip from locking itself in hardware (if not already set) */
        if (NRF_UICR->APPROTECT != UICR_APPROTECT_PALL_HwDisabled)
        {
            LOG_ERR("Device debug prevented on next POR (0x%08X), resetting to disable this.", NRF_UICR->APPROTECT);
            system_WriteDebugPortAccess(UICR_APPROTECT_PALL_HwDisabled);
        }
    
        /* Prevent chip from locking itself in software */
        NRF_APPROTECT->DISABLE = APPROTECT_DISABLE_DISABLE_SwDisable;
    
    #else
        /* Enforce HW protection (if not already set) */
        if (NRF_UICR->APPROTECT != UICR_APPROTECT_PALL_Enabled)
        {
            LOG_ERR("Device debug allowed on next POR (0x%08X), resetting to disable this.", NRF_UICR->APPROTECT);
            system_WriteDebugPortAccess(UICR_APPROTECT_PALL_Enabled);
        }
    
        /* Belts and braces, enforce SW protection */
        NRF_APPROTECT->FORCEPROTECT = APPROTECT_FORCEPROTECT_FORCEPROTECT_Force;
    
    #endif //CONFIG_DEBUG_PORT_ACCESS
    }
    

    Any suggestion how to avoid UICR register being erased?

    Kind regards
    Mohamed
  • My original problem was not resolved, every time I programmed a device the UICR was reset to its original values, could be a nrfjprog or segger issue. My team just stopped trying to store config data on the UICR and just stored it in mesh config entry...

    Also, we could not erase the UICR on runtime, could be our mistake or not, but if your project already uses the MeshSDK, just use mesh config entry to store your data, it's a no-brainer, all of these issues disappeared and mesh config entry is not that difficult to learn, setup and use.

  • Thank you rmarques.

    We are not using MeshSDK and I know nothing about it. 

    Using UICR seems like a very simple way of achieving what we want (if it works!). I am going to persevere with UICR a little longer before I go to plan B.

    Kind regards
    Mohamed
  • Hi Mohamed,

    Generally, when Access port protection is enabled, the only way to recover is to do a full chip erase (ERASE ALL), and that will erase the UICR. So the only way to avoid this is to ensure that access port protection is not enabled in the first place.

    The code snippet you have here works with the latest revision of the ICs, which improved APPROTECT. Can you first double check which IC revision you are using so that you know this code matches the revision you have? Assuming it does and there are no bugs in this code (I might overlook something and I dont know your system_WriteDebugPortAccess() function), you will always have to do a recover / erase all if your CONFIG_DEBUG_PORT_ACCESS is false/0.

Related