GCC 4.7.2 EEMEM storage

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

Hi all,

I switched to GCC 4.7.2 compiler and noticed that the EEMEM variables are stored in reverse order. Example if I declared two variables as shown below

char EEMEM ch1;

char EEMEM ch2;

 

in 4.3.x ch1 is assigned 0, and ch2 is assigned 1 addresses. However in 4.7.2 ch2 is assigned the last available EEPROM address and ch1 is assigned one before. Why this change? Is it done for any specific reasons? How can re-order this list like in the earlier version? Thanks for your time and support.

 

 

Parthasaradhi Nayani

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

It's a change in the avr-ld linker in binutils . You can look at the development history to see why it changed. It could be that the authors had a deliberate plan in the change or it could just be a side effect of how they process the symbols - perhaps they just found it easier or more efficient to process them in reverse order?

 

Anyway you should NEVER rely on some quirk of the compiler/linker like this. The C standard says nothing about the order of variable placement in memory so it is "implementation defined" and as such it subject to change at any time.

 

If you want a specific order in memory you can guarantee that with a struct{}. So use:

struct {
  char ch1;
  char ch2;
} eevars EEMEM;

You are still subject to issues such as aligment/packing but on AVR the packing is to byte boundaries so is the "obvious" layout.

 

You will now have to refer to:

eeprom_write_byte(&eevars.ch1, 'A');
if (eeprom_read_byte(%eevars.ch2) == 'B')...

etc.

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

Hi clawson,

Thanks for the reply. My problem is, we have some boards at our customer sites whose code was compiled using the older GCC 4.3 and some EEPROM locations were written with data on site.

New upgraded code was loaded on site and because of the ordering/storage of variables in EEPROM, the earlier settings were lost or not readable by code prompting the user to enter fresh settings. We were not sure what the problem was till we found that the difference in the way variables in EEPROM section were stored in 4.3 and 4.7. The sequence of storage has also changed. Is there any linker flag which we can use to tell linker to store from lower address rather than from higher. Incidentally the first address is not the highest address of the available memory. Wonder how linker just picked a random address. Any suggestions please? Thank you.

Parthasaradhi Nayani

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

I just told you how to "repair" this. In the latest code put them all in ONE struct. You get to control the layout within the struct so can make it match the deployed layout. While it's pretty certain the linker will place that one struct variable at 0x810000, if there's any doubt you can force it there with a section declaration and then a -Wl,-section-start=0x810000. But, as I say, with just one struct variable the linker will place it there anyway.

 

All you then have to worry about is your struct layout matching the previous layout the linker happened to generate.

Wonder how linker just picked a random address.

There is nothing "random" about  it. It is 100% determinisitic. It just so happens that it is a DIFFERENT deterministic layout compared to the ancient version of compiler/linker you were using.

 

(BTW why 4.7 and not at least 4.8 or even 4.9?)

Last Edited: Tue. Apr 7, 2015 - 08:51 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

stupid side question :

is a struct guarantied to be in a known order? (I know that a array are but not a struct!) 

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

Yes.

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

ok thanks.

I was under the impression that it was legal for the compiler to rearrange so  :

8bit

16 bit 

8 bit

 

would have a layout of:

8 bit

8 bit

16 bit 

to match the structure of the CPU

 

 

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

No,   AFIK,  the compiler is not allowed to change the order of the struct members.

 

However,   the compiler is certainly allowed to add padding to members so that they are aligned conveniently for the target.

e.g. a M68000 compiler would ensure that pointers were word-aligned.

 

Since the AVR has no alignment issues,   no AVR compilers do any alignment.

 

Regarding your EEMEM 'layout'.    Cliff and I disagree.    Personally I would #define specific offsets for each byte of data in EEPROM.    Then I am not dependent on one particular toolchain or even one particular release.    It also means that you can migrate to ARM or 8051 with the same 'User data'.

 

