Search code examples
cavravr-gccsectionsflash-memory

How to reserve a fixed flash section for data?


I need to store some large chunks of data in flash memory, where it will be read often and occasionally be rewritten using SPM. I already figured out how to use pointers to __flash and pgm_read_byte to access it, how not to omit the const (despite my writing to it), how to actually access the array in a loop so that it doesn't get completely optimised away (after inlining), but I don't really understand how to declare my array.

const uint8_t persistent_data[1024] __attribute__(( aligned(SPM_PAGESIZE), 
                                                    section("mycustomdata") )) = {};

works about fine, except that I do not want to initialise it. When programming my device (an Arduino ATmega328P), I want this section to be keept so that it retains the data previously written by the application. The above does zero-initialise it, and my hex file contains zeroes that the programmer happily uses to overwrite my data.

Using the __flash modifier instead of __attribute__(( section("…") )) does about the same here, except that it places the array elsewhere and I don't have any control about where it is put. It still does this when I use __flash and omit the initialisation (though I get a "uninitialized variable 'persistent_data' put into program memory area [-Wuninitialized]" warning).

Now I am trying to omit the initialiser:

const uint8_t persistent_data[1024] __attribute__(( aligned(SPM_PAGESIZE),
                                                    section("mycustomdata") ));

and get rather unexpected results. The sections data from the .lss output shows

Idx Name          Size      VMA       LMA       File off  Algn
  …
  1 mycustomdata  00000480  00800480  000055e2  00005700  2**7
                  CONTENTS, ALLOC, LOAD, DATA
  2 .text         00005280  00000000  00000000  000000d4  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE

This does put all the initialisation zeroes in the hex file at the load memory address 55E2 (instead of omitting them), while the virtual memory address (which the variable persistent_data points to) refers to 0480 - in the middle of the code from the text section!

(I also tried to omit the const, and to omit the const and the initialiser, which both had the same effect as omitting only the initialiser).

I am at a loss. Do I need to use extern maybe? (Any attempt at doing so ended up with a "undefined reference to persistent_data" error). Do I need to use a linker script?

How do I make persistent_data refer to a location is program memory that is not used by any other data, and have the compiler not emit any initialisation data for that location in the hex file?


Solution

  • You don't seem to realize that you actually need two versions of your hex file - one that is suitable for a "new" installation on a new (or worse: re-used, thus with random flash content) chip that initializes the flash section to make sure there is no arbitrary data in there that might be interpreted, and another one used to update a pre-programmed chip that misses this section in order to keep data already modified by your users. So, you are going to need the version that initializes this section anyhow.

    The simplest way to achieve this is like your first example, initialize the data to build the "naked chip" version of your code, and produce the "update" version by simply removing this initialized section from the object file with objcopy (assumed you use a GNU toolchain). See the -R option of this tool.

    Also, make sure this data section is located at a fixed address - you don't want it to move every time you change something in your code.

    I would rather try and use EEPROM if available than go through the hassle of reprogramming.