ERASEUICR not erasing UICR

I have a problem erasing data written to the NRF_UICR->CUSTOMER registers.

I want to store the hardware version and serial number in these registers, but the serial number may change once in the lifetime of the product. Thus I would like to be able to update it.

The function I've written works on the NRF52832 chip, but doesn't seem to work on the NRF52840.

The infocenter says that when the erase bit in the confic register is set, I could erase all customer registers.

But I cannot get this to work on the NRF52840.

I've also disabled the protection registers using;

NRF_APPROTECT->DISABLE = APPROTECT_DISABLE_DISABLE_SwDisable;

But that doesn't seem to help.

Any help would be appreciated.

The function in question;

                uint8_t total_flash_size = ROUND_UP(HARDWARE_VERSION_SZ + HARDWARE_KEYWORD_SZ + SERIAL_NUMBER_SZ + SERIAL_KEYWORD_SZ, 4);
                uint32_t flash_copy_array[(total_flash_size / 4)];
                serial_number_t serial_number;
                uint32_t *pversion_flash = HARDWARE_VERSION_PTR; //Hardware version is first so use that as origin
               
                //Get data from flash
                memcpy((uint8_t*)flash_copy_array, (uint8_t*)pversion_flash, total_flash_size);
               
                //Put new data in temp stuct
                memcpy(serial_number.u.data, data, SERIAL_NUMBER_SZ);
                serial_number.keyword = SERIAL_NUMBER_KEYWORD;
//
                //HardwareVersionPointer is the first address, so this is used to calculate the offset for the array
                memcpy(&((uint8_t*)flash_copy_array)[SERIAL_NUMBER_PTR - HARDWARE_VERSION_PTR], (uint8_t*)&serial_number, SERIAL_NUMBER_SZ + SERIAL_KEYWORD_SZ);
               
                __disable_irq();
                //Configure for erase
                NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
                while(NRF_NVMC->READY == NVMC_READY_READY_Busy){;}
                NRF_NVMC->ERASEUICR = NVMC_ERASEUICR_ERASEUICR_Erase << NVMC_ERASEUICR_ERASEUICR_Pos;

                //Wait for erase to complete
                while(NRF_NVMC->READY == NVMC_READY_READY_Busy){;}

                //Configure for write
                NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
                while(NRF_NVMC->READY == NVMC_READY_READY_Busy){;}

                //Write data to flash
                for(int i = 0; i < (total_flash_size / 4); i++)
                {
                    while(NRF_NVMC->READYNEXT == NVMC_READY_READY_Busy){;}//NRF52840 has this register, so use it. Maybe it will help? nop
                    pversion_flash[i] = flash_copy_array[i];
                }
                //memcpy(pversion_flash, flash_copy_array, sizeof(flash_copy_array));
                while(NRF_NVMC->READY == NVMC_READY_READY_Busy){;}

                //Undo configuration
                NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
                while(NRF_NVMC->READY == NVMC_READY_READY_Busy){;}
                __enable_irq();

                SendCMD_Data(kAck, (uint8_t*)paddr, MEMORY_ADDRESS_SIZE);
                //System reset is needed for the new UICR configuration to take effect
                k_msleep(200);
                NVIC_SystemReset();
