stm32 hal library warning with C++14 & above
The problem is -std=c++14
changing the semantics of a volatile
expression cast to (void)
, and introducing an apparently* unconditional warning for it, and a coder at ST trying to make "triple sure" that a register read would take place.
The definition of the UNUSED()
macro is
#define UNUSED(x) ((void)(x))
and __IO
is defined as
#define __IO volatile
Then the expansion of __HAL_RCC_GPIOB_CLK_ENABLE()
would be
do {
volatile uint32_t tmpreg;
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
/* Delay after an RCC peripheral clock enabling */
tmpreg = RCC->AHB2ENR & RCC_AHB2ENR_GPIOBEN;
((void)(tmpreg));
} while(0)
The delay and read-back of the register is recommended by various STM32 errata saying
A delay between an RCC peripheral clock enable and the effective peripheral enabling should be taken into account in order to manage the peripheral read/write to registers.
[...]
insert a dummy read operation from the corresponding register just after enabling the peripheral clock.
As all peripheral registers are of course declared as volatile
, a simple expression containing just the register in question would force a readback with the necessary wait states via the same peripheral bus, so this would suffice:
do {
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
/* Delay after an RCC peripheral clock enabling */
RCC->AHB2ENR;
} while(0)
the rest is presumably an overengineered workaround for some buggy compilers, but I'm yet to see one so broken that an expression with a volatile type would be optimized out.
There is that edge case however, with a volatile variable cast to (void)
, where the semantics have apparently changed in C++14.
Let's take this simple example
void x() {
volatile int t;
t=1;
((void)(t));
}
Arm gcc 7.2.1 invoked with -O3 -mcpu=cortex-m4 -mthumb -Wall -x c++ -std=c++11
would compile it to
x():
sub sp, sp, #8
movs r3, #1
str r3, [sp, #4]
ldr r3, [sp, #4]
add sp, sp, #8
bx lr
and the same code compiled with -std=c++14
is
x():
sub sp, sp, #8
movs r3, #1
str r3, [sp, #4]
add sp, sp, #8
bx lr
... and a warning:
<source>: In function 'void x()':
<source>:5:13: warning: conversion to void will not access object of type 'volatile int'
((void)(t));
~^~
Also notice the missing ldr
instruction in the second case. The variable is not accessed after the write with C++14.
My original question is intended to find out a solution, leaving the underlying ST driver intact. One possible solution would be to use the direct register access without going through the library provided convenient macro.
I'd suggest go ahead and avoid the library, IMHO HAL is better treated as a collection of examples or implementation suggestions.
*I couldn't find a way to disable it. That doesn't mean there is none.
There is code you can commit to your own repository to work around the issue and still compile the code with c++14.
/* Workaround for the broken UNUSED macro */
#include "stm32f3xx_hal_def.h"
#undef UNUSED
#define UNUSED(x) ((void)((uint32_t)(x)))
This needs to be added before any of the HAL headers are included. For me it was convenient to place into the stm32f3xx_hal_conf.h
file right after the module enable macros (i.e. #define HAL_WWDG_MODULE_ENABLED
line) but before the actual HAL headers are included.
I updated all my sources to #include "stm32f3xx_hal_conf.h"
instead of individual HAL headers.
This works because based on @berendi's excellent research the warning comes form the volatile
designation. By casting the value to something that's not volatile first, the new clause in the C++14 standard is eluded.