Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

GPREGRET register causing data corruption when set during custom use-case

Hi all. I am using nRF5 SDK 15.0.0 and the S140 SoftDevice v6.0.0 to develop on an nRF52840. Due to software infrastructure constraints, I am unable to implement DFU using Nordic-provided tools. Instead I rely on a custom dual-bank update process that involves storing a binary blob in a specific region in flash (0x70000 - 0xE0000). Once the blob transfer is completed, I reboot and use the GPREGRET register to identify a pending DFU in the custom bootloader after a software reset. I don't have any direct Nordic DFU functionality in the bootloader main code, and it conducts no flash operations before exiting and starting the main application. Despite this, rebooting with the GPREGRET register set to 0xB1 causes memory corruption in my payload region where the binary blob is stored. I've tried the following to confirm that GPREGRET setting is where the problem arises (checked with nrfjprog memrd):

  • Binary transfer: No corruption after entering main app
  • Binary transfer + software reset: No corruption after entering main app
  • Binary transfer + setting GPREGRET + manual power cycle (to zero out retained values): No corruption after entering main app
  • Binary transfer + setting GPREGRET + software reset: Corrupted binary blob/payload data

With this new finding, I have a handful of questions that I'm concerned about because I need to ensure my custom OTA DFU process is as error-free as possible:

  • What does setting GPREGRET trigger after a reboot, aside from holding the values that were set?
  • How can I prevent the flash region where my binary blob is stored from being modified, unless done so directly in custom code via the bootloader main?
  • Are there any other registers I can use to pass a flag to the bootloader without triggering any SDK functionality?
  • So .. I tested this, albeit on an nRF52833 'cos I couldn't be bothered to switch to an nRF52840. Data sheet indicates identical performance, soft reset retains registers .. after 100 soft resets (no errata, power on, no debugger attached):

     Results: mResetCount = 100, SRAM_OkCount = 100, GPREGRET_OkCount = 100, GPREGRET2_OkCount = 100

    // Place following data in section .noinit so it doesn't get wiped on a reset
    #pragma default_variable_attributes = @ ".noinit"
      // Valid data indication for this data
      static volatile uint32_t mIamInitialized    __attribute__((section(".noinit")));
      static volatile uint32_t mDummyGPREGRET     __attribute__((section(".noinit")));
      static volatile uint32_t GPREGRET_OkCount   __attribute__((section(".noinit")));
      static volatile uint32_t GPREGRET2_OkCount  __attribute__((section(".noinit")));
      static volatile uint32_t SRAM_OkCount       __attribute__((section(".noinit")));
      static volatile uint32_t mResetCount        __attribute__((section(".noinit")));
    #pragma default_variable_attributes =
    // End - Place following data in section .noinit so it doesn't get wiped on a reset
    
    #define MAGIC_8BIT_CODE1  96
    #define MAGIC_8BIT_CODE2  23
    #define MAGIC_8BIT_CODE3 102
      void TestGPREGRET(void)
      {
         char InfoPacket[120] = "";
         mResetCount++;
         if (mDummyGPREGRET == MAGIC_8BIT_CODE1)
         {
            SRAM_OkCount++;
         }
         if (NRF_POWER->GPREGRET == MAGIC_8BIT_CODE2)
         {
            GPREGRET_OkCount++;
         }
         if (NRF_POWER->GPREGRET2 == MAGIC_8BIT_CODE3)
         {
            GPREGRET2_OkCount++;
         }
         // See if data is valid - Use unique device ID as the valid signature
         if (mIamInitialized != NRF_FICR->DEVICEID[0])
         {
            // Indicated data is now valid - Use unique device ID as the valid signature
            mIamInitialized = NRF_FICR->DEVICEID[0];
            mResetCount       = 0;
            SRAM_OkCount      = 0;
            GPREGRET_OkCount  = 0;
            GPREGRET2_OkCount = 0;
            mDummyGPREGRET       = MAGIC_8BIT_CODE1;
            NRF_POWER->GPREGRET  = MAGIC_8BIT_CODE2;
            NRF_POWER->GPREGRET2 = MAGIC_8BIT_CODE3;
         }
         if (mResetCount < 100)
         {
            // Trigger another reset
            NVIC_SystemReset();
         }
         // break after all the reset tests, log results
         // Results: mResetCount = 100, SRAM_OkCount = 100, GPREGRET_OkCount = 100, GPREGRET2_OkCount = 100
         snprintf(InfoPacket, sizeof(InfoPacket)-1, "Results: mResetCount = %u, SRAM_OkCount = %u, GPREGRET_OkCount = %u, GPREGRET2_OkCount = %u\r\n", mResetCount, SRAM_OkCount, GPREGRET_OkCount, GPREGRET2_OkCount);
         uartSend(InfoPacket, strlen(InfoPacket));
         while(1) ;
      }
    

  • Hello,

    The GPREGRET registers are meant to be retained through soft resets, as shown in the table here: Reset behavior. We also use these registers in our SDK examples to enable the application to signal the bootloader to enter DFU mode following a soft reset.

    What does setting GPREGRET trigger after a reboot, aside from holding the values that were set?

    These are just general-purpose retention registers and will not have any effect unless the application or bootloader is actually using them. The SoftDevice/MBR does not use them. 

    How can I prevent the flash region where my binary blob is stored from being modified, unless done so directly in custom code via the bootloader main

    I'm not sure what could be corrupting the data. What kind of corruption are you seeing? Is there any pattern, or does it seem arbitrary? You can use nrfjprog to inspect the memory.

    $ nrfjprog --memrd 0x70000 --n 0x70000 > dfu_slot.txt

    Are there any other registers I can use to pass a flag to the bootloader without triggering any SDK functionality?

    I'm not sure what SDK functionality it could be since you are using a custom bootloader. If there is, you should be able to find references to it by searching through your bootloader and application project for 'gpregret'.

    Best regards,

    Vidar

  • Thank you for confirming that the GPREGRET register should not affect flash on reboot with my custom bootloader. I tested again today and analyzed the binary blob at each step of the process, and it is indeed unrelated to GPREGRET. I hadn't been giving enough time for the fstorage init and store calls to process, so I would get occasional corruption. I tested the 4 trials mentioned in my first post a handful more times, and the error rate between them is completely random. After gating fstorage calls behind a flag set by the callback handler, I am now able to safely transfer the blob without error.

    I'm concerned about this current transfer method, so I will be exploring alternative options for OTA DFU. Thank you again for the information you provided.

Related