Gregor
Gregor

Reputation: 21

gcc changes address of mapped variable during optimization

I'm compiling C code for ARM stm32 microcontroller using arm-none-eabi-gcc v4.9.3 in an open source environment. The code runs fine without compiler optimizations (gcc -g -O0 ...).

When I enable even slightest optimizations (gcc -g -O1 ...), the address of linker defined variables are changed.

file memory.ld

register_cortexm3_ACTLR      = 0xe000e008;
...

MEMORY
{
                 rom (rx ) : ORIGIN = 0x08000000,  LENGTH = 256K
                 ram (rxw) : ORIGIN = 0x20000000,  LENGTH = 64K
        sram_bitband (rw ) : ORIGIN = 0x22000000,  LENGTH = 32768K
          peripheral (rw ) : ORIGIN = 0x40000000,  LENGTH = 1024K
  peripheral_bitband (rw ) : ORIGIN = 0x42000000,  LENGTH = 32768K
                sram (rwx) : ORIGIN = 0x60000000,  LENGTH = 1048576K
        ram_external (rwx) : ORIGIN = 0x60000000,  LENGTH = 1048576K
                code (rwx) : ORIGIN = 0x60000000,  LENGTH = 1048576K
     device_external (rw ) : ORIGIN = 0xa0000000,  LENGTH = 1048576K
  private_peripheral (rw ) : ORIGIN = 0xe0000000,  LENGTH = 1024K
       vendor_memory (rw ) : ORIGIN = 0xe0100000,  LENGTH = 523264K
}

SECTIONS
{
 /* Section for variables mapped onto registers */
 .peripherals (OVERLAY) :
 {
    . = ALIGN(4);
    *(.peripherals)
 } >peripheral

 .private_peripherals (OVERLAY) :
 {
    . = ALIGN(4);
    *(.private_peripherals)
} >private_peripheral

...

file register_cortexm3.h

typedef struct {
    unsigned DISMCYCINT  : 1;
    unsigned DISDEFWBUF  : 1;
    unsigned DISFOLD     : 1;
    unsigned reserved1   : 13;
    unsigned reserved2   : 16;
} __attribute__( ( __packed__ ) ) register_cortexm3_actlr_t;

...

file register_cortexm3.c

void check_same( volatile void* Address1, volatile void* Address2 ) {

    if ( Address1 != Address2 )
    { Assert_Halt_EC( ec_InvalidImplementation ); }
}

volatile register_cortexm3_actlr_t register_cortexm3_ACTLR   
__attribute__( ( section( ".private_peripherals" ) ) ); 

void register_cortexm3_prepare() {

    // checking for correct linker script settings
    check_same(&( register_cortexm3_ACTLR ), ( volatile void* ) 0xe000e008);
}

When optimizations are enabled, the above comparison fails because inside the check_same() function, the first argument is 0xe0000000 (the start address of its memory section) according to gdb:

Breakpoint 1, Assert_Halt_EC   (ErrorCode=ErrorCode@entry=ec_InvalidImplementation) at ttc-lib/ttc_basic.c:65
65      void Assert_Halt_EC( volatile ErrorCode_e ErrorCode ) { // block endless
(gdb) up
#1  0x08004440 in check_same (Address1=Address1@entry=0xe0000000, Address2=Address2@entry=0xe000e008) at ttc-lib/ttc_basic.c:58
58          { Assert_Halt_EC( ec_InvalidImplementation ); }
(gdb) up
#2  0x080034f6 in register_cortexm3_prepare () at ttc-lib/register/register_cortexm3.c:40
40          Assert_SameAddress( &( register_cortexm3_ACTLR ), ( void* ) 0xe000e008 );
(gdb) x &( register_cortexm3_ACTLR )
0xe000e008:     0x00000000

As you can see in the last gdb output line, gdb knows the correct address of register_cortexm3_ACTLR.

Is this a gcc bug or a feature? How to work around it?

Upvotes: 2

Views: 394

Answers (2)

Gregor
Gregor

Reputation: 21

To put it all together. This is how one can map a variable to a fixed address instead of using a pointer variable.

Advantages of mapped variables over pointers: - no RAM usage (each pointer consumes 4 bytes in 32-bit architecture) - no pointer dereferencing for register access (one less memory access) - no initialization code (pointer has to be inialized)

Let's assume we want a variable u32_t register_Foo mapped to address 0x12345678. Add a line to your linker script (here memory.ld):

memory.ld

register_Foo = 0x12345678;

Add a register struct and an extern declaration in your header file.

foo.h

typedef struct {
    unsigned DISMCYCINT  : 1;
    unsigned DISDEFWBUF  : 1;
    unsigned DISFOLD     : 1;
    unsigned reserved1   : 13;
    unsigned reserved2   : 16;
} __attribute__( ( __packed__ ) ) register_foo_t;

extern volatile register_foo_t register_Foo;

Access your register in your source code.

foo.c

#include "foo.h"

void foo() {
    register_Foo.DISFOLD = 1;
    ...
}

Thanx for getting this clear.

Upvotes: 0

Ross Ridge
Ross Ridge

Reputation: 39551

I'm not sure why optimization would any affect on this but the basic problem is that you've given two different and conflicting definitions of the symbol register_cortexm3_ACTLR. One definition, in your linker script, says that it's at address 0xe000e008 in no section, while the other, in your C code, says that it's at some offset of the .private_peripherals section.

You need to choose one of the other. If you want the variable at the address given in linker script, use an extern to reference it. Something like:

extern volatile register_cortexm3_actlr_t register_cortexm3_ACTLR;

Upvotes: 2

Related Questions