Defining an EEPROM location for a variable

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

I've got two structures that I want to appear in EEPROM and I'd like to define where these structures are located. I've boiled my program down to the bare essentials so I could post it here:

test.c:

int Test1 __attribute__((section(".eeprom1")));
int Test2 __attribute__((section(".eeprom2")));

int main(void)
{
    ;
}

compilation:

avr-gcc.exe -mmcu=atmega32 -gdwarf-2 -c test.c -o test.o

linking:

avr-gcc.exe -mmcu=atmega32 -Wl,-Map=test.map,--section-start,.eeprom1=810000,--section-start,.eeprom2=810100 test.o -o test.elf

I used a similar procedure (using the __attribute__ and --section-start bits) earlier to place certain modules at desired locations in the Flash, but when I tried to apply this technique to EEPROM, it didn't do what I expected. I was hoping that gcc would place the variable Test1 at EEPROM location $0000 (gcc location $810000) and variable Test2 at EEPROM location $0100 (gcc location $810100). However, as you can see from the following snippets of the test.map, gcc dumped both variables in EEPROM without any direction as to where:

Memory Configuration

Name             Origin             Length             Attributes
text             0x00000000         0x00020000         xr
data             0x00800060         0x0000ffa0         rw !x
eeprom           0x00810000         0x00010000         rw !x
*default*        0x00000000         0xffffffff

Linker script and memory map

LOAD c:/WinAVR/bin/../lib/gcc/avr/3.4.6/../../../../avr/lib/avr5/crtm32.o
Address of section .eeprom1 set to 0x810000
Address of section .eeprom2 set to 0x810100
LOAD test.o
LOAD C:/WinAVR/lib/gcc/avr/3.4.6/avr5\libgcc.a
LOAD c:/WinAVR/bin/../lib/gcc/avr/3.4.6/../../../../avr/lib/avr5\libc.a
LOAD C:/WinAVR/lib/gcc/avr/3.4.6/avr5\libgcc.a
.eeprom         0x00810000        0x4
 *(.eeprom*)
 .eeprom1       0x00810000        0x2 test.o
                0x00810000                Test1
 .eeprom2       0x00810002        0x2 test.o
                0x00810002                Test2
                0x00810004                __eeprom_end = .
.eeprom1        0x00810000        0x0

.eeprom2        0x00810100        0x0

It looks like the linker understood where .eeprom1 and .eeprom2 were supposed to go, and it understood that I wanted these two variables in EEPROM, but it failed to do it how I asked.

Does anyone have any suggestions of things I could try?

BTW, I'm using avr-gcc.exe version 3.4.6.

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

See Dean's PROGMEM tutorial in the Tutorial "forum".

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38003

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

That's a dandy tutorial, but it does not appear to apply to the question at hand.

In a nutshell: I have a structure and a location in EEPROM where I want it. How do I tell the gcc linker to place the structure at that location?

Many thanks,
Gre7g

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

Personally, I'd leave the linker scripts out of the equation entirely.

I'd make all the necessary changes in the makefile, by adding new "-Wl,--section-start=..." linker arguments, and revised lists of inclusions and exclusions in the avr-objcopy lines.

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

Just out of interest, why do you want the second struct at offset 0x100 in the EEPROM? I can only think it's because you want something else besides the main app to be able to "see it" at that address and the only other thing I can think of for "something else" is a bootloader? Otherwise why does it matter and why not accept what the linker would generate by default. In fact just use the standard .eeprom. If it IS the bootloader then I'd make the two structs members of an all encompassing struct and make it's layout visible to the build of both the app and the BL

Cliff

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

This program has the ability to self-update with a download from the web. Some of the EEPROM settings should be reset in the process and some (such as calibration info) should not. I want to define hard locations for these two blocks to make sure that future changes in the size of one block will not change the starting location of the other block. Otherwise a future software update could break the unit. It might misread the calibration info because it would expect the block to start elsewhere.

lfmorrison, I'm not using linker scripts. I am using "-Wl,--section-start=..." instructions as you can see above, but gcc isn't handling them correctly (or at least how I would expect).

In a previous product, I did the following sort of solution:

union
{
    int Data;
    char Padding[256];
} Test1 __attribute__((section(".eeprom")));

