5.1.33 · D1C Programming

Foundations — volatile keyword — preventing optimization of hardware registers

1,958 words9 min readBack to topic

Before you can understand why volatile exists, you must be fluent in the pieces the parent note quietly assumes: what memory is, what a register is, what the compiler does when it "optimises", what a pointer is, and what an interrupt is. This page builds every one of them from nothing.


1. Memory = a long street of numbered boxes

Everything starts here. Your computer's RAM is nothing more than a huge row of tiny boxes. Each box holds one byte (a number 0–255) and each box has an address — a fixed number naming its position, like a house number on a street.

Figure — volatile keyword — preventing optimization of hardware registers

Why the topic needs this: a "hardware register" is literally a box at a special address that a physical chip is wired to. Without the idea of an address, (volatile uint32_t *)0x40021000 is gibberish.


2. A variable = a named box

When you write int flag = 0;, C picks one box (well, a few adjacent boxes) in memory, glues the name flag to it, and puts 0 inside.

Why the topic needs this: hardware registers have an exact width. A UART status register is often uint8_t (one byte of flags); a peripheral control register is often uint32_t. Getting the width wrong reads the wrong number of boxes.


3. A CPU register — the other meaning of "register"

Careful: the word "register" is used two ways, and confusing them is the whole bug.

Figure — volatile keyword — preventing optimization of hardware registers

Why the topic needs this: the parent note's infinite loop happens because the compiler kept flag in a CPU register and stopped re-reading the RAM box. You cannot understand the bug without both meanings.


4. The compiler and "optimization"

These are controlled by optimization levels: Compiler Optimization Levels (-O0 -O2). At -O0 the compiler barely optimises (bugs may hide); at -O2 it optimises aggressively (bugs appear).

Figure — volatile keyword — preventing optimization of hardware registers

Why the topic needs this: volatile is defined as "the qualifier that disables these three optimizations for one variable." No optimization → no need for volatile. The keyword only makes sense against this backdrop.


5. Pointers and the * symbol

The register access (volatile uint32_t *)0x40021000 is pure pointer notation. Let's earn it.

Figure — volatile keyword — preventing optimization of hardware registers

Why the topic needs this: the entire "WHERE the qualifier sits" section of the parent turns on right-to-left pointer reading. Without pointers, half the topic is unreadable.


6. Interrupts and ISRs — the invisible writer

Why the topic needs this: the classic hang bug is caused by an ISR. Also feeds the crucial caveat that volatile is not synchronization — for that you need _Atomic and memory barriers.


7. Memory-mapped IO — where hardware is an address


Prerequisite map

Memory = numbered boxes

Variable = named box

Address and hex 0x

Types uint8 uint32

CPU register vs hardware register

Compiler optimization: caching

Optimization levels O0 O2

Pointers and the star operator

Type qualifiers volatile and const

Memory-mapped IO

Interrupts and ISR

Invisible writer

volatile keyword


Equipment checklist

Cover the right side and answer aloud. If any is shaky, re-read that section above.

What is a memory address, and why is it usually written like 0x40021000?
A number naming one box in RAM; written in hexadecimal (base-16) because round binary addresses look tidy in hex.
How many bytes does a uint32_t occupy and what range does it hold?
4 bytes (4 boxes); values 0 to .
What is the difference between a CPU register and a hardware register?
A CPU register is a fast scratch slot inside the processor; a hardware register is a box in memory at a fixed address wired to a peripheral chip.
Name the three optimizations that matter for volatile.
Caching a value in a CPU register, dead-store elimination, and reordering of accesses.
What does the * do in the expression *p versus in the type uint32_t *?
In the expression it dereferences (fetches the box p points to); in the type it declares "pointer to".
What does *(volatile uint32_t *)0x40021000 mean in plain words?
The volatile 32-bit hardware register living at address 0x40021000, read freshly each access.
Why can an ISR change a variable "invisibly" to the compiler?
The ISR runs asynchronously; the compiler cannot see from inside a loop that the ISR will fire and modify the variable.
What makes a memory-mapped read different from a normal RAM read?
It can have a side effect (advancing a FIFO, clearing a flag), so it must not be removed or duplicated by the compiler.
Is const volatile a contradiction?
No — const means your code won't write it, volatile means hardware can change it; together they describe a read-only-to-you, hardware-updated register.

Recall Ready to proceed?

If you can answer every checklist item, you now own every symbol the parent note uses. Return to the main topic and the caching bug will read like a story you already know.