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

nRF51 boot-loader SystemInit() function

The device we are using is nRF51822.  It uses a bootloader from the Nordic SDK 11.0.0

The boot-loader we are using for the nRF51822 has several blocks of code in the SystemInit() function that are related to Product Anomaly Notificatiions:

  • PAN 26 "System: Manual setup is required to enable the use of peripherals"
  • PAN 59 "MPU: Reset value of DISABLEINDEBUG register is incorrect"
  • PAN 76 "System: Excessive current in sleep mode with retention"

This code in SystemInit() has functions that are reading values from the following locations::

  • 0xF0000FE0
  • 0xF0000FE4
  • 0xF0000FE8
  • 0xF0000FEC

For example:

               if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x1)

These addresses are in the reserved area of the memory map.  I cannot find any registers in the nRF51 Reference Manual corresponding to these addresses.

The relevant file is: system_nrf51.c.  The contents of this file are attached below.

It appears that this code if trying to determine the type of chip or chip variant and initialize some peripheral registers.  However, even the peripheral register addresses don't all make sense.

I cannot find any Product Anomaly Notifications on the Nordic website.

We are concerned that our code is trying to access invalid memory locations.

Can you explain what is going on here? 

/* Copyright (c) 2015, Nordic Semiconductor ASA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, this
 *     list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *   * Neither the name of Nordic Semiconductor ASA nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/* NOTE: Template files (including this one) are application specific and therefore expected to
   be copied into the application project folder prior to its use! */

#include <stdint.h>
#include <stdbool.h>
#include "nrf.h"
#include "system_nrf51.h"
#include "nrf_gpio.h"


/*lint ++flb "Enter library region" */



#define __SYSTEM_CLOCK      (32000000UL)     	/* nRF51822 devices in Taiyo Yuden BLE module use System Clock Frequency 32MHz */
//#define __SYSTEM_CLOCK      (16000000UL)     /*!< nRF51 devices use a fixed System Clock Frequency of 16MHz */


static bool is_manual_peripheral_setup_needed(void);
static bool is_disabled_in_debug_needed(void);
static bool is_peripheral_domain_setup_needed(void);
void SetResetXtalFreq_32MHz(void);
void test_pin_output(void);



#if defined ( __CC_ARM )
    uint32_t SystemCoreClock __attribute__((used)) = __SYSTEM_CLOCK;
#elif defined ( __ICCARM__ )
    __root uint32_t SystemCoreClock = __SYSTEM_CLOCK;
#elif defined   ( __GNUC__ )
    uint32_t SystemCoreClock __attribute__((used)) = __SYSTEM_CLOCK;
#endif

void SystemCoreClockUpdate(void)
{
    SystemCoreClock = __SYSTEM_CLOCK;
}

void SystemInit(void)
{
	/* Configure reset XTAL frequency*/
	SetResetXtalFreq_32MHz();

	
    /* If desired, switch off the unused RAM to lower consumption by the use of RAMON register.
       It can also be done in the application main() function. */

    /* Prepare the peripherals for use as indicated by the PAN 26 "System: Manual setup is required
       to enable the use of peripherals" found at Product Anomaly document for your device found at
       https://www.nordicsemi.com/. The side effect of executing these instructions in the devices
       that do not need it is that the new peripherals in the second generation devices (LPCOMP for
       example) will not be available. */
    if (is_manual_peripheral_setup_needed())
    {
        *(uint32_t volatile *)0x40000504 = 0xC007FFDF;
        *(uint32_t volatile *)0x40006C18 = 0x00008000;
    }

    /* Disable PROTENSET registers under debug, as indicated by PAN 59 "MPU: Reset value of DISABLEINDEBUG
       register is incorrect" found at Product Anomaly document for your device found at
       https://www.nordicsemi.com/. There is no side effect of using these instruction if not needed. */
    if (is_disabled_in_debug_needed())
    {
        NRF_MPU->DISABLEINDEBUG = MPU_DISABLEINDEBUG_DISABLEINDEBUG_Disabled << MPU_DISABLEINDEBUG_DISABLEINDEBUG_Pos;
    }

    /* Execute the following code to eliminate excessive current in sleep mode with RAM retention in nRF51802 devices,
       as indicated by PAN 76 "System: Excessive current in sleep mode with retention" found at Product Anomaly document
       for your device found at https://www.nordicsemi.com/. */
    if (is_peripheral_domain_setup_needed()){
        if (*(uint32_t volatile *)0x4006EC00 != 1){
            *(uint32_t volatile *)0x4006EC00 = 0x9375;
            while (*(uint32_t volatile *)0x4006EC00 != 1){
            }
        }
        *(uint32_t volatile *)0x4006EC14 = 0xC0;
    }

	test_pin_output();						// DEBUG   Just toggle a pin	
	

}


