Nordic NRF52840 Storage Part II
This post is the continued talk about the persistent storage for Nordic nRF52840. In part I we went over using the Flash Data Storage (FDS) library. In this part II, I’ll be discussing another import component, user information configuration registers (UICR).
UICRs are non-volatile memory (NVM) registers for configuring user-specific settings, this is in contract to Factory information configuration registers (FICR), which are pre-programmed in the Nordic factory and cannot be erased by the user. Thus UICR, once written, won’t be able to be erased by DFU or regular SES flash memory operations, make it suitable for factory configuration settings, e.g. serial number. It can, however, be erased with nrfjprog --eraseall / --eraseuicr
or nrfjprog --program --chiperase / --sectoranduicrerase
. Be aware Nordic stores bootloader address in UICR too (in fact only CUSTOMER[0]-[31] are for user), an erase of UICR will cause bootloader fail.
FICR, UICR, and Mac Address
Like many other posts, I came to learn FICR/UICR by searching a unique identifier for each nRF chip. There are many other aspects of FICR and UICR, which are usually under the core components of the product specification, but will not be discussed in this post.
In short, the default 6-bytes BLE address displayed in peer is the MAC address stored in DEVICEADDR registers in FICR. It is randomly generated by the chip manufacturer (usually by Nordic) and can not be manipulated by user. It can be considered unique in most practical scenarios.
You can, however, create your own MAC address following the BT SIG specification as discussed here and here, save it to the user configuration registers UICRs, and then load and set it as the BLE address with sd_ble_gap_addr_set()
function in your application. You can go over the details of nice write-ups here and here.
UICR Usage
UICR has 32 CUSTOMER registers (each 4-bytes) of total 1024 bits for user configurations. Though they are most suitable for settings of an end product that don’t change once left the production line, you do have the option to change them in the application after disabling the SoftDevice. Here’s a discussion about the setting the register during program execution.
To write the registers in production time, you can either include the UICR values into the application hex file (see UICR Config Example), or write to the registers directly with nrfjprog
:
nrfjprog -f nrf52 --memwr 0x10001080 --val 0xA1A2A3A4
where the base address of UICR is 0x10001000, and the user reserved register CUSTOMER[x] starts at 0x080. You can write any of the 32 registers you want, but the value also has to be aligned to 32-bit word.
To read from command line tool:
nrfjprog --memrd 0x10001080 --w 32 --n 4
where --w <display in 32/16/8>
and --n <number of bytes to read>
. A hardware reset is required for it to take effect.
To read from application code:
// create a buffer
uint8_t buffer[SERIAL_NUMBER_LENGTH];
memset(buffer, 0xff, SERIAL_NUMBER_LENGTH);
// read from UICR->CUSTOMER[0]
uint8_t* p_uicr = (uint8_t*) &NRF_UICR->CUSTOMER[0]; // 0x10001080
memcpy(buffer, p_uicr, SERIAL_NUMBER_LENGTH);
where the bytes are stored in little-endian fashion in each register.
Finally, be aware the flash memory endurance is 10,000 writes/erase cycles, which applies to UICR as well.
UICR Protection
See this post for details.