Skip to main content
Lars Beiderbecke
Senior III
April 28, 2019
Solved

Are HAL registers defined volatile, and will this prevent optimization?

  • April 28, 2019
  • 5 replies
  • 2581 views

Suppose I have the following code snippet:

void some_func() {
 int data = GPIOA->IDR;
 if (data == 0x01)
 func1();
 else if (data == 0x02)
 func2();
}

If optimization is turned on, I would expect the compiler to remove variable data and replace both occurrences of data with GPIOA->IDR -- which is clearly different from the original code.

Hence my question if GPIOA->IDR is defined as volatile. And if it is, is this sufficient to prevent this "optimization", or does variable data need to be volatile as well?

(My IDE would not jump to the declaration of GPIOA->IDR.)

This topic has been closed for replies.
Best answer by Chris1

Data structure element definitions that access peripheral registers typically look like:

 __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */

where __IO is #defined as volatile.

5 replies

Tesla DeLorean
Guru
April 28, 2019

Why would it replace both? data isn't volatile.​ It will also likely be held in a processor register and not memory. If data were volatile it still wouldn't cause it to reload the content from GPIO IDR but might force the value to always be held in a RAM variable so a pointer access from an IRQ or DMA would be able to change it.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
Tesla DeLorean
Guru
April 28, 2019

What the optimizer can't do is fold "efficient" stupidity like this :

RCC->PLLCR &= ~0x00000012;

RCC->PLLCR |= 0x00000034;

RCC->PLLCR &= ~0x001200;

RCC->PLLCR |= 0x00003400;

RCC->PLLCR &= ~0x00120000;

RCC->PLLCR |= 0x00340000;

RCC->PLLCR &= ~0x12000000;

RCC->PLLCR |= 0x34000000;

Where as this will fold to a handful of instructions and is scoped to use a processor register.

uint32_t tmp = RCC->PLLCR;

tmp &= ~0x00000012;

tmp |= 0x00000034;

tmp &= ~0x001200;

tmp |= 0x00003400;

tmp &= ~0x00120000;

tmp |= 0x00340000;

tmp &= ~0x12000000;

tmp |= 0x34000000;

RCC->PLLCR = tmp;

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
S.Ma
Principal
April 28, 2019

Very good example Clive.

S.Ma
Principal
April 28, 2019

I use IAR and most of my global variables are not volatile.

Also if the global variables are declared in a different C file, compiler should avoid optimizing too much.

Intead of cutting corners, implement test code, go debug mode and look at the assembly.

You should learn faster than waiting for this post to end.

turboscrew
Senior III
April 30, 2019

Basically, 'volatile' tells the compiler that the contents of a variable (registers are also seen as variables by the compiler) can change without the compiler seeing it. That is: the compiler can't trust that the value stays the same if compiler doesn't see a piece of code that could change it, but it has to evaluate all accesses. 'Volatile' is used when HW or other thread/process/module can change the data. In C compilation is done file by file, and only linker sees the whole code "at once". The symbols are, however. collected from all files.

Lars Beiderbecke
Senior III
May 7, 2019

I realize that. Hence my question if GPIOx->IDR is volatile, since then the compiler cannot optimize by replacing both occurrences of data with GPIOA->IDR.

At the time I wasn't sure if data needed to be volatile as well to prevent this substitution, but now I realize no, the substitutee being volatile is sufficient for this.

(And then there are the other people telling my that the compiler would never do this substitution for any kind of "int data = X" anyway.)

Chris1
Chris1Best answer
Associate II
May 7, 2019

Data structure element definitions that access peripheral registers typically look like:

 __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */

where __IO is #defined as volatile.

S.Ma
Principal
May 7, 2019

By the way, as we are discussing GPIOs, BSSSR register is quite handy to minimize Read/Modify/Write operations when different functions share different output pins of the same port.

That avoids using the OUT register directly with RMW operation.

Lars Beiderbecke
Senior III
May 7, 2019

Thanks, I know about BSRR; this was just an example by Clive, and has nothing to do with my question.

Pavel A.
Super User
May 7, 2019

There's also ARM specific reordering, See this PDF page 7 for example. It is from Broadcom, Does STM32 have similar quirks?

-- pa