Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

RAM retention failure

Hello

I am trying to figure out the ram retention on my nRF52-DK using SES and SDK v17.1.0. I've run through the example ram_retention, and studied a topic in Nordic's documentation, as well as a couple of threads here in DevZone, like this one, and this one. I adapted my code to implement RAM retention using the given examples, but it didn't work for me. 

I did all the mentioned steps:

  • declared the variable I want to retain in the .non_init section ( static uint32_t ble_activate __attribute__((section(".non_init"))); )
  • turned the RAM powered on during the SystemOFF sleepmode:
    // Configure the RAM retention parameters
    uint8_t i;
    for(i = 0; i < 8; i++) // Retain RAM 0 - RAM 7
    {
        sd_power_ram_power_set(i, (POWER_RAM_POWER_S0POWER_On << POWER_RAM_POWER_S0POWER_Pos) |
                                  (POWER_RAM_POWER_S1POWER_On << POWER_RAM_POWER_S1POWER_Pos) |
                                  (POWER_RAM_POWER_S0RETENTION_On << POWER_RAM_POWER_S0RETENTION_Pos) |
                                  (POWER_RAM_POWER_S1RETENTION_On << POWER_RAM_POWER_S1RETENTION_Pos));
    }
    // Delay for one second 
    nrf_delay_ms(1000);
  • And checking up the NRF_POWER->GPREGRET  for a pre-set value upon every wakeup.

However, I see that value is not preserved since I am outputting it using printf and it returns back as a nonsense number. Please, find my main.c attached. The crucial steps are implemented in void mers_ram_retention(void). The code is loosely based on ble_app_uart example, that is, in configures BLE stack as per original method and then starts advertising, and finally, goes to SystemOFF. 

/**
 * Copyright (c) 2014 - 2021, 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:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, 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.
 *
 * 3. 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.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
 *
 */

#include <stdint.h>
#include <string.h>
#include "app_timer.h"
#include "bsp_btn_ble.h"
#include "nrf_pwr_mgmt.h"

#if defined (UART_PRESENT)
#include "nrf_uart.h"
#endif
#if defined (UARTE_PRESENT)
#include "nrf_uarte.h"
#endif

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "nrf_delay.h"

#include "MERS_BLE.h"
#include "MERS_LoRaWAN.h"
#include "MERS_fstorage.h"


/* Project global variables */
extern uint8_t key_cnt;
extern bool keys_received;
extern bool ble_connected;
static uint32_t ble_activate __attribute__((section(".non_init")));


/**@brief Function for initializing the timer module.
 */
static void timers_init(void){
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);
}



/**@brief Function for handling events from the BSP module.
 *
 * @param[in]   event   Event generated by button press.
 */
void bsp_event_handler(bsp_event_t event){
    uint32_t err_code;
    switch (event)
    {
        case BSP_EVENT_SLEEP:
            sleep_mode_enter();
            break;

        case BSP_EVENT_DISCONNECT:
            //err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            //if (err_code != NRF_ERROR_INVALID_STATE)
            //{
            //    APP_ERROR_CHECK(err_code);
            //}
            break;

        case BSP_EVENT_WHITELIST_OFF:
            //if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
            //{
            //    err_code = ble_advertising_restart_without_whitelist(&m_advertising);
            //    if (err_code != NRF_ERROR_INVALID_STATE)
            //    {
            //        APP_ERROR_CHECK(err_code);
            //    }
            //}
            break;

        default:
            break;
    }
}

/**@brief   Function for handling app_uart events.
 *
 * @details This function will receive a single character from the app_uart module and append it to
 *          a string. The string will be be sent over BLE when the last character received was a
 *          'new line' '\n' (hex 0x0A) or if the string has reached the maximum data length.
 */
/**@snippet [Handling the data received over UART] */
void uart_event_handle(app_uart_evt_t * p_event){
    static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
    static uint8_t index = 0;
    uint32_t       err_code;

    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:
            UNUSED_VARIABLE(app_uart_get(&data_array[index]));
            index++;

            //if ((data_array[index - 1] == '\n') ||
            //    (data_array[index - 1] == '\r') ||
            //    (index >= m_ble_nus_max_data_len))
            //{
            //    if (index > 1)
            //    {
            //        NRF_LOG_DEBUG("Ready to send data over BLE NUS");
            //        NRF_LOG_HEXDUMP_DEBUG(data_array, index);

            //        do
            //        {
            //            uint16_t length = (uint16_t)index;
            //            err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
            //            if ((err_code != NRF_ERROR_INVALID_STATE) &&
            //                (err_code != NRF_ERROR_RESOURCES) &&
            //                (err_code != NRF_ERROR_NOT_FOUND))
            //            {
            //                APP_ERROR_CHECK(err_code);
            //            }
            //        } while (err_code == NRF_ERROR_RESOURCES);
            //    }

            //    index = 0;
            //}
            break;

        case APP_UART_COMMUNICATION_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        default:
            break;
    }
}
/**@snippet [Handling the data received over UART] */



