Topic : Struct member location

Forum : ST7/STM8

Original Post
Post Information Post
January 25, 2011 - 12:23am
Guest

Hi,

I am using a struct to store variables in the data EEPROM that are validated by a CRC, and I would like to include the CRC as a member of that same struct. Since the old CRC value must not be used in the computation of the new one, I would like to make sure that the crc member is located at the end of the struct in memory. Can this be guaranteed by the compiler? Please advise. Thanks in advance!

struct ee_data_struct   // example structure that will contain 7 bytes
{
    u32 foo;            // Offset 0 ?
    u16 bar;            // Offset 4 ?
    u8 crc;             // Offset 6 ?
};

// This function returns the associated CRC value
u8 get_crc( u8* data, u8 size )
{
    u8 crc;
    // ...(magic happens)...
    return crc;
}

void main()
{
    u8 c;
    ee_data_struct at 0x4000 ee_data;

    ee_data.foo = 1;
    ee_data.bar = 2;
    ee_data.crc = get_crc( (u8*)&ee_data, sizeof( ee_data_struct ) - 1);
    // or maybe something like this, but you get the idea: 
    //      ee_data.crc = get_crc( &ee_data, sizeof( ee_data_struct ) - sizeof(((struct ee_data_struct *)0)->crc);
    //
    // Is it safe to expect this to be equivalent to the following ??
    //      ee_data.crc = get_crc( (u8*)0x4000, 6);
}

Replies
Post Information Post
+1
0
-1
January 25, 2011 - 10:23am
Raisonance Support Team

Hi Jacob,

I don't know what STM8 you are using, but you can use the "eeprom keyword to qualify your structure, which will automatically make it mapped to the EEPROM:

eeprom struct ee_data_struct   // example structure that will contain 7 bytes
{
    u32 foo;            // Offset 0 ?
    u16 bar;            // Offset 4 ?
    u8 crc;             // Offset 6 ?
};

If you want to map the CRC to a given eeprom address, for example to address 0x1020, you can add the "at 0x1020" qualifier like this:

at 0x1020 eeprom struct ee_data_struct   // example structure that will contain 7 bytes
{
    u32 foo;            // Offset 0 ?
    u16 bar;            // Offset 4 ?
    u8 crc;             // Offset 6 ?
};

Best Regards,

+1
0
-1
January 25, 2011 - 1:37pm
Guest

Bruno, thank you for your prompt reply. I am familiar with the eeprom keyword and at qualifier, but I'm afraid I was not clear enough with my question. I am able to locate the structure in EEPROM OK, but I want to ensure that a particular member of the structure is located at a known position (e.g. very beginning or very end) within the structure. In my example "foo" and "bar" are not part of the CRC. I want to compute a CRC based on "foo" and "bar" and save the result in CRC, but I want to do this using a fast algorithm that takes a contiguous chunk of data (e.g. pointer to start & number of bytes).

In other words, I want to do something like the following:

u8 data_array[128];
u8 crc;

crc = get_crc( data_array, 128 );

and I want to use a structure like this:
struct block_of_data
{
    u8 data_array[128];
    u8 crc;
} my_data;

my_data.crc = get_crc( my_data.data_array, 128 );

but I have many discrete members not just one array, so the above solution is not practical. Do I need to use a nested structure of some kind or is it OK to just list the CRC as the last member and assume that it will be the last byte stored in memory?
struct block_of_data_without_crc
{
    u32 foo;
    u16 bar;
};

struct complete_block
{
    block_of_data_without_crc data;
    u8 crc;
} my_complete_block;

my_complete_block.crc = get_crc( my_complete_block.data, sizeof(struct complete_block) );

Sometimes there are macros to discover what the offset of a struct member is, for example http://msdn.microsoft.com/en-us/library/dz4y9b9a.aspx. I'm not sure if something like this exists for the STM8S or if I should just look at the linker output, memory map, and memory dump via debugger.

+1
0
-1
January 25, 2011 - 1:54pm
Raisonance Support Team

Jacob,

C does not allow the compiler to reorder structure members to save space. It is also possible to tell most C compilers to "pack" the members of a structure to a certain level of alignment, e.g. "pack(2)" means align data members larger than a byte to a two-byte boundary so that any padding members are at most one byte long.

In the case of the Raisonance RKit-STM8, the RCSTM8 C compiler fully packs the structures, which means that there is no "hole" added in a structure by the compiler.

Your code can safely rely on this.
You can use the offsetof() macro defined in to ensure that a given structure member is at the right place (assert is your friend)

IMHO, it would be simpler to handle the computed CRC values in a safe place, with the actual data in another. This way you do not need specific structure handling.

Best Regards,

+1
0
-1
January 26, 2011 - 2:01am
Guest

Bruno,

Thanks for the detailed explanation. I wasn't sure if it was safe to assume that the compiler would not re-order struct members for alignment, optimization, or some other reason (I am not a compiler guru like you :D but I am learning). I looked at the offsetof macro, and I understand it now, thanks for showing me where I could find that.

Unfortunately, I do not know how to use the standard assert macros in embedded code yet. I am "cheating" using a preprocessor trick described here: http://www.jaggersoft.com/pubs/CVu11_3.html but would appreciate it if you would direct me to any appropriate documentation for using Raisonance's libraries/includes.

+1
0
-1
January 26, 2011 - 8:53am
Raisonance Support Team

Hi Jacob,

The preprocessor trick for "compile-time" assertions you cited above is excellent. I really approve the use of this in your case, as you do not really require dynamic verification of conditions (which is why the classic assert does).

Beware however that the trick used by Jaggersoft takes char takes up some RAM and ROM space, as it creates a global variable.
You can modify it as follows (using the extern qualifier) which will gives the same results at no cost.

#define COMPILE_TIME_ASSERT(expr)   \
    extern char constraint[expr]

A limitation of this method is that there is no "clean" error message to the user.
For example, on our RCSTM8 compiler the following error is output whenever a static assertion fails:

"*** ERROR P016 IN LINE 119 OF main.c : Invalid numeric constant"

This is OK for me, but this is far from meaningful for unexperienced users or programmers that are not used to assertions.
The workaround to this limitation is to activate static assertions only for advanced users, and remain with standard assertions for less experienced ones.

"Classic" assertions are part of the C standard.
You can use them very simply, as follows:

#include 

...
assert(sizeof(int) == 2);    /* Ensure we are using a 16-bit compiler */

At runtime, the expression to be asserted "sizeof(int) == 2" will be evaluated, and if it is false a message will be output. In the RCSTM8 implementation, a message will be output by the printf() function, using getchar(). By default this goes out to the UART (9600/N/8/1 settings), but you can redefine the putchar function so that it performs something else (check your compiler documentation :D)

The nice trick about "classic" assertions is that you can remove ALL their generated code just by recompiling your application with the NDEBUG preprocessor macro defined. This is a standard C feature.

Best Regards,