This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How to apply Nordic software workarounds/Errata for a given hardware revision in the field

Greetings!

This is about the errata published by Nordic, i.e., software workarounds from Nordic for a given hardware revision.

Example: nRF52840 Errata

https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev2/ERR/nRF52840/Rev2/latest/err_840_new.html 

ID    Module   Description

[20]  RTC:     Register values are invalid

This anomaly applies to IC Rev. Revision 2, build codes CKAA-Dx0, QIAA-Dx0.

In ID [20], there is a software workaround recommended by Nordic. To apply this workaround for our nRF52840 devices in the field, we need to know the build codes QIAA-Dx0 either in the Bill of Materials (BOM) or in the Non-Volatile Memory (NVM) register such as INFO.VARIANT in the chip.

The problem:

  • BOM only captures the order code: nRF52840-QIAA-R.
  • It is not clear which NVM register captures the QIAA-Dx0 in the nRF52840 Product Specification v1.1.
  • Based on my understanding, QIAA-Dx0, seems only to be available in a factory environment and the hardware/software engineers have no information about this unless we look at what is printed on the chip. So, how to DFU these Nordic Software Errata/Workaround?

Questions:

  1. Typically, the software workarounds are available in the SDK. When it is not in the SDK, are these patches available in an easy to integrate way of importing or compiling these patches? For example, are the workarounds available module-wise in a .c file for a given QIAA-Dx0?
  2. Is there any NVM register that I could look up QIAA-Dx0 to apply the patch? 
  3. Currently, there appears to be no way for a developer to know the hardware revision number (like QIAA-Dx0) being used in the factory. Is there any Nordic recommended way of patching our devices in the field for such hardware revisions? Especially when DFU or Firmware Over the Air is performed. 

Kindly let me know your thoughts on this each of the above bullets. Looking forward to your response.