static bool is_manual_peripheral_setup_needed(void)
{
    if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x1) && (((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0))
    {
        if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x00) && (((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0))
        {
            return true;
        }
        if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x10) && (((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0))
        {
            return true;
        }
        if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x30) && (((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0))
        {
            return true;
        }
    }

    return false;
}

static bool is_disabled_in_debug_needed(void)
{
    if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x1) && (((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0))
    {
        if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x40) && (((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0))
        {
            return true;
        }
    }

    return false;
}

static bool is_peripheral_domain_setup_needed(void)
{
    if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x1) && (((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0))
    {
        if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0xA0) && (((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0))
        {
            return true;
        }
        if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0xD0) && (((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0))
        {
            return true;
        }
    }

    return false;
}


/* Set reset XTAL frequency to 32MHz
 *
 * The body of this function will only run once.  
 * The first time the boot-loader runs, after the flash memory has been erased
 * and programmed, the UICR registers are blank unless they have been written during
 * flash programming.  This function will program the XTALFREQ register the first time
 * the boot-loader runs if it doesn't have the correct value.  After that the XTALFREQ 
 * register will have the correct value and the initial "if" statement will always be false.
 *
 * A reset is required after writing to the XTALFREQ register.  UICR registers are read on 
 * reset and changes to these registers have no effect until the next reset.
 *
 * Alternatively can write to UICR XTALFREQ register during flash programming:
 *	nrfjprog --memwr 0x10001008 --val 0xFFFFFF00
 *
 */
void SetResetXtalFreq_32MHz(void)
{

	if (NRF_UICR->XTALFREQ == 0xFFFFFFFF)
	{
		// Enable NVM write access
		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
		{ }
		
		NRF_UICR->XTALFREQ = 0xFFFFFF00;			// 32MHz
		
		// Set NVM access to read-only
		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
		{ }

		NVIC_SystemReset();					// Changes take effect after reset
	}		
}