/**@brief  Function for initializing the UART module.
 */
/**@snippet [UART Initialization] */
static void uart_init(void){
    uint32_t                     err_code;
    app_uart_comm_params_t const comm_params =
    {
        .rx_pin_no    = RX_PIN_NUMBER,
        .tx_pin_no    = TX_PIN_NUMBER,
        .rts_pin_no   = 0,
        .cts_pin_no   = 0,
        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity   = false,
#if defined (UART_PRESENT)
        .baud_rate    = NRF_UART_BAUDRATE_115200
#else
        .baud_rate    = NRF_UARTE_BAUDRATE_115200
#endif
    };

    APP_UART_FIFO_INIT(&comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_event_handle,
                       APP_IRQ_PRIORITY_LOWEST,
                       err_code);
    APP_ERROR_CHECK(err_code);
}
/**@snippet [UART Initialization] */



/**@brief Function for initializing buttons and leds.
 *
 * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to wake the application up.
 */
static void buttons_leds_init(bool * p_erase_bonds){
    bsp_event_t startup_event;

     uint32_t err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
     APP_ERROR_CHECK(err_code);

     err_code = bsp_btn_ble_init(NULL, &startup_event);
     APP_ERROR_CHECK(err_code);

    *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}


/**@brief Function for initializing the nrf log module.
 */
static void log_init(void){
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}


/**@brief Function for initializing power management.
 */