Cheers,
Tilak

  • Hi,

    Errata's are typically implemented in the SDK, Softdevice, and MDK. You will have to refer to the release notes for a specific release to find if the errata's are implemented already, or if you need to take specific precautions in your own code. To determine what hardware revision you are on, you may get a way to do so by looking at the MDK files, e.g. in the system_nrf52.c you can see they are reading magic internal registers to find if the errata's should be implemented. In your case you may just re-use the same code in your project (for instance see errata_36()). In general the first if() statement in the errata_36() refer to the nRF52-variant (e.g. nRF52832 or nRF52840) and the nested if() statements refer to various hardware revisions of the variant.

    Unfortunately there is no public documented register you can read.

    Best regards,
    Kenneth

  • Hi Kenneth,

    I thought I understood your response but after looking at the code I am a little confused. Please help clarifying the following:

    1. The errata_36() appears very different in nRF52832 and nRF52840. I was expecting the code to remain the same. Hence, please explain as there is no documentation.
      1. Which if() magic register maps to nRF52832 and also for nRF52840.
      2. Which if() maps to which build code of the hardware revision (CIAA-Ex0, QFAA-Ex0, QFAB-Ex0, CIAA-EA0, QFAA-EA0) for nRF52832 and (CKAA-Dx0, QIAA-Dx0) for nRF52840
      3. I am curious to know why is there a unique file system_nrf52840.c and not a generic one like system_nrf52.c for nRF52840.

    Setup: nRF5_SDK_15.3

    /* blinky_pca10056.emProject, nRF52840:  system_nrf52840.c */
    static bool errata_36(void)
    {
        if (*(uint32_t *)0x10000130ul == 0x8ul){
            if (*(uint32_t *)0x10000134ul == 0x0ul){
                return true;
            }
            if (*(uint32_t *)0x10000134ul == 0x1ul){
                return true;
            }
            if (*(uint32_t *)0x10000134ul == 0x2ul){
                return true;
            }
            if (*(uint32_t *)0x10000134ul == 0x3ul){
                return true;
            }
        }
    
        /* Apply by default for unknown devices until errata is confirmed fixed. */
        return true;
    }
    
    /* blinky_pca10040.emProject, nRF52832:  system_nrf52.c */
    static bool errata_36(void)
    {
        if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x6) && (((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0)){
            if (((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x30){
                return true;
            }
            if (((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x40){
                return true;
            }
            if (((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x50){
                return true;
            }
        }
    
        return false;
    }
    

    This is really critical for us to apply the patches based the hardware version otherwise the errata document makes no sense as we cannot find the hardware version.

    Cheers,
    Tilak

    ps: If possible, please respond bullet wise to the corresponding question.

  • Hi,

    The nRF52832 was the first chip in the nRF52-series, so the chip specific file was named 'system_nrf52.c' (retrospective a better name would be system_nrf52832.c since it only used for the nRF52832 variant). It is not a generic file, it may only return true for any of the if statements if run on the nRF52832, e.g.:

    if (*(uint32_t *)0x10000130ul == 0x8ul) return true: nRF52840 identified

    if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x6) && (((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0)) return true: nRF52832 identified

    The nested if statements check the hardware revision of the nRF52-variant, e.g:

    if (*(uint32_t *)0x10000134ul == 0x3ul) will on the nRF52840 return true for engineering D and rev 2 given if (*(uint32_t *)0x10000130ul == 0x8ul) return true.

    if (((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x50) will on the nRF52832 return true for engineering C, and rev 1 and rev 2 given if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x6) && (((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0)) return true.

    You can find the relationship between the hardware revision and build code from:

    https://infocenter.nordicsemi.com/topic/comp_matrix_nrf52840/COMP/nrf52840/nRF52840_ic_revision_overview.html?cp=4_0_3_0

    https://infocenter.nordicsemi.com/topic/comp_matrix_nrf52832/COMP/nrf52832/ic_revision_overview.html?cp=4_2_2_0 

    Best regards,
    Kenneth

  • I had the same issue understanding the different variants, so wrote code for the various parts like this:

    // nRF52832
    // ========
    // Link https://infocenter.nordicsemi.com/topic/comp_matrix_nrf52832/COMP/nrf52832/nrf52832_comp_matrix.html
    const NRF52Description_t mNRF52832Description[] = {
    //  Code    Rev     Variant Build   Pkg Flash  RAM   Handler
    //  ======  ======  ======= ===== ===== ===== =====  ======
      {"AAAA",  ENG_A,  "QFAA",  "AA0 QFN48 512kB 64kB", NULL}, //
      {"AAAC",  ENG_A,  "QFAA",  "AC0 QFN48 512kB 64kB", NULL}, //
      {"AABA",  ENG_B,  "QFAA",  "BA0 QFN48 512kB 64kB", NULL}, //
      {"AAAA",  ENG_B,  "CHAA",  "AA0 WLCSP 512kB 64kB", NULL}, //
      {"AABB",  ENG_C,  "QFAA",  "BB0 QFN48 512kB 64kB", NULL}, //
      {"AABA",  ENG_C,  "CHAA",  "BA0 WLCSP 512kB 64kB", NULL}, //
      {"AABA",  ENG_C,  "CIAA",  "BA0 WLCSP 512kB 64kB", NULL}, //
      {"AABx",  REV_1,  "QFAA",  "Bx0 QFN48 512kB 64kB", mNRF52832_Anomalies}, //
      {"ABBx",  REV_1,  "QFAB",  "Bx0 QFN48 256kB 32kB", mNRF52832_Anomalies}, //
      {"AABx",  REV_1,  "CIAA",  "Bx0 WLCSP 512kB 64kB", mNRF52832_Anomalies}, //
      {"AAEx",  REV_2,  "QFAA",  "Ex0 QFN48 512kB 64kB", mNRF52832_Anomalies}, //
      {"ABEx",  REV_2,  "QFAB",  "Ex0 QFN48 256kB 32kB", mNRF52832_Anomalies}, //
      {"AAEx",  REV_2,  "CIAA",  "Ex0 WLCSP 512kB 64kB", mNRF52832_Anomalies}  // Testing
    //  ^^^^               ^^     ^^
    //  ||||               ||     || Note first column "Code" is a combination of "Varient" and "Build"
    //  |||+---------------||-----|+
    //  ||+----------------||-----+
    //  ||                 ||
    //  |+-----------------|+
    //  +------------------+
     };
    #define NUMBER_OF_NRF52832_MODELS ( sizeof(mNRF52832Description)/sizeof(mNRF52832Description[0]) )
    const uint16_t NumberNRF52832_Models = NUMBER_OF_NRF52832_MODELS;

    From this it becomes clear how to construct the specific part number to understand which errata require applying. I only apply errata form modules which are actually used. "x" is don't care. Construct the id with something like this:

       char ch, VarientStr[4+1] = "----";
       if (isprint(ch = (NRF_FICR->INFO.VARIANT>> 0 & 0xFF))) VarientStr[3] = ch;
       if (isprint(ch = (NRF_FICR->INFO.VARIANT>> 8 & 0xFF))) VarientStr[2] = ch;
       if (isprint(ch = (NRF_FICR->INFO.VARIANT>>16 & 0xFF))) VarientStr[1] = ch;
       if (isprint(ch = (NRF_FICR->INFO.VARIANT>>24 & 0xFF))) VarientStr[0] = ch;
       snprintf(Settings, sizeof(Settings), "Part nRF%X", NRF_FICR->INFO.PART);

    Checking for a match something like this:

       // Check code for match unless target char in LS byte is 'x' (which means Don't care)
       for (i=NumberOfModels-1; i>0; i--)
       {
          if (pDescription[i].Code[0] != VarientStr[0]) continue;
          if (pDescription[i].Code[1] != VarientStr[1]) continue;
          if (pDescription[i].Code[2] != VarientStr[2]) continue;
          if (pDescription[i].Code[3] != VarientStr[3] && pDescription[i].Code[3]!='x') continue;
          IdMatchFound = true;
          break;
       }

    nRF522840:

    // nRF52840
    // ========
    // Link https://infocenter.nordicsemi.com/topic/comp_matrix_nrf52840/COMP/nrf52840/nRF52840_ic_revision_overview.html
    const NRF52Description_t mNRF52840Description[] = {
    //  Code    Rev     Variant Build   Pkg Flash  RAM   Handler
    //  ======  ======  ======= ===== ===== ===== =====  ======
      {"AAAA",  ENG_A,  "QIAA",  "AA0 aQFN73 1MB 256kB", NULL}, //
      {"AABB",  ENG_B,  "QIAA",  "BB0 aQFN73 1MB 256kB", NULL}, //
      {"AAAA",  ENG_B,  "CKAA",  "AA0 WLCSP  1MB 256kB", NULL}, //
      {"AACA",  ENG_C,  "QIAA",  "CA0 aQFN73 1MB 256kB", NULL}, //
      {"AACA",  ENG_C,  "CKAA",  "CA0 WLCSP  1MB 256kB", NULL}, //
      {"AACx",  REV_1,  "QIAA",  "Cx0 aQFN73 1MB 256kB", mNRF52840_Anomalies}, //
      {"AACx",  REV_1,  "CKAA",  "Cx0 WLCSP  1MB 256kB", mNRF52840_Anomalies}, //
      {"AADA",  ENG_D,  "QIAA",  "DA0 aQFN73 1MB 256kB", NULL}, //
      {"AADA",  ENG_D,  "CKAA",  "DA0 WLCSP  1MB 256kB", NULL}, //
      {"AADx",  REV_2,  "QIAA",  "Dx0 aQFN73 1MB 256kB", mNRF52840_Anomalies}, //
      {"AADx",  REV_2,  "CKAA",  "Dx0 WLCSP  1MB 256kB", mNRF52840_Anomalies}  //
    //  ^^^^               ^^     ^^
    //  ||||               ||     || Note first column "Code" is a combination of "Varient" and "Build"
    //  |||+---------------||-----|+
    //  ||+----------------||-----+
    //  ||                 ||
    //  |+-----------------|+
    //  +------------------+
     };
    #define NUMBER_OF_NRF52840_MODELS ( sizeof(mNRF52840Description)/sizeof(mNRF52840Description[0]) )
    const uint16_t NumberNRF52840_Models = NUMBER_OF_NRF52840_MODELS;

  • Thank you  for the elegant code :) I will get back to you if I run into trouble. Thanks.

Related