Of course,   this does mean that you access an EEPROM location slightly differently with GCC.   i.e. with a number rather the &variable.    But it is still the same eeprom_read_xxx() function.

It is a significant difference for Codevision because CV handles EEPROM natively.

 

Cliff would argue that a C variable should be left to the Compiler.    Which is fine for a one-off build with a one-off toolchain release.    But not so good if your engine's configuration data has moved.

 

If you do migrate to a fixed #define of each EEPROM location,   don't do any mixing with the '&eeprom_variable' approach.   i.e. stick to one style or the other.

 

David.

 

p.s.  implementation alignment applies to all aspects of a C compiler.   Which is why you should always be very careful with copying raw data from one system to another.    e.g. a FAT filing system to a big-endian CPU

Last Edited: Tue. Apr 7, 2015 - 09:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Agreed that the order of structure members is not meddled by the compiler. Assuming no padding, nothing prevents compiler/linker from moving the entire structure from one memory location to another. Since our requirement is to access non-volatile memory which was in a place before upgrading is no more at the same location,we are getting errors etc. How can one solve this problem? This is specially dominant/requirement in embedded systems. Any pointers please?

Parthasaradhi Nayani

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

read this:

http://teslabs.com/openplayer/do...

 

something like this I guess

 

ByteOfData = eeprom_read_byte((uint8_t*)46);

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

Cliff has given you one solution.

I have given you another solution.

 

Whichever way that you do it,   you need to alter your source code.   e.g.

 

Cliff's approach:

     // your current approach 
     eeprom_update_word(&ee_word_variable, value);   
     // put all EEMEM variables int a single ee_struct
     eeprom_update_word(&ee_struct.ee_word_variable, value);   
     

My approach:

     // your current approach 
     eeprom_update_word(&ee_word_variable, value);   
     // #define OFFSET of EACH LOCATION
     eeprom_update_word((uint16_t *)EE_WORD_VARIABLE_OFFSET, value);   
     

One advantage of the GCC project is that you can just egrep for "eeprom_" to see where you need to edit.

It is a lot harder to 'un-native' CV.

 

David.

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

I was under the impression that it was legal for the compiler to rearrange

Then how could you do things like this:

 

http://www.tenouk.com/Module43.html

 

Almost everything in TCP/IP is defined in term of C struct{}'s with a fixed layout. One PC reads another's TCP and IP packets by applying the same struct{} layout to both. The "issues" that may be involved in doing this are just packing/alignment and possible issues of endianism interpretation.

 

It's interesting to note, however, that when Chan attempts to read FAT data structures in FatFs he chooses to do so using "David's method" - that is simply #define'ing offsets to the various fields - so that he can avoid issues like alignment/packing.

 

So it's probably 50:50 as to which one is the "right" solution.

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

For all HW things (for me eeprom is that), I would do like David.

That bring all guessing out of the question. (perhaps more typing but clear to see where something start.)

And since I use Delphi on the PC I don't reuse the headers.

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

I always come back to C variables in SRAM. When you write:

int n = 12345;
long l = 0xBABEFACE;
char c = 'X';

int main(void) {
 ...

you'd never think about where the linker might be choosing to hold n, l and c. It picks some magic location for you and you just accept what it chose.

 

I can see how EEPROM is a bit different in that it is maintained between one build of software and another but personally I'd still let the linker take care of it for you because if you get into the realms of:

#define N_LOC 100
#define L_LOC 102
#define C_LOC 105

you've now taken on the manual job of doing what just comes naturally to the linker. It's far to easy to make the kind of error I just did ;-)

 

(yes the 105 truly is the "deliberate error" in that to make a point!).

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

I guess it's the two different way to look at it.

 

Is it a computer, that control some HW. (cliff)

 

Or is it some HW you can control with SW. (me)

 

And like your example with "int long char" is very bad :) at least something U8 U16 ... if it's related to something known.