/*lint --flb "Leave library region" */


 

  • Edvin,

    For your information we set UICR register XTALFREQ to select 32MHz XTAL.

       NRF_UICR->XTALFREQ = 0xFFFFFF00; // 32MHz

    This set in the bootloader with the following code:

    void SetResetXtalFreq_32MHz(void)
    {
    
    	if (NRF_UICR->XTALFREQ == 0xFFFFFFFF)
    	{
    		// Enable NVM write access
    		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
    		while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
    		{ }
    		
    		NRF_UICR->XTALFREQ = 0xFFFFFF00;			// 32MHz
    		
    		// Set NVM access to read-only
    		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
    		while (NRF_NVMC->READY == NVMC_READY_READY_Busy)
    		{ }
    
    		NVIC_SystemReset();					// Changes take effect after reset
    	}		
    }
    

    This code only runs once, because after XTALFREQ has been set to select the 32MHz XTAL the first if() statement is false.  This code is from Taiyo-Yuden.

    Phil Matthews

  • Hello Phil,

    All I know is from the PAN:

    So it is related to the RAM access. It looks like it only applies to the nRF51802, so it probably doesn't apply to your chip.

    Does the code ever reach this statement? If not, I don't see why we need to worry about this address. I could spend some days trying to figure out who wrote the workaround, and what it points to, but I don't really see the use case if is_peripheral_domain_setup_needed() never returns true.

     

    Phil said:
    When it fails when have found that the application doesn't run and that the boot-loader main() function doesn't get called.

     How do you determine this? Lack of advertisements, or do you have some other way of checking whether or not your application is started?

    Do you have a way to reproduce this while debugging or monitoring some sort of logs? 

  • Edvin,

    I have this documentation from the nRF51802-PAN-v1.3.  It doesn't tell me much.  Specifically it doesn't say what is at those addresses or why these values are being written to them.  Our concern is the while() loop. Potentially the code could end up here and loop forever if the value at address 0x4006EC00 does not become 0x0001.  I have found when I force this code to execute, them the nRF51 fails to run on every second Power-On Reset.

    I am still testing to determine if our code ever gets to this while(0 loop.  I have been inserting code to toggle pins P0.17 or P0.19 at various places in the bootloader.  So far it doesn't ever get to the while loop.  Also tracing through the function is_peripheral_domain_setup_needed() with the values I read from flash using JLink, this function returns false.  So maybe this is not the problem.  It would be nice to know what is going here.

    According Nordic documentation, the nRF51802 is the low-cost variant of the nRF51822.  They share the same reference manual.  As far as I can tell they have the same peripherals and memory map.

    To answer your other questions, we know bootloader doesn't jump to the application because there is no UART comms, the current consumption is high and there is no BLE advertising.  I have inserted code in the bootloader to toggle GPIO pins so I know where the code gets to and verified that when the nRF51 fails the boot-loader main() is not called.  I got to the point that the boot-loader always gets to the start of SystemInit() but not to the end.  So that's why I think it has something to so with these fix-up functions.

    I have commented out all these three blocks of PAN fix-up functions and so far the nRF51 has always started Ok.  I'm still testing this.  Is it ok to remove these three blocks of code?

    Phil Matthews

  • Edvin

    In Product Anomaly Notice nRRF51822-PAN v3.3 there is:

      PAN 78: HFCLK: Software reset does not work properly when 32MHz is used as a clock source.

    We are using a 32MHz XTAL.  The 32MHz XTAL is selected by writing to UICR register NRF_UICR->XTALFREQ = 0xFFFFFF00; // 32MHz.

    In the boot-loader I am sure the 32MHz XTAL is not running, only the 16MHz internal RC HFCLK.  Also the boot-loader doesn't issue software reset.

    However, are you aware of any problems at Power-on reset or Hardware reset when 32MHz XTAL is selected?

    Phil Matthews

  • Hello,

    I discussed a bit with a colleague of mine, and here is what we concluded with:

    You should not modify the MDK files. They are implemented on millions upon millions of devices, and there is nothing indicating that there is a bug here that is causing issues with your bootloader. So don't change anything in the system_nrf51.c. It is used to identify what variant the chip is and execute some code based on this.

    I don't know specifically what are located on the addresses that you keep asking about, but it is not relevant. We should look elsewhere for the buggy behavior on your devices.

    It looks like you have placed SetResetXtalFreq_32MHz() in the start of SystemInit(). You shouldn't do that. You can place it in the start of main(), after the mdk files (as-is) are done doing what they should do.

    For debugging purposes, you can try to toggle a gpio in the start of SystemInit() to see whether the chip is starting at all in the cases where it fails. If you suspect that there may be some layout issues, then you can upload your layout, and we can do a review.

    Another thing: When you do your power cycle tests, can you please make sure that VDD is pulled below 0.3V before you power it back on in every test? We know that an incomplete power cycle can lead to unresponsive behavior until a full power on reset is performed. 

    Another thing that you can try for debugging purposes: You can try to add a 1Mohm pull-down resistor on the SWDIO pin. It may sound a bit contraintuitive, since there is a 13kohm internal pullup, but if the issue is related to the VDD not being pulled low enough during your power cycle testing, this may have an effect.

    Some relevant information copy-pasted from another private ticket here on DevZone:

    It may be that what you are experiencing is due to a missing reset condition. In the nRF51 series, a power-on-reset (POR) event is triggered when the supply voltage is monotonically increasing from zero (0v) to a valid operating voltage (VDD) in less than 100ms. However, if the supply voltage increases from a few hundred mV instead of zero, POR cannot be ensured, and the chip can enter into an unresponsive state. 

    If the device is powered up from a voltage other than zero, in rare circumstances, the internal POR circuit may not trigger. This leads to the device entering an unresponsive state. Such a scenario may happen if, for example, capacitors connected to the power supply have not been fully discharged. 

    The unresponsive state is reset by a valid POR event, such as power cycle (<0.3V). Or an off-chip POR circuit can be added to facilitate the triggering of the POR event under these conditions. As an example of an off-chip POR circuit can be adding 1Mohm resistor between SWDIO/nRESET and GND.

    An external 1Mohm pull down on the SWDIO/nReset pin to GND will create an additional source for reset on power up. The drawback is an increased base current of about 2-3µA (VDD/1Mohm). 

    Can you try in your setup with an external 1Mohm and report back with the results?

    Best regards,

    Edvin

Related