static void power_management_init(void){
    ret_code_t err_code;
    err_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling the idle state (main loop).
 *
 * @details If there is no pending log operation, then sleep until next the next event occurs.
 */
static void idle_state_handle(void){
    if (NRF_LOG_PROCESS() == false)
    {
        nrf_pwr_mgmt_run();
    }
}


/**@brief A function to fetch LoRaWAN keys
*/
mers_ble_keys_t mers_ble_obtain_keys(void){
    /* Check if the LoRa keys have been received and store them in the Flash */
    if (keys_received){
        NRF_LOG_INFO("Received 4 keys.");
        NRF_LOG_FLUSH();
        mers_store_lora_keys();
        ble_activate = false;
        /* Keys are received, we can disconnect and disable BLE */
        //mers_ble_disconnect();       // <--- Leave the disconnect decision to the Smartphone 
        ble_connected = false;
        return MERS_KEYS_RECEIVED_OK;
    }
    else
        return MERS_KEYS_RECEIVED_NOT_OK;
}


void mers_ram_retention(void){
    // Check if chip is waking up after a SoftReset.
    if(NRF_POWER->GPREGRET == 0xBB){  
        printf("\n\rble_activate value: %d", ble_activate);
        // Reset GPREGRET register
        // NRF_POWER->GPREGRET = 0x00;        
    }
    else {
        ble_activate = 0x01UL;            
        // Store magic number in GPREGRET register to indicate that the content should be checked upon reset. 
        NRF_POWER->GPREGRET = 0xBB;
    }

    // Configure the RAM retention parameters
    uint8_t i;
    for(i = 0; i < 8; i++) // Retain RAM 0 - RAM 7
    {
        sd_power_ram_power_set(i, (POWER_RAM_POWER_S0POWER_On << POWER_RAM_POWER_S0POWER_Pos) |
                                  (POWER_RAM_POWER_S1POWER_On << POWER_RAM_POWER_S1POWER_Pos) |
                                  (POWER_RAM_POWER_S0RETENTION_On << POWER_RAM_POWER_S0RETENTION_Pos) |
                                  (POWER_RAM_POWER_S1RETENTION_On << POWER_RAM_POWER_S1RETENTION_Pos));
    }
    // Delay for one second 
    nrf_delay_ms(1000);
}


/**@brief Application main function.
 */
int main(void){

    bool erase_bonds;

    // Initialize.
    uart_init();
    log_init();
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();
    fstorage_init();

    // Start execution.
    printf("\n\r*********************************");
    printf("\n\r       MarineEye Remote Sensor"   );
    printf("\n\r          Pero, 2022"             );
    printf("\n\r*********************************");
    /*
        Add Git version printout 
    */
    NRF_LOG_INFO("Debug logging for MERS over RTT started.");
    NRF_LOG_FLUSH();
    
    mers_ram_retention();
    printf("\n\rble_activate value: %d", ble_activate);
    printf("\n\rkeys_received value: %d", keys_received);

    /* Activate the BLE radio and start advertising */
    if (ble_activate){
        mers_ble_init();
        printf("\n\rBLE Advertising started.\n\r");
    }
    else{
        printf("\n\rBLE not activated.");
    }
    
    ///* Check for the keys in the Flash */
    //if (mers_read_lora_keys() != LORA_KEYS_IN_FLASH_OK){   

    //      ble_activate = true;
    //      /* Activate BLE radio and get the keys */
    //      mers_ble_obtain_keys();      
    //}
    //else{
    //    NRF_LOG_INFO("\n\rLoRaWAN Keys found.\n\rBLE radio not started.");
    //    /*
    //    Do the job with Lora keys
    //    */
    //}

    //NRF_LOG_INFO("\n\rDevice going to sleep");
    //NRF_LOG_FLUSH();
    uint32_t dummy_cntr = 0;

    while(1){

        if (ble_connected){
            if(mers_ble_obtain_keys() == MERS_KEYS_RECEIVED_OK){
                printf("\n\rLoRaWAN Keys received.");
                sleep_mode_enter();
            }
            ble_activate = 0UL;
        }
        else{
            dummy_cntr++;
            if ((dummy_cntr % 40) == 0)
                printf("\n\rble_activate value: %d", ble_activate);
        }
        
        idle_state_handle();
    }
}


/**
 * @}
 */

Could you please tell me what went wrong?

Best, 

W

Parents
  • Hello,

    The RAM configuration looks to be correct. However, when the noinit section gets corrupted it usually is because there is a bootloader overwriting the section on startup. Could that be the case here? If not, I will try to set up a test tomorrow and see if I can replicate the issue on my side.

    Best regards,

    Vidar

  • Hello Vidor, thanks for the reply. I'm not using the bootloader, as far as I know. I have started from ble_app_uart and built upon it, never including any bootloader. 

    Hey, if it helps, I have uploaded you a complete project -  I've stripped down all functionality except ble-uart and ram retention to demonstrate the issue. It should run on PCA10040:

    MERS.zip

    Best, P

  • Hey Vidar, this has worked out! Thanks man, you're awesome!

    I've taken your approach with NRF_POWER->RAM[i].POWERSET functions, since I turn SoftDevice on, only after the RAM retention is set, and only if the variable retained in RAM checks out. Is that a right way to do things here? Check the variable in RAM, and turn the BLE depending on it?

    Second, I feel it's kinda waste of power to turn on all of the RAM regions, since my 4-byte variable resides only in one of them. Can you tell me how to find out where my variable actually is, and how to pick a proper RAM region accordingly?

    Best, 

    P

  • Excellent Slight smile

    Is that a right way to do things here? Check the variable in RAM, and turn the BLE depending on it?

    Yes, this will work.

    Can you tell me how to find out where my variable actually is, and how to pick a proper RAM region accordingly?

    You can see where the .non_init section is located in RAM from the memory usage view in SES here (or from the *.map output):

    And when you know the address, you can look up the which RAM slave and section it belongs to by referring to figure 1 in the Memory chapter of the PS. Each 4K section consumes about 30 nA when retention is enabled.

    Important: the .non_init section does not start at a fixed address. So it may change between builds. E.g. when you turn off or enable compiler optimization.

  • You can see where the .non_init section is located in RAM from the memory usage view in SES here (or from the *.map output):

    Can you believe, I did exaxtly that last night? I found out that my variable lives at 0x20003F00, i.e in the section RAM 1. When I turned off all sections except that one, it worked just fine! How cool is that?

    Important: the .non_init section does not start at a fixed address. So it may change between builds. E.g. when you turn off or enable compiler optimization.

    This is what I also noticed. Is there a way to fix a variable at the given section and make sure it stays there every time?

  • Yes, you can specify a fixed start address for the non_init section in your flash_placemecement.xml file.

    e.g.

    Note that this fixed address must be somewhere between .tbss and   .heap (or .stack if you don't use heap).

  • Cool. One last question: in short words, how does the compiler (linker?) determine the addresses of all those sections? I always though it was pregiven in the .ld file (though I never bothered to look it up there)

Reply Children
Related