And then you have the risk of padding.

 

And I'm the person that prefer to write ASM so I'm 100% in control, (perhaps it take a bit longer to write), but it so much easier to debug because you exactly know what it's supposed to do.

 

As a side note the project I work on at the moment I have written the eeprom routines myself, but that was primary because when I planed the code I wanted to have CRC on some parts of the eeprom, and that way I could hide it. (after all eeprom read and write is only about 10 lines of code, and I have the old habit of not using address 0 on an AVR so I have a hidden offset.)    

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

Hello all,

Thank you for all your suggestions and comments. To keep the order of variables one possibility is to use structure(s). This method may be acceptable if order of variables is more important than the actual location, however this will not solve the problem if non-volatile memory is used, as in my case. Even the location at which the structure is stored is important. Using fixed addresses has its own problems/bugs as clawson has just shown the example. IMHO, a better approach would be -

 

1. Use structures, to keep order of variables intact.

2. Use a pointer to the structure and assign a fixed address to the pointer (as done in most ARM compilers to access registers).

 

Will the above approach result in extra code? Well even if it does, one has to live with it, I guess unless there is a better method.

 

Your valuable comments please? Thanks.

 

 

 

Parthasaradhi Nayani

Last Edited: Wed. Apr 8, 2015 - 01:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes,  of course you can make a typo when writing your series of #defines for the EEPROM offsets.

It is your responsibility to get this correct.    But once you have created them,   your EEPROM data is accessible in every version and platform that you may ever use.

 

Only you know how you are using your EEPROM.   e.g. for configuration data or for 'non-volatile' variables.

 

You have to design your application properly in the first place.    After all,   your EEPROM locations must never change.    However,  you can add extra locations at a later date.

 

David.

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

Thanks for your feedback David. So programmer has really no control over Compiler/Linker sad

Parthasaradhi Nayani

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

Well,  you could control the Compiler and you could control the Linker.    It would be messy but some people like to make things complicated.

 

You do realise that the struct method is not completely safe.   e.g. a later Linker might choose to allocate EEPROM starting at a different location to 0.

 

I think that I showed you the sort of edits required for the two different approaches.

Yes,  it would take you 10 minutes to do the necessary egrep and edit(s).

You could even automate the edits with a sed script.

 

You might even be able to do the changes with a simple block of #defines in one H file and zero changes to the C files.  

e.g. #define ee_old_variable ee_struct.new_variable

 

It is always risky to change namespaces without careful thought.    You can end up with using a name twice or wrong scope.

 

Before we spend a lot of our time on this subject,   why not do an egrep over all your project files?    Are we talking about 10s of lines or 1000s of lines that refer to EEPROM data?

 

David.

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

Hi David,

Thanks for the reply. Even if linker/compiler(s) change, the approach of pointer with fixed address will solve this issue.

Parthasaradhi Nayani

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

I don't see how you think that this would solve anything.

 

Let's say that you have chosen the ee_struct method.    You still have to edit all the eeprom_func_xxx() references.

If you are not going to tell the linker to place the ee_struct at a specific location,   you can deference a ee_struct pointer to point to a fixed address.

 

Then you would use &(ee_struct_ptr->new_variable) instead of &ee_struct.new_variable.

 

It is your choice and your program.    And I wish you luck with the associated maintenance.

 

David.

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

however this will not solve the problem if non-volatile memory is used, as in my case. Even the location at which the structure is stored is important.

