12

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.

Andrew Goedhart
  • 937
  • 9
  • 17
  • When I've needed this I've just done all of the `constexpr` calculations on straight integers and then `reinterpret_cast`ed them to pointers when I need to actually read/write them. – TartanLlama May 08 '17 at 11:27
  • 1
    I'm confused as to how that compiles. I thought that `reinterpret_cast` is not constexpr? Has this been changed? – Nir Friedman May 08 '17 at 11:28
  • Clang's error is more concise `note: reinterpret_cast is not allowed in a constant expression` – Henri Menke May 08 '17 at 11:29
  • 1
    What exactly do you need the pointers for at compile time? – TartanLlama May 08 '17 at 11:32
  • Yeah, that was going to be my next comment. Constant propagation is a pretty simple and reliable optimization. I would just forget about constexpr, write the code, look at the assembly, I'd be surprised if it doesn't just propagate the constant everywhere. `constexpr` is really more about when you need the result in a language-required constant context (like the size of a `std::array`) then for optimization purposes. – Nir Friedman May 08 '17 at 11:34
  • 4
    Possible duplicate of [Constexpr pointer value](http://stackoverflow.com/questions/10369606/constexpr-pointer-value) – Henri Menke May 08 '17 at 11:34
  • The above problem is to avoid having to use macros which are not allowed in many embedded c++ standards. Generally the answer is you shouldn' – Andrew Goedhart May 08 '17 at 12:34
  • Generally the answer is you shouldn't use constexpr but the problem is that the issue with that answer is that it does not take into account environments where this is standard practice via macros. The real question is there some way to avoid macros in c++ that guarantees the same sematics. I.e. guarantees embedding of the compile time constants in the code and does not sometimes lead to const variable that may take up valuable ram and also need to initialised by the startup code on bare metal implementations. Now the above implementation I listed in the question does this. – Andrew Goedhart May 08 '17 at 12:50
  • 1
    Constant propagation works sort of. In embedded environments constexpr is guaranteed to go into the .text section (FLASH) as a compile time constant. 'const' may end up in ram and get initialised during startup. For small processors with small RAMs thats a problem. – Andrew Goedhart May 08 '17 at 12:55
  • 1
    A large portion of bare metal (no OS) embedded code deals where register hardware access, so I would need to be looking at the disassembly all the time. I would rather use a language construct where the the compiler guarantees it is going to generate the right code. – Andrew Goedhart May 08 '17 at 13:05
  • Is it because you're only reading the data, once, that you don't need to declare it volatile ? – Johan Boulé Aug 12 '17 at 11:34
  • What this guy is doing here looks rather clean : https://youtu.be/zBkNBP00wJE?t=1060 – Johan Boulé Aug 12 '17 at 11:40
  • In gcc5/gcc6 pointer arithmetic with nullptr seems to work http://rextester.com/FRBMD33557 However, it doesn't compile in clang, so, I guess it is a bug in GCC – hutorny Nov 14 '17 at 19:35
  • gcc 5.4.1 from LPCXpresso actually does allow constexpr reinterpret_cast. Probably that's just a bug in that gcc version. – Dan Stahlke Apr 22 '18 at 19:35
  • @johan in this example code above it is a set of calibration constants embedded in a special section of the flash by the manufacturer. If it was register addresses then yes it would definitely need to be volatile. – Andrew Goedhart Jun 07 '18 at 09:40

2 Answers2

5

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.

dc42
  • 314
  • 3
  • 6
  • Thank you so much! I was missing how to do this when gcc changed after 4.9, and just resurrected my old code with this fix. – escrafford Feb 09 '19 at 05:33
1

As was already pointed out in the comments: reinterpret_cast is not allowed in a constant expression This is because the compiler has to be able to evaluate constexpr at compile time, but the reinterpret_cast might use runtime instructions to do its job.

You already suggested to use macros. This seems a fine way for me, because the compiler will definitely not produce any overhead. However, I would not suggest using your second way of hiding the reinterpret_cast, because as you said, a function is generated. This function will likely take way more memory away than an additional pointer.

In any case, the most reasonable way seems to me, to just declare a const pointer. As soon as you use optimizations, the compiler will just insert the memory location into your executable instead of using a variable. (See https://godbolt.org/g/8KnUKg )

jan.sende
  • 750
  • 6
  • 23