Casting the results of a volatile expression to void
Asked Answered
S

1

2

Note:

This is Not the same thing that has been asked many times. Yes I have read the many many posts about casting to void. None of those questions resulted in the answer I suspect is true here.


Background info:

Embedded C. This is specifically related to memory mapped volatile pointers. In other words, peripheral registers.

I came across the following line in a routine that involves writing to an I2C peripheral:

(void) I2C1->SR2;

I2C1 is #defined as a struct * to volatile memory.

So the result of this line is NOT to "avoid a compiler warning" as is the answer on all the searches I did here. It is in fact causing the compiler to read that register (since it's volatile) and then throw it away. This register has flags in it. The act of reading the register causes the flags to clear.

Now this is pretty important since the goal was to clear the flags not just avoid some compiler warning!

What has me worried however, is that at some level of optimization or maybe a different compiler, this code will get optimized away. That is my question:

Will this get optimized away or is there a way to guarantee it won't be optimized away?

I put all the relevant code together below:

#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region                                */
#define APB1PERIPH_BASE       PERIPH_BASE
#define I2C1_BASE             (APB1PERIPH_BASE + 0x5400)
#define I2C1                ((I2C_TypeDef *) I2C1_BASE)
typedef struct
{
  __IO uint16_t CR1;        /*!< I2C Control register 1,     Address offset: 0x00 */
  uint16_t      RESERVED0;  /*!< Reserved, 0x02                                   */
  __IO uint16_t CR2;        /*!< I2C Control register 2,     Address offset: 0x04 */
  uint16_t      RESERVED1;  /*!< Reserved, 0x06                                   */
  __IO uint16_t OAR1;       /*!< I2C Own address register 1, Address offset: 0x08 */
  uint16_t      RESERVED2;  /*!< Reserved, 0x0A                                   */
  __IO uint16_t OAR2;       /*!< I2C Own address register 2, Address offset: 0x0C */
  uint16_t      RESERVED3;  /*!< Reserved, 0x0E                                   */
  __IO uint16_t DR;         /*!< I2C Data register,          Address offset: 0x10 */
  uint16_t      RESERVED4;  /*!< Reserved, 0x12                                   */
  __IO uint16_t SR1;        /*!< I2C Status register 1,      Address offset: 0x14 */
  uint16_t      RESERVED5;  /*!< Reserved, 0x16                                   */
  __IO uint16_t SR2;        /*!< I2C Status register 2,      Address offset: 0x18 */
  uint16_t      RESERVED6;  /*!< Reserved, 0x1A                                   */
  __IO uint16_t CCR;        /*!< I2C Clock control register, Address offset: 0x1C */
  uint16_t      RESERVED7;  /*!< Reserved, 0x1E                                   */
  __IO uint16_t TRISE;      /*!< I2C TRISE register,         Address offset: 0x20 */
  uint16_t      RESERVED8;  /*!< Reserved, 0x22                                   */
  __IO uint16_t FLTR;       /*!< I2C FLTR register,          Address offset: 0x24 */
  uint16_t      RESERVED9;  /*!< Reserved, 0x26                                   */
} I2C_TypeDef;

Somewhere down in a function....

(void) I2C1->SR2;

Thanks in advance for any help. This site has been a great resource for newbies like me.

Shiekh answered 19/5, 2014 at 20:43 Comment(4)
Note that without the make/model of compiler and processor no 'answer` can be correct for you situation.Renny
Actually THAT is I guess the answer to the question. If it is compiler dependent then it's the wrong way to do it. So same question worded a little differently, "Is there a compiler independent method to achieve the same thing?" By the way the compiler is GCC.Shiekh
If a volatile object is accessed, that access cannot be optimized away by a conforming C compiler.Coward
Kevin's comment is incorrect. The use of the volatile keyword is the portable way to prevent a memory access from being optimized away.Ctn
C
3

The volatile keyword is the portable way to prevent memory accesses from being optimized away and/or reordered. It should be noted that the proper use of the volatile keyword makes casting the results of the expression to (void) unnecessary. For example, let's say I've typedef'd a structure and have an instance of that structure.

typedef struct 
{
    int a;
    int b;
}
    SomeStruct;

SomeStruct test;

The following code will cause the compiler to complain, "warning: expression result unused"

    SomeStruct *vptr = &test;
    vptr->a;

I can avoid the warning by casting the result to (void), but then the compiler is free to optimize away the read.

    SomeStruct *vptr = &test;
    (void) vptr->a;

However, if I declare the pointer as volatile and don't cast to (void), I won't get a warning and the compiler will not optimize away the read.

    volatile SomeStruct *vptr = &test;
    vptr->a;

The moral of the story is that if you are using the volatile keyword, you should not cast expressions to (void). That will only suppress warnings that would otherwise identify missing or incorrect use of the volatile keyword.

Ctn answered 19/5, 2014 at 23:48 Comment(3)
I would like to add some clarifications just for the record.Shiekh
I would like to add some clarifications just for the record. It may not be obvious but in the code snip-it above "__IO" is short for "volatile". Also I just compiled the "I2C1->SR2" without the cast to void and using the highest optimization setting and it still produces the read correctly. So thank you user3386109. It has been answered to my satisfaction. Yay!Shiekh
I like using a (volatile void)expression cast where expression is itself volatile, to signify that the expression is explicitly being evaluated with hardware side effects, that way it isn't confused with (void)expression for silencing unused variables. As far as I know casting volatile expressions to (volatile void) has no additional meaning than casting to (void) but it's nicely self-documenting.Hamate

© 2022 - 2024 — McMap. All rights reserved.