And why do you believe that to be true (it isn't by the way). I know David said:

You do realise that the struct method is not completely safe.   e.g. a later Linker might choose to allocate EEPROM starting at a different location to 0.

But he's just messing with your mind ;-)

 

As I say your only potential problem when putting all vars in a single struct is whether you can guarantee that struct is located at 0 in EEPROM. I think you can rely on the linker to start counting from 0 in any address space myself but if you thought there were any doubt then use an __attribute__((section(".myeesect"))) on the struct (and forget "EEMEM" which itself is just __attribute__((section(".eeprom"))) anyway) and then in the linker use -Wl,-section-start=.myeesect=0x810000 and that will do pretty much what was happening with .eeprom anyway. You'd also need to modify the avr-objcopy that creates the .eep file.

So programmer has really no control over Compiler/Linker

Now you are getting silly - the programmer has TOTAL control over the compiler and linker. In fact GCC possibly offers the most configurable compiler and linker in the world. There aren't many people who understand all of the hundreds (possibly thousands?) of command line options the tools support! (and I haven't even mentioned things like linker scripts).

 

Oh and the thing about pointers to EEPROM is a complete red-herring.

 

David was very clear in his display of two examples above. You either use struct{} or you #define and either is very simple. I'm not entirely sure why you are making a mountain out of this particular mole-hill?

 

Oh and you know the overlapping thing I warned about with #define's? You could mitigate a lot of the risk with something along the lines of "+sizeof(var_type)" to place varN+1 after varN of var_type.

 

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

But he's just messing with your mind ;-)

Now you are revealing all my secrets!

 

David.

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

I use struct because I need to know where things are in EEPROM - the whole point of using EEPROM is that I can go in and change parameters without recompiling / reflashing.

 

The other point I would make is that as well as using struct you can also initialise like below. Sorry if it's already obvious to everyone, it wasn't to me (momentary lapse).

 

struct  
{
	uchar	dbSteps;			// how much debouncing to do
	uchar	lineDelay;			// uS to wait for capacitance of wires
	// which channels are half-octaves not full ones
	uchar	halfOcts;
	// start C# note for each keyboard to avoid transposing
	uchar	baseNote[NUM_CHANNELS];
	uchar	baseChan[NUM_CHANNELS];
	// which octaves to invert? (1 = invert, bitwise)
	uchar	invOct[NUM_CHANNELS];
	// and which octave has extra note
	uchar	extraNote[NUM_CHANNELS];
}	eevars	EEMEM = 
{
	1,			// dbSteps
	20,			// lineDelay
	3,			// halfOcts
	{37, 73, 49, 49, 49, 13},	// baseNote
	{1, 1, 2, 3, 4, 5},			// baseChan
	{0, 0, 0, 16, 0, 0},		// invOct
	{1, 0, 0, 0, 0, 0}			// extraNote
};

 

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

The OP wanted to recompile a new version of the software and use the eeprom data from an historic version.

 

Your method initialises the EEPROM from the .EEP file.

 

A common technique is to test a magic location in the EEPROM.    If virginal,   you load default values into all the eeprom fields (and set the magic value in the magic location).

A non-virginal EEPROM will be left intact.     This technique does not use a .EEP file and you just set the EESAVE fuse.

 

David.

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

Sorry yes, I should have prefaced my reply with "This won't help the OP, but for the benefit of anyone else who, like me, stumbles across this thread by searching on 'EEPROM reversed order' or similar..."

 

Thanks for the other technique, I hadn't thought of that.

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

Hello all,

Thanks a lot for all your time.

Parthasaradhi Nayani

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

Thanks for the other technique, I hadn't thought of that.

You can also take it a step (or several) steps further.  Some of our apps have up to four sets of configurable/important parameters:

 

-- Active set

-- "Factory default".  Can be used as described for virgins, and also could be used to "limp  home" either upon Restore Factory Defaults command, or detection of a bad set of parameters.

 

-- Extending that last, two more sets can be used:  "Min" and "Max" for each parameter. That can be used for the aforementioned detection process.

 

Now, Min and Max can be held in flash as yet another level of "protection".  I can't think of it in my apps, but Factory set could be there as well.

 

The CodeVision toolchain allows struct assignment across memory spaces, so Restore Factory Default can be one line.  In practice in apps with short watchdog intervals and many parameters I tend to break it up and put a WDR at intervals.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.