This worked well as I could change Data and allow it to grow (as long as it didn't get larger than Padding) without changing anything. However, it seems like a Kludge and applying that solution to my new code would mean many changes throughout the entire codebase (every instance of Test1 would become Test1.Data).

That's why I was hoping I could fix this in the linker.

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

Try choosing different names for your sections. (Don't use ".eeprom1" and ".eeprom2". Instead, try ".regulareep" and ".fixedeep", or whatever seems appropriate. Maybe the linker is fixating on everything that matches the pattern "eeprom*" and grouping them together?)

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

I was thinking the same (and trying to modify a Makefile accordingly to test the theory - not successful as yet).

One thing that's clear is that the linker script being invoked seems to treat anything that matches *.(.eeprom*) under .eeprom - see the output above from the .map - and presumably rules that the sub-sections of .eeprom that match be placed adjacently. I imagine this over-rules any commands to position the section_starts, arbitrarily named sections presumably wouldn't be subject to this.

Cliff

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

Yarrrggghh!

Yeah, that did it. I tried sect1 and sect2 and suddenly gcc does exactly what I asked it to do.

It looks like one of those cases where there's a special loophole in the code to protect us from ourselves.

Thanks tons for the help!

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

Gre7g wrote:
It looks like one of those cases where there's a special loophole in the code to protect us from ourselves.

Nope, have a look at the linker scripts that are used which are in \winavr\avr\lib\ldscripts

This will show you what are effectively "reserved names" for sections (or rather sections for which rules already exist that might interfere with what you are trying to achieve)

Cliff

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

Ooh. Handy. Thanks!

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

I had the same problem (placing a variable at a specific location in EEPROM) and found this posting and tried the solutions out.
Indeed, when I name the sections .eeprom*, it does not work, the variables are succesively placed in the EEPROM regardless of the "--section-start" option.
But when I give the section a different name, the linker generates just an empty .eep file and I am wondering what I did wrong / forgot to configure ....
This is the message I get at compilation:

Build started 29.1.2007 at 14:14:39
avr-gcc.exe  -mmcu=atmega48 -Wall -gdwarf-2 -mcall-prologues -Os -fsigned-char  -MD -MP -MT main.o -MF dep/main.o.d  -c  ../main.c
avr-gcc.exe -mmcu=atmega48 -Wl,--section-start=.sect1=0x810070  -Wl,-Map=LiIon.map main.o init.o ISR.o ADC.o UART_ISR.o func.o     -o LiIon.elf
avr-objcopy -O ihex -R .eeprom  LiIon.elf LiIon.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 -O ihex LiIon.elf LiIon.eep
avr-objdump -h -S LiIon.elf > LiIon.lss

AVR Memory Usage
----------------
Device: atmega48

Program:    2596 bytes (63.4% Full)
(.text + .data + .bootloader)

Data:        213 bytes (41.6% Full)
(.data + .bss + .noinit)


Build succeeded with 0 Warnings...

In the program I defined a variable:

int __attribute__((section(".sect1"))) Test1 = 7;

and I added as linker options the following line:

-Wl,--section-start=.sect1=0x810070

Is there something else I should configure in order to set .sect1 as part of the EEPROM?

Thanks for your help!

I am working with a mega48 and I have avr-gcc version 3.4.6

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

It's not a problem with the linker. It's actually a problem with the commands which extract the programming HEX and EEP files from the linked output.

You need to modify the avr-objcopy lines in your makefile to fix it. There are two such lines in WinAVR's default makefile: One generates the hex (Flash) file, and the other generates the eep (EEPROM) file. Both will need modification.

First, you need to modify the hex file target's objcopy commandline to specifically omit the new section. Something like this should work:

Quote:
avr-objcopy -O ihex -R .eeprom -R .sect1 LiIon.elf LiIon.hex

Second, you need to modify the eep file target's objcopy commandline to specifically include the new section. Something like this should work:

Quote:
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 -j .sect1 --set-section-flags=.sect1="alloc,load" --change-section-lma .sect1=0x0070 -O ihex LiIon.elf LiIon.eep

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

Thanks a lot! I modified the makefile (added the marked lines):

## Intel Hex file production flags                             
HEX_FLASH_FLAGS = -R .eeprom                                   
  HEX_FLASH_FLAGS += -R .sect1                                   
                                                               
HEX_EEPROM_FLAGS = -j .eeprom                                  
HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load"   
HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0             
  HEX_EEPROM_FLAGS += -j .sect1                                  
  HEX_EEPROM_FLAGS += --set-section-flags=.sect1="alloc,load"    
  HEX_EEPROM_FLAGS += --change-section-lma .sect1=0x70           

and it works!

There would be something else though, to make the whole thing a bit more "user friendly":
1. I am using avr-gcc (WinAVR) & AVR Studio 4.12 as editor. To make everything work, I edited the makefile with a text editor and then modified the configuration options ("Edit configuration options ...") to include an external makefile.
Is there a way to add the extra options through AVR Studio?

2. AVR Studio's report about memory occupation is not correct anymore (the EEPROM memory is not even listed):

AVR Memory Usage
----------------
Device: atmega48

Program:    2892 bytes (70.6% Full)
(.text + .data + .bootloader)

Data:        161 bytes (31.4% Full)
(.data + .bss + .noinit)


Build succeeded with 0 Warnings...

The generated .eep file and the pointers I used in the program to refer some EEPROM locations are correct though.
Is there a way to correct this issue (include this new section as part of EEPROM for the memory ocupation report)?

Radu

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

1. Unfortunately, I don't think AVR Studio's project options dialogue box currently includes any facility for modifying the objcopy command line options.

2. The version of avr-size included in WinAVR has been patched by EW to group certain memory sections together in its report. The result is a more friendly report of memory usage for typical AVR applications, including percentage utilization for each of the common memory types. However, a side effect of this is that it ignores all memory sections which don't fit in its list.

If I'm not mistaken, it's still possible to modify its command line options to switch to a more "traditional" memory report where all memory sections are itemized individually. I believe the command line option would be "-A". However, a drawback of this format is that you'd need to do the math yourself to add up the totals for the various related memory types (.bss + .data + .noinit = RAM; .data + .text + .bootloader = Flash, etc), and determine the percent utilization.