Parents Reply Children
  • In that case:

    Could you explain how you see that it does not work?

    What does nrfx_nvmc_uicr_erase() return?

  • I have written a few debug lines, such that upon startup the first customer register is read and send to my serial terminal. Then that value from the customer register is incremented by one and inserted as the first uint32 of a dummy serial number, then given to the write function to be written.

    The write function will perform a NVIC_SystemReset() to update the UICR configuration, this will then reset the nrf and begin the cycle anew.

    The result in the nrf52832 is a nicely incrementing output in the terminal on every boot.

    But in the nrf52840 the message in the terminal is always the same; 0x00, 0x00, 0x00, 0x00 followed by the rest of the dummy serial.

    As for the output of the nrfx_nvmc_uicr_erase(), it was 0x0BAD0000.

  • nrfx_nvmc_uicr_erase should actually not be able to return the value you mentioned (at least not the one in the nRF5 SDK version 17). It always returns NRFX_SUCCESS.

    Anyway, I have a similar problem (which is why I stumbled upon this), but apparently only with a specific chip revision. I'm calling nrfx_nvmc_uicr_erase, but when I check NRF_UICR->CUSTOMER directly afterwards, it's not 0xFFFFFFFF (I can only "debug via buzzer", since I have ready products w/o debug connectors on my desk. Would need to do some tinkering to get the Chip revision out).

    Edit: NRF_FICR->INFO.VARIANT is 0x41414430 / AAD0

  • Sort of nice to hear that I'm not the only one with this problem.

    My NRF_FICR->INFO.VARIANT is 0x41414630 / AAF0. But the datasheet lists AAF0 as 0x41414530, which is weird as 0x45 is an 'E'.

    However, I've looked up the possible return values of the nrfx_nvmc_uicr_erase() function (e.g. NRFX_SUCCESS)  and those are actually 0x0BAD0000 + the error indicator.

    This is in nrfx_errors.h;

    #define NRFX_ERROR_BASE_NUM         0x0BAD0000
    typedef enum {
        NRFX_SUCCESS                    = (NRFX_ERROR_BASE_NUM + 0),  ///< Operation performed successfully.
        NRFX_ERROR_INTERNAL             = (NRFX_ERROR_BASE_NUM + 1),  ///< Internal error.
        NRFX_ERROR_NO_MEM               = (NRFX_ERROR_BASE_NUM + 2),  ///< No memory for operation.
        NRFX_ERROR_NOT_SUPPORTED        = (NRFX_ERROR_BASE_NUM + 3),  ///< Not supported.
        NRFX_ERROR_INVALID_PARAM        = (NRFX_ERROR_BASE_NUM + 4),  ///< Invalid parameter.
        NRFX_ERROR_INVALID_STATE        = (NRFX_ERROR_BASE_NUM + 5),  ///< Invalid state, operation disallowed in this state.
        NRFX_ERROR_INVALID_LENGTH       = (NRFX_ERROR_BASE_NUM + 6),  ///< Invalid length.
        NRFX_ERROR_TIMEOUT              = (NRFX_ERROR_BASE_NUM + 7),  ///< Operation timed out.
        NRFX_ERROR_FORBIDDEN            = (NRFX_ERROR_BASE_NUM + 8),  ///< Operation is forbidden.
        NRFX_ERROR_NULL                 = (NRFX_ERROR_BASE_NUM + 9),  ///< Null pointer.
        NRFX_ERROR_INVALID_ADDR         = (NRFX_ERROR_BASE_NUM + 10), ///< Bad memory address.
        NRFX_ERROR_BUSY                 = (NRFX_ERROR_BASE_NUM + 11), ///< Busy.
        NRFX_ERROR_ALREADY_INITIALIZED  = (NRFX_ERROR_BASE_NUM + 12), ///< Module already initialized.

        NRFX_ERROR_DRV_TWI_ERR_OVERRUN  = (NRFX_ERROR_DRIVERS_BASE_NUM + 0), ///< TWI error: Overrun.
        NRFX_ERROR_DRV_TWI_ERR_ANACK    = (NRFX_ERROR_DRIVERS_BASE_NUM + 1), ///< TWI error: Address not acknowledged.
        NRFX_ERROR_DRV_TWI_ERR_DNACK    = (NRFX_ERROR_DRIVERS_BASE_NUM + 2)  ///< TWI error: Data not acknowledged.
    } nrfx_err_t;
  • We had an fix to approtect a while ago, so approtect behaves differently for different nRF52840 revisions. See  Working with the nRF52 Series' improved APPROTECT for a list.

    We can see the difference between the old and new at Enabling access port protection mechanism:

    As you can see, The new one (Hardware and software) is automatically enabled on reset.

    You can not erase UICR with approtect enabled.

    You say that you have disabled approtect in the post here, but I suspect you might have missed part of it. Did you verify that approtect was indeed disabled by flashing the device without --recover?

    DaRu said:
    However, I've looked up the possible return values of the nrfx_nvmc_uicr_erase() function (e.g. NRFX_SUCCESS)

    I checked, and looks like this function will return NRFX_SUCCESS if approtect is locked, so that unfortunately does not give us the info we are looking for.

Related