I don't know why but I see the same odd behaviour sometimes in the debugger environment. Look at this code (function):
static void I2C_TransferConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t Size, uint32_t Mode, uint32_t Request)
{
uint32_t tmpreg = 0;
/* Check the parameters */
assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance));
assert_param(IS_TRANSFER_MODE(Mode));
assert_param(IS_TRANSFER_REQUEST(Request));
/* Get the CR2 register value */
tmpreg = hi2c->Instance->CR2;
/* clear tmpreg specific bits */
tmpreg &= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
/* update tmpreg */
tmpreg |= (uint32_t)(((uint32_t)DevAddress & I2C_CR2_SADD) | (((uint32_t)Size << 16 ) & I2C_CR2_NBYTES) | \
(uint32_t)Mode | (uint32_t)Request);
/* update CR2 register */
hi2c->Instance->CR2 = tmpreg;
}
And take a look at this picture of code in the debugger environment:
When I run it, at first it goes to line 4057 and then it goes to line 4054! Why?
As you can see, both lines are only several variable + several operands! That's all!
Best Answer
The C standard specifies that reads and writes of volatile variables and calls to library routines are considered "observable operations". A compiler is required to produce an executable which will produce all observable operations in the sequence that the source file directs, but the generated code is change around anything else in the code as it sees fit provided only that all observable operations happen in the same sequence.
For example, if code computes a value, performs an observable operation which does not make use of the value, and then performs one which does, the compiler may decide to perform the first observable operation before computing the value needed for the second. Given code like:
if the value
x
is never used after the above code, and if variablesy
andz
are not volatile, a compiler might legitimately decide that the code would be more efficient assince in the situation where
volatile1
is zero it takes less time to execute, and ifvolatile1
is non-zero it doesn't take any longer. Of course, the whole purpose of doing the assignment before testingvolatile
may have been to minimize the elapsed time between readingvolatile1
and writingvolatile2
, but if that's the case if may be helpful to do something like:and then...
That would ensure that an observable operation using the computed value would take place before the observable read of
volatile1
. Some compilers may offer a means of "observably using" a value without having to actually store it anyplace, but I don't know of any standard means of doing that.