I work with microcontrollers, where sometimes there are machine registers that have actions that occur when reading a register. Yes, this is not a typo, there are many registers that cause actions when they are written, but in some cases, if you read the register, something happens.
The most common example of this is a UART receive register connected to one end of a FIFO; for example, let's say there is RXDATA. When you read RXDATA, it pulls one byte from the FIFO, and the next time you read RXDATA, it will get the next byte.
Is there enough information in volatileto make the compiler understand that there may be side effects from reading?
Example snippet in C:
#include <stdint.h>
#include <stdbool.h>
volatile uint8_t RXDATA;
bool check_bits_1()
{
const uint8_t rxdata = RXDATA;
return (rxdata & 1) && ((rxdata & 0x80) == 0);
}
bool check_bits_2()
{
return (RXDATA & 1) && ((RXDATA & 0x80) == 0);
}
bool check_bits_3()
{
const bool bit_0_is_1 = RXDATA & 1;
const bool bit_7_is_0 = (RXDATA & 0x80) == 0;
return bit_0_is_1 && bit_7_is_0;
}
If I ignore the C standard and pretend that the compiler does exactly what I think I'm asking for it to do ( DWIM ), then my intuition is that these three functions have different behavior:
- In the first case, we read
RXDATAonce, so we pull out one FIFO byte, and then do some math on it. - In the second case, we read
RXDATAonce or twice (because it &&has short circuit behavior), doing the math directly by the value of the register, so we can either pull one or two bytes from the FIFO, and this has the wrong behavior. RXDATA, FIFO, .
RXDATA volatile, .
C , volatile , ? , C?