Controlling flash data & addresses at compile time

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a data structure stored in flash that contains application configuration parameters, but want to explicitly set the flash address AND the value contained in those addresses at compile time. See example structure below. Then, the compiled .hex file will already contain the default settings to be used by my application on first power-up. Does anyone know how to do this in 'C' using WinAVR? Any help/tips would be greatly appreciated. Thanks.

typedef struct {
	signed long paramA;
	signed long paramB;
	unsigned short paramC;
	unsigned short paramD;
}params_t;

// I want to locate this data at address 0x1000 in flash
params_t appConfig = {
	{36000, 39000, 200, 20}}
};

[/u]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In avr-gcc, you put data into flash using PROGMEM (there is a thread in the tutorials section to tell you how).

Quote:
I want to locate this data at address 0x1000 in flash
You can put data into named sections. The question now is, why?

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The reason is to store user config data in Non-volatile memory. I anticipate someone's response to suggest EEPROM is be better suited to do this, however, there is not enough EEPROM space available for the user config data structure in this particular application.

For future reference though, is there a similar command to PROGMEM available to specify EEPROM start addresses? Thanks.

Clint

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

For future reference though, is there a similar command to PROGMEM available to specify EEPROM start addresses?

The EEPROM based equivalent is PROGMEM is EEMEM. There's a whole article about its use in the Tutorial Forum.
Quote:

The reason is to store user config data in Non-volatile memory. I anticipate someone's response to suggest EEPROM is be better suited to do this, however, there is not enough EEPROM space available for the user config data structure in this particular application.

The you realise that:

a) you will only be able to re-write the data using SPM routines that are located in the BLS

b) Unlike eeprom the flash is in 32/64/128/256 byte pages and to change just one byte you must erase an entire page and then rewrite updated contents with the single byte changed

c) the flash has a 10,000 (per page) erase limit while the eeprom was 100,000 erase cycles (per byte)

It may be as well to store the more often updated values in EEPROM anyway and put the very seldom changed values in flash.

To achieve this the "magic" is to use __attribute__((section(".yoursectionname"))) in the code and then --section-start=.yoursectionname=0xNNNNN (given in BYTES not words!) as an instruction to the linker. Determine how much SPM code you will require then set the BLS boundary accordingly using the BOOTSZ fuses. Use and BOOTLOADER_SECTION on the SPM routines then position them with --section-start=.bootloader=0xMMMMM (BLS address in bytes). Having determined the BLS address then also determine your flash based data size and subtract this from the BLS address and round down to the nearest SPM page boundary. Now you have your --section-start value for .yoursectionname

The easiest way to position the data is to put it all into a single struct such as:

typedef struct {
 int n;
 long l;
 char array[10];
} struct_type;

struct_type struct_in_flash __attribiute__((section(".yoursectionname")));

struct_type struct_copy_in_ram;

I'd also engineer a pgm_read_block() from pgm_read_byte() run in a loop to sizeof(struct_in_flash) in order to read the struct from struct_in_flash to struct_copy_in_ram. Or else just pgm_read_???() the individual struct elements.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks. This helps a lot. After a reset or power-up, I do actually copy the config data from flash into RAM for run time, so that part is already done.

Quote:
...then --section-start=.yoursectionname=0xNNNNN (given in BYTES not words!) as an instruction to the linker.

Quote:
...then position them with --section-start=.bootloader=0xMMMMM (BLS address in bytes).

Where do you put these commands? I'm using WinAVR with AVR Studio. Is there a linker script file, or do I just put them in the compiler configuration options? When I look at the compiler config options, I see what looks like two possible ways to do this: 1) Memory Settings -> Add -> Name, Address, or 2) Custom Options -> [Linker Options] -> Add. Which one do I use? Thanks.

Clint

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Where do you put these commands? I'm using WinAVR with AVR Studio. Is there a linker script file, or do I just put them in the compiler configuration options?

Studio has tried to make section address setting easier. On the Project menu use "Configuration Options". Within that go to the section "Memory settings". There use the [Add] button. Leave "Memory type" set to "flash". In "name" enter the name such as .yoursectionname or .bootloader

In the address field this is VERY IMPORTANT. All Atmel tools (including this one even though it uses GCC under the hood) addreses in flash are given as WORD addresses. So in this box put the address in words (half the address in bytes) of where you want things positioned. When you look at the Build Tab output you'll see that Studio itself then doubles these values back into the byte addresses that GCC likes to use on the --section-start's

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay thanks. One more thing that hasn't been answered yet - initializing the data in the struct.

Your code:

typedef struct { 
 int n; 
 long l; 
 char array[10]; 
} struct_type; 

struct_type struct_in_flash __attribiute__((section(".yoursectionname"))); 

struct_type struct_copy_in_ram;

Could I then do this to initialize my data:

typedef struct { 
 int n; 
 long l; 
 char array[10]; 
} struct_type; 

struct_type struct_in_flash = { 
    {32767, 2000000, "Hello"}
}__attribiute__((section(".yoursectionname"))); 

struct_type struct_copy_in_ram;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes that will work.