Question about avr-gcc compiler optimization of struct* constants

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

I'm using AVR Studio with the avr-gcc compiler.

I'm using the ATmega128 for a project which has a lot of EEPROM parameters.  I have come to learn that maintaining #define EEPROM locations is a pain in the bum, so I've come up with this (as an example):

struct EEPROM_s
{
	unsigned char key1;
	unsigned int key2;
	unsigned char key3;
	unsigned int key4;
	unsigned char key5;
};

// interface 
unsigned char EERead8( unsigned int addr );
unsigned int EERead16( unsigned int addr );
void EEWrite8( unsigned int addr, unsigned char data );
void EEWrite16( unsigned int addr, unsigned int data );

#define EEPROM_LOC( name )		(unsigned int)&( ( (struct EEPROM_s*)0 )->name )

So, if I want to write to key3, I just have to write:

EEWrite8( EEPROM_LOC( key3 ), 42 );

The #define works, in that it returns the correct EEPROM address.

My question is: does the compiler turn the evaluation of the value of EEPROM_LOC into a simple unsigned int constant, or does it bugger about adding structure properties? 

Secondary question is how do I look at the assembler output of my project with function labels?  I could just look at that instead.

Many thanks for any replies.

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

I'm sorry to have troubled you, I saw on another forum that I can view the .lss file in ../debug.

 

printz( "key3 adr = %d\n", EEPROM_LOC( key3 ) );
     3da:	1f 92       	push	r1
     3dc:	83 e0       	ldi	r24, 0x03	; 3
     3de:	8f 93       	push	r24
     3e0:	8d ef       	ldi	r24, 0xFD	; 253
     3e2:	90 e0       	ldi	r25, 0x00	; 0
     3e4:	9f 93       	push	r25
     3e6:	8f 93       	push	r24
     3e8:	af d5       	rcall	.+2910   	; 0xf48 <printf_P>

... and the compiler does indeed turn it into a constant in the assembler.

 

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

In it's generic form, your construct:

#define EEPROM_LOC( name )		(unsigned int)&( ( (struct EEPROM_s*)0 )->name )

is quite widely used. As such it gets an entry named offsetof in <stddef.h>

/* Offset of member MEMBER in a struct of type TYPE. */
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)

For the best performance I would rewrite your MACRO to use it thus: (NB: untested)

#define EEPROM_LOC( name )	(unsigned int) offsetof( (struct EEPROM_s), name )

 

 

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

Loving "George and Mildred". That certainly places you in a specific time and a specific place! 

 

Gotta ask though, why are you reinventing the EEPROM wheel? Why not avr/eeprom.h and EEMEM to let the linker place your variables for you? 

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

I'm a Luddite, and I like to write portable code :)

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

Thanks for your reply :)

I'm not sure what you mean by "best performance". 

Do you mean that the macro you advised is better, or similar, or the same in code size, or more portable, or less portable?

I design firmware over a few targets, and I like to keep my interface clean. 

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

I meant "best performance" over all optimisation settings.

I imagine it would be "more portable" because it's implemented in <stddef.h>

 

 

E.g. Microsoft have a "builtin"and a C and C++ definition.

#if defined _MSC_VER && !defined _CRT_USE_BUILTIN_OFFSETOF
    #ifdef __cplusplus
        #define offsetof(s,m) ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
    #else
        #define offsetof(s,m) ((size_t)&(((s*)0)->m))
    #endif
#else
    #define offsetof(s,m) __builtin_offsetof(s,m)
#endif

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/eeprom.h>

struct EEPROM_s
{
    unsigned char key1;
    unsigned int key2;
    unsigned char key3;
    unsigned int key4;
    unsigned char key5;
};

EEPROM_s EEMEM eeVars;

int main(void) {
    eeprom_update_byte(&eeVars.key1, 'X');
}

Does it really need to be any more complex than this ?

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

Thank you :)

I had no idea such a feature was available in <stddef.h>.

I come from an assembler and VLSI ASIC design background, and I'm used to writing things from scratch.  I think I need to catch up a bit in terms of C libraries and see what else I can get for free :)

Best regards.

 

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

Thanks for your reply :)

I'm not sure that what I'm doing is particularly complex, but your solution isn't portable across AVR/PIC/ARM compilers.  I know this is a thread about AVRs, but I did mention portability of my code.

Best regards.

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

Off Topic, my two goats are named after them.  The thing is, George wants all the attention and Mildred just wants to find a quiet place to lie down for a nap. 

// goat.h
struct goat_s
{
    int funny;
    int odd;
    int hungry;
    int playtime;
    int randomEvent;
    int sleep;
    int playFight;
    int whySitThere;
}

I have tried modifying the settings in <goat.h>, but it always reads back as a random number.

Best regards.

 

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

George and Mildred wrote:
... but your solution isn't portable across AVR/PIC/ARM compilers.
That can be difficult to completely achieve due to C's ambiguities.

Clang is common among those three computer architectures.

 

Common C Interface Standard | MPLAB® XC8 C Compiler User’s Guide for AVR® MCU

offsetof Macro | MPLAB® XC8 C Compiler User’s Guide for AVR® MCU

AVR LLVM released | AVR Freaks (Clang)

Compiler Overview | MPLAB® XC8 C Compiler User’s Guide for PIC® MCU

[below Note]

When compiling for the C99 standard, this compiler utilizes the Clang compiler front end. The older CPP/P1 front end is used when building for C90 projects.

Clang C Language Family Frontend for LLVM

 

"Dare to be naïve." - Buckminster Fuller

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

gchapman wrote:
can be difficult to completely achieve due to C's ambiguities.

also due to fundamental differences between the different underlying target hardwares.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

George and Mildred wrote:
but your solution isn't portable across AVR/PIC/ARM compilers.  I know this is a thread about AVRs, but I did mention portability of my code.

Which of those have "portable" byte-addressable R/W EEPROM?  I always rail a bit about cross-target total portability.  IME one of the reasons an architecture and/or model was chosen in the first place is that it had a best-in-class feature.  Will you then dumb-down ALL of your apps to the common denominators?  I think not.

 

But put in a portability layer (which may not be the most space-efficient in Mega8-class apps) and now you can have your portable primitives "update parameter XYZ to value ABC".  Underneath on the AVR you use efficient and solid facilities such as described above.  On your MSP430 you use other proven techniques to handle the double parameter-storage-block in flash.  And whatever applies to the others -- maybe you have an external device.

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.

Last Edited: Mon. Oct 11, 2021 - 08:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using the excellent Godbolt Tool I find it works for all the compilers I tried. (including avr-gcc)

 

https://godbolt.org/z/5TMsx4fdh

 

You used generic int so the actual values do vary.

Last Edited: Mon. Oct 11, 2021 - 08:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
Using the excellent Godbolt Tool I find it works for all the compilers I tried.

 

heh heh.  It comes from six blocks distance from my first college dorm.

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.

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

George and Mildred wrote:
The #define works, in that it returns the correct EEPROM address.

 

...but happens to be a completely unnecessary hack. Such `#define`s are usally written by people who can't be bothered to actually learn the programming language they are trying to use.

 

Standard library already provides a feature, called `offsetof`, which implements this exact functionality.  The language (C, as well as C++) guarantees that the result of `offsetof` is an integral constant expression (http://port70.net/~nsz/c/c11/n15...), which immediately means that it is known at compile time.

Dessine-moi un mouton

Last Edited: Wed. Oct 13, 2021 - 04:59 PM