Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set a constexpr pointer to a physical Address

In embedded programming you often need to set pointers that point to a physical address. The address is non relocatable and fixed. These are not set by the linker as typically they represent registers or in this case calibration data located at a predetermined address in OPT memory. This data is set when the device is first tested in production by the chip manufacturer.

so the first attempt was:

static constexpr uint16_t *T30_CAL = reinterpret_cast<uint16_t *>(0x1FFFF7B8u);

But that leads to following warning / error under GCC and is 'illegal' according to the standard (c++ 14) .

..xyz/xxxx/calibration.cpp:23:40: error: reinterpret_cast from integer to pointer

Now i can fudge it by

constexpr uint32_t T30_ADDR = 0x1FFFF7B8u;
static constexpr inline uint16_t *T30_CAL(){ 
  return reinterpret_cast<uint16_t *>(T30_ADDR);
}

which compiles without warnings but ......

I suppose GCC can optionally compile this to a function instead of a constexpr, though it does inline this every time.

Is there a simpler and more standards compliant way of doing this ?

For embedded code these definitions are required all the time, so it would be nice if there was a simple way of doing this that does not require function definitions.

The answers to the previous questions generally resulted in a answer that says this is not allowed in the standard and leaves it at that.

That is not really what I want. I need to get a compliant way of using C++ to generate compile time constant pointers to a fixed address. I want to do it without using Macros as that sprinkles my code with casts that cause problems with compliance checkers. It results in the need to get compliance exceptions in multiple places rather then one. Each exception is a process and takes time and effort.

Constexpr guarantees, on embedded systems, that the constant is placed in .text section (flash) whilst const does not. It may be placed in valuable ram and initialised by the .bss startup code. Typically embedded devices have much more flash then RAM. Also the code to access variables in RAM is often much more inefficient as it typically involves at least two memory access on embedded targets such as ARM. One to load the variable's RAM address and the second to load the actual constant pointer value from the variable's location. Constexpr results in the constant pointer being coded directly into the instruction stream or results in a single constant load.

If this was just a single instance it would not be an issue, but you generally have many different peripherals each controlled via there own register sets and then this becomes a problem.

A lot of the embedded code ends up reading and writing peripheral registers.

like image 751
Andrew Goedhart Avatar asked May 08 '17 11:05

Andrew Goedhart


1 Answers

Use this instead:

static uint16_t * const T30_CAL = reinterpret_cast<uint16_t *>(0x1FFFF7B8u);

GCC will store T30_CAL in flash on an ARM target, not RAM. The point is that the 'const' must come after the '*' because it is T30_CAL that is const, not what T30_CAL points to.

like image 96
dc42 Avatar answered Oct 02 '22 14:10

dc42