Search code examples
c++castingembeddedconstinit

How I place a global variable at a compile-time known address?


I'm writing code for a specific stm32 chip with memory mapped peripherals. I happen to know that one of the GPIO ports, GPIOA, is controlled by several 32 bit ints located starting at address 0x40018000. How can I express this information at compile time?

My initial attempt looked like this:

struct Gpio {
    volatile uint32_t crl;
    volatile uint32_t crh;
    // ...
};

constinit Gpio& GPIOA = *reinterpret_cast<Gpio*>(0x40018000);

This seems like a sensible way to say "The reference GPIOA refers to an object located at memory address 0x40018000

Unfortunately this does not work:

error: 'constinit' variable 'GPIOA' does not have a constant initializer
error: 'reinterpret_cast' from integer to pointer

What options are available to me? How can I spell a specific numeric memory address at compile time?

Prior research:


Solution

  • It turns out there is a straightforward way to place a global variable at a specific address.

    It is possible to directly set the address of a symbol using linker scripts. In C++, simply declare the variable like so:

    extern Gpio GPIO;
    

    Do not define this variable in any .cpp file.

    Then, in the linker script, give it an address:

    GPIO = 0x40018000;
    

    When linking, tell the linker to use your script using the -T option.

    Your program should now link without any undefined reference errors. The symbol will refer to the memory location you specified. One limitation is there will not be any initialization for this value, but this is desirable for a memory mapped register.