Other Parts Discussed in Thread: SYSCONFIG,
HI,
the title might be a little misleading, however the issue I have is similar to the one found here:
My goal: to store some settings (I only need six bytes to store my structs) in NVS, so that they can be changed at runtime, but want to initialize them at compile/link/FW load time, not at boot time (i.e. the values that have been changed by the user should be preserved even if the device shuts off completely due to a power failure and reboots when power is available again). I'm also trying to follow a design pattern that I have successfully used with the MSP430 and its information memory, in order to keep coherence in my framework, which targets both platforms, both in its bare-metal version and in its OS-based one (mandatory for the CC2340 using BLE).
Basically I would like to define an NVS instance/region in Sysconfig, so that I can use the recommended NVS APIs at runtime. Then, I would like to use the TI Clang compiler/linker attributes to initialize the values when flashing the device. Disclaimer: I have read all the available documentation and searched e2e across all devices/forums, the closest thread that I found was the one linked above, however there was no definitive answer from the TIer and the user reverted to a workaround that doesn't solve the issue at all.
Here's what I try and what happens:
This is my valid code (tested working when not creating the NVS in Sysconfig, as exposed in the memory browser with the expected endianness, I'm using uint32_t types for better compatibility with this core, as opposed as uint16_t types in the MSP430):
#DEFINE SEGMENT_D 0x7F800 // (address chosen because it's at the last 0x800 segment at the top of the flash, correct me if I'm wrong that this is the minimum amount of memory for an NVS instance)
const uint32_t setting_a __attribute__((persistent, location (SEGMENT_D + 0), used)) = 0xdead;
const uint32_t setting_b __attribute__((persistent, location (SEGMENT_D + 4), used)) = 0xdeaf;
const uint32_t setting_c __attribute__((persistent, location (SEGMENT_D + 8), used)) = 0xbeef;
1) If I add a GENERATED NVS instance in Sysconfig at a different base address (in this case 0x7f000, with a 0x800 sector), this is what I get after compiling/linking/loading:
MEMORY BROWSER:
0x000 7F800 setting_a
0x000 7F800 0xAD 0xDE 0x00 0x00
0x000 7F804 setting_b
0x000 7F804 0xAF 0xDE 0x00 0x00
0x000 7F808 setting_c
0x000 7F808 0xEF 0xBE 0x00 0x00
MEMORY MAP:
0007f000 0007f000 00000800 00000000 rw-
0007f000 0007f000 00000800 00000000 rw- .TI.bound:flashBuf0 // The NVS instance
0007f800 0007f800 00000004 00000004 rw-
0007f800 0007f800 00000004 00000004 rw- .TI.bound:setting_a
0007f804 0007f804 00000004 00000004 rw-
0007f804 0007f804 00000004 00000004 rw- .TI.bound:setting_b
0007f808 0007f808 00000004 00000004 rw-
0007f808 0007f808 00000004 00000004 rw- .TI.bound:setting_c
2) If I add a POINTER NVS instance in Sysconfig at a different base address (in this case 0x7f000, with a 0x800 sector), this is what I get after compiling/linking/loading:
The memory browser is unchanged (as expected), while the memory map shows no entry at 0007f000 (also expected).
HOWEVER, IF
1) I create a GENERATED NVS instance at the same 0x7f800 address, it won't compile/link/load because I get an:
#10010 errors encountered during linking; "test_01.out" not built test_01 C/C++ Problem
<a href="file:/home/disegni/ti/ccs1230/ccs/tools/compiler/dmed/HTML/10099.html">#10099-D</a> program will not fit into available memory, or the section contains a call site that requires a trampoline that can't be generated for this section. run placement with alignment fails for section ".TI.bound:flashBuf0" size 0x800, overlaps with ".TI.bound:seting_a", size 0x4 (page 0) test_01 C/C++ Problem
gmake: *** [all] Error 2 test_01 C/C++ Problem
gmake[1]: *** [Esachron_02.out] Error 1 test_01 C/C++ Problem
recipe for target 'all' failed makefile /test_01/Debug line 246 C/C++ Problem
recipe for target 'test_01.out' failed makefile /test_01/Debug line 250 C/C++ Problem
2) I create a POINTER NVS instance at the same 0x7f800 address, it does compile/link/load but this is what I see in the memory browser:
MEMORY BROWSER:
0x000 7F800 setting_a
0x000 7F800 0xFE 0xE0 0x0F 0x96
0x000 7F804 setting_b
0x000 7F804 0xFF 0xFF 0xFF 0x96
0x000 7F808 setting_c
0x000 7F808 0xFF 0xFF 0xFF 0x96
This behavior occurs no matter which base address I choose (of course they must be aligned to a 2048 page boundary).
Looking at the NVS section of the generated ti_drivers_config.c (when I use different base addresses), I find the following, for the different options:
1) GENERATED NVS at different base address (notice the initial comment, which is not present in the following POINTER generated file):
/*
* =============================== NVS ===============================
*/
#include <ti/drivers/NVS.h>
#include <ti/drivers/nvs/NVSLPF3.h>
/*
* NVSLPF3 Internal NVS flash region definitions
*
* Place uninitialized char arrays at addresses
* corresponding to the 'regionBase' addresses defined in
* the configured NVS regions. These arrays are used as
* place holders so that the linker will not place other
* content there.
*
* For GCC targets, the char arrays are each placed into
* the shared ".nvs" section. The user must add content to
* their GCC linker command file to place the .nvs section
* at the lowest 'regionBase' address specified in their NVS
* regions.
*/
#if defined(__TI_COMPILER_VERSION__) || defined(__clang__)
static char flashBuf0[0x800] __attribute__ ((retain, noinit, location(0x7f000)));
#elif defined(__IAR_SYSTEMS_ICC__)
__no_init static char flashBuf0[0x800] @ 0x7f000;
#elif defined(__GNUC__)
__attribute__ ((section (".nvs")))
static char flashBuf0[0x800];
#endif
NVSLPF3_Object NVSLPF3_objects[1];
static const NVSLPF3_HWAttrs NVSLPF3_hwAttrs[1] = {
/* CONFIG_NVS_0 */
{
.regionBase = (void *) flashBuf0,
.regionSize = 0x800
},
};
#define CONFIG_NVS_COUNT 1
const NVS_Config NVS_config[CONFIG_NVS_COUNT] = {
/* CONFIG_NVS_0 */
{
.fxnTablePtr = &NVSLPF3_fxnTable,
.object = &NVSLPF3_objects[0],
.hwAttrs = &NVSLPF3_hwAttrs[0],
},
};
const uint_least8_t CONFIG_NVS_0_CONST = CONFIG_NVS_0;
const uint_least8_t NVS_count = CONFIG_NVS_COUNT;
***************************************************************************************
2) POINTER NVS at different base address:
/*
* =============================== NVS ===============================
*/
#include <ti/drivers/NVS.h>
#include <ti/drivers/nvs/NVSLPF3.h>
NVSLPF3_Object NVSLPF3_objects[1];
static const NVSLPF3_HWAttrs NVSLPF3_hwAttrs[1] = {
/* CONFIG_NVS_0 */
{
.regionBase = (void *) 0x7f000,
.regionSize = 0x800
},
};
#define CONFIG_NVS_COUNT 1
const NVS_Config NVS_config[CONFIG_NVS_COUNT] = {
/* CONFIG_NVS_0 */
{
.fxnTablePtr = &NVSLPF3_fxnTable,
.object = &NVSLPF3_objects[0],
.hwAttrs = &NVSLPF3_hwAttrs[0],
},
};
const uint_least8_t CONFIG_NVS_0_CONST = CONFIG_NVS_0;
const uint_least8_t NVS_count = CONFIG_NVS_COUNT;
***************************************************************************************
3) POINTER NVS at same base address is identical and looks like the preferred option with no buffer allocated, but unusable since the memory is initialized automatically:
/*
* =============================== NVS ===============================
*/
#include <ti/drivers/NVS.h>
#include <ti/drivers/nvs/NVSLPF3.h>
NVSLPF3_Object NVSLPF3_objects[1];
static const NVSLPF3_HWAttrs NVSLPF3_hwAttrs[1] = {
/* CONFIG_NVS_0 */
{
.regionBase = (void *) 0x7f800,
.regionSize = 0x800
},
};
#define CONFIG_NVS_COUNT 1
const NVS_Config NVS_config[CONFIG_NVS_COUNT] = {
/* CONFIG_NVS_0 */
{
.fxnTablePtr = &NVSLPF3_fxnTable,
.object = &NVSLPF3_objects[0],
.hwAttrs = &NVSLPF3_hwAttrs[0],
},
};
const uint_least8_t CONFIG_NVS_0_CONST = CONFIG_NVS_0;
const uint_least8_t NVS_count = CONFIG_NVS_COUNT;
***************************************************************************************
So, I ask myself, where and how is it that, in the case I choose the POINTER NVS at the same address, the memory is not initialized the way I want, with my defaults at FW load time. Is there an obscure option within the linker or flashing tool (or CCS)? If those non 0xFF values are some NVS control structs, why isn't the driver initializing them at a later stage, after the first usage of the instance?
Can you please point me to a solution or at least to a workaround that is not too quick and dirty and hard to learn for the casual user, since I'm trying to use this functionality in the context of my framework.
Apologies if I'm missing something so self-evident or if I'm doing something completely wrong, but I'm new (like almost everyone else), to this excellent platform.
Thank you for your help,
kind regards
Stefano