volatile exact semantics
The frame arrived.
The task never saw it.
At -O0 everything works. In the optimized release, the 10 ms communication task occasionally misses the flag written by the CAN Rx ISR.
What does volatile mean?
The value may change outside the current code flow. The compiler must perform the required access to the real object instead of trusting an older value kept in a CPU register.
Only where the value changes asynchronously.
CAN, LIN or timer ISR updates a status read by a cyclic task.
Controller hardware changes a bit while software polls its fixed register.
DMA updates a descriptor independently of normal CPU execution.
Use OS resources, atomics or queues when the real problem is a race or event loss.
One shared object. Two execution contexts.
The object is zero-initialized in .bss RAM. The qualifier changes access rules—not its storage area.
static volatile uint8_t CanRxPending; /* .bss RAM */
void Can_RxIsr(void)
{
CanRxPending = 1u;
}
void Com_10msTask(void)
{
if (CanRxPending != 0u)
{
CanRxPending = 0u;
ProcessCanFrame();
}
}CAN Rx ISR → 10 ms communication task
Compare the generated runtime behaviour with and without volatile.
10 ms task loads CanRxPending from .bss RAM.
Useful—but not free.
- Required reads and writes remain observable.
- Hardware polling matches the intended runtime model.
- Assembly retains the expected load/store evidence.
- Atomicity, races, lost events or ordering.
- Multicore cache coherency.
- Poor architecture hidden by excessive qualifiers.
Debug the evidence—not the keyword.
Symptom: CAN frame is present, ISR runs, but the optimized task does not process it.
- Reproduce exactly
Replay the same CAN stimulus and preserve timestamps.
- Prove the producer
Confirm ISR entry and RAM write at the flag address.
- Find the first mismatch
RAM becomes 1, but task control flow behaves as 0.
- Compare assembly
Check -O0 versus release: repeated load or one cached read?
- Select the correct fix
Use volatile for visibility; counter, queue, atomic or OS resource for stronger guarantees.
- Stress the correction
Repeat under CAN burst, timing variation and optimized build.
Two CAN frames arrive before the 10 ms task runs.
A Boolean flag can represent only “pending” or “not pending.” Which design preserves both events?
Do not stop at “it works”
Inputs, addresses, state, timing and generated instructions.
Expected contract against the first different runtime boundary.
Repeat the scenario and demonstrate the correction under realistic conditions.
What is the primary guarantee of volatile?
Think through volatile exact semantics
Ask for an explanation, an automotive example or a question that tests your understanding.
Checking mentor availability…