Skip to content

NvStore does not correctly persist values through reset #8785

Closed
@NeilMacMullen

Description

@NeilMacMullen

Description

TARGET=UBLOX_EVK_NINA_B1 (nrf52)
TOOLCHAIN=GCC_ARM
MBED commit bf6f2c3 (but also present at least as far back as commit a8abecc)

The NvStore implementation does not appear to persist values through a soft-reset. It seems likely that it is mis-identifying the active page. This may be related to increasing the max_keys count above the default. The following code is self-contained apart from the implementation of DEBUGLN which is just a shim over segger_rtt-printf. You can replace it with whatever tracing mechanism you prefer.

The test first sets item #1 then resets and changes the value after also adding a new item (#27). Both items are correctly listed until the CPU is reset. After reset the old value of item #1 is seen and the new item #27 is not present. The expected behaviour is that the test should continue to the 'final' section but it continuously loops through the "second run" block.

#include "mbed.h"
#include "debugln.h"
#include "nvstore.h"

//#define CLEANBOARD
#define MAX_SETTING_VALUE_LENGTH (32)
#define MAX_KEYS 128
#define RESET NVIC_SystemReset()

//ensure strings are correctly aligned to a uint32 boundary
typedef union {
    uint32_t Numeric;
    char String[MAX_SETTING_VALUE_LENGTH];
} nvstore_item_value_t;

#define MYASSERT(OP, EXPECTED, MESSAGE)                             \
    {                                                               \
        int err = OP;                                               \
        if (err != EXPECTED)                                        \
        {                                                           \
            DEBUGLN("FAIL: Got %d but expected %d", err, EXPECTED); \
            HALT(MESSAGE);                                          \
        }                                                           \
    }

bool TryGetNvStoreItem(int id, nvstore_item_value_t &value)
{
    NVStore &nvstore = NVStore::get_instance();
    uint16_t size_read;
    value.String[0] = 0;
    int err = nvstore.get(id, sizeof(nvstore_item_value_t), value.String, size_read);
    return (err == NVSTORE_SUCCESS) && size_read;
}

void WriteNvStoreItem(int id, nvstore_item_value_t &value)
{
    NVStore &nvstore = NVStore::get_instance();
    int len = strlen(value.String) + 1;
    MYASSERT(nvstore.set(id, len, value.String), NVSTORE_SUCCESS, "Failed to write setting");
}

void WriteAndVerifyItem(int n, char *val)
{
    nvstore_item_value_t write_item;
    strcpy(write_item.String, val);
    WriteNvStoreItem(n, write_item);
    nvstore_item_value_t read_item;
    MYASSERT(TryGetNvStoreItem(n, read_item), true, "Failed to read back setting that we just wrote");
    MYASSERT(strcmp(val, read_item.String), 0, "Read different value back than written");
}

void ListNvStoreItems(char *message)
{
    DEBUGLN("%s", message);
    for (int n = 0; n < MAX_KEYS; n++)
    {
        nvstore_item_value_t read_item;
        if (TryGetNvStoreItem(n, read_item))
            DEBUGLN("%d: '%s'", n, read_item.String);
    }
}

void RESETSTORE()
{
    NVStore &nvstore = NVStore::get_instance();
    nvstore.reset();
    HALT("reset store");
}

void EnsureMaxKeysCorrect()
{
    NVStore &nvstore = NVStore::get_instance();
    MYASSERT(nvstore.init(), NVSTORE_SUCCESS, "failed to init store");
    if (nvstore.get_max_keys() != MAX_KEYS)
        nvstore.set_max_keys(MAX_KEYS);
    MYASSERT(nvstore.get_max_keys(), MAX_KEYS, "Max keys has not been correctly set");
}

int main()
{
#ifdef CLEANBOARD
    RESETSTORE();
#endif

    DEBUGLN("======== entered main after reset ========");
    EnsureMaxKeysCorrect();
    ListNvStoreItems("Items on entry....");
    nvstore_item_value_t item1;
    if (!TryGetNvStoreItem(1, item1))
    {
        //Write "01" the first time we run the program
        DEBUGLN("First run so setting #1 to 01...");
        WriteAndVerifyItem(1, "01");
    }
    else
    {
        if (!strcmp(item1.String, "01"))
        {
            //On the second run, add a new key and increment item 1 to indicate that
            //we can move on....
            DEBUGLN("second run so item #1 to 02 and adding extra item...");
            WriteAndVerifyItem(27, "abcded");
            WriteAndVerifyItem(1, "02");
        }
        else
        {
            //this should be the 3rd (and final) run but we never get here :(
            ListNvStoreItems("Final items");
            HALT("Test passed");
        }
    }
    ListNvStoreItems("Items on exit...");
    DEBUGLN("resetting in 10 seconds...");
    wait_ms(10000);
    RESET;
}

Example output....

0> ======== entered main after reset ========
0> Items on entry....
0> First run so setting #1 to 01...
0> Items on exit...
0> 1: '01'
0> resetting in 10 seconds...
0> entered main after reset ========
0> ======== entered main after reset ========
0> Items on entry....
0> 1: '01'
0> second run so item #1 to 02 and adding extra item...
0> Items on exit...
0> 1: '02'
0> 27: 'abcded'
0> resetting in 10 seconds...
0> ======== entered main after reset ========
0> Items on entry....
0> 1: '01'
0> second run so item #1 to 02 and adding extra item...
0> Items on exit...
0> 1: '02'
0> 27: 'abcded'
0> resetting in 10 seconds...
0> ======== entered main after reset ========
0> Items on entry....
0> 1: '01'
0> second run so item #1 to 02 and adding extra item...
0> Items on exit...
0> 1: '02'
0> 27: 'abcded'
0> resetting in 10 seconds...

`

Issue request type

[ ] Question
[ ] Enhancement
[x] Bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions