10
votes

I posted the same question in the STM32 community forum as well, but didn't receive an answer.

I am using stm32 HAL library in a project with C++14 enabled. It issues me the following warning which I can't get rid of.

../platform/stm32/l4/STM32L4xx_HAL_Driver/Inc/stm32l4xx_hal_rcc.h:735:57:

warning: conversion to void will not access object of type 'volatile uint32_t {aka volatile long unsigned int}' UNUSED(tmpreg); \

This happens, when a call to __GPIOX_CLK_ENABLE() or __HAL_RCC_GPIOX_CLK_ENABLE is called.

Has anyone been able to get rid of the above warning leaving the HAL source code intact.

Or any ideas as what is possible to be done.

The current warning level is -Wall.

I've experienced the above issue with both l4 & f4 series code.

An Example code:

int main(void)
{
    HAL_Init();

    __GPIOB_CLK_ENABLE();
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.Pin = GPIO_PIN_7;

    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);

    for (;;)
    {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_Delay(500);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
        HAL_Delay(500);
    }
}

The culprit is __GPIOB_CLK_ENABLE(), which gets expanded to the following(in ST drivers).

#define __HAL_RCC_GPIOB_CLK_ENABLE()           do { \
                                                 __IO uint32_t tmpreg; \
                                                 SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN); \
                                                 /* Delay after an RCC peripheral clock enabling */ \
                                                 tmpreg = READ_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN); \
                                                 UNUSED(tmpreg); \
                                               } while(0)

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.

Thank you in advance.

3
I did some research and the reason for this warning is that the UNUSED macro, which is part of the mentioned macros, cast a volatile reference to void. It is not related to C++14 nor -Wall, but all g++ versions give the same diagnostic. The reason why can be found in the linked duplicate. The solution would be not to use volatile references, which is fishy practice when writing hardware-related code - use volatile pointers instead. Perhaps you are using a reference by accident?Lundin
The warning is not issued in C++11. I can successfully compile the same code with C++11 without getting any warning with -Wall. It is definitely not all g++ compiler versions. That is the reason behind this question.aep
It is definitely not a duplicate. I would urge you to download the STM32 CubeMX HAL source code and compile it both in C++11 & C++14. The warning becomes evident in C++14 but never in C++11.aep
I was able to reproduce it down to C++03 by simply casting any volatile reference to void. So this has nothing to do with the compiler version. There must be something in your caller code that behaves differently in C++14. Please edit your question with a MCVE that contains the caller code giving the warning.Lundin
I'll re-open the question for now, but I don't believe it can be answered without an example. It may very well be that the problem lies in the ST drivers, though as I understand it these are written in pure C? Where does the reference come from?Lundin

3 Answers

9
votes

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.

4
votes

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.

0
votes

As @oliv mentioned in reply to @berendi's answer, the root cause appears to be a bug in GCC, which has been fixed in more recent versions. The warning went away for me when I upgraded to the "Version 9-2019-q4-major" toolchain (GCC 9.2).

Also, for the STM32G0 series, ST changed the definition of UNUSED to:

#define UNUSED(X) (void)X      /* To avoid gcc/g++ warnings */

which makes the warning go away for earlier versions of the compiler.