# Volatile Memory

### Volatile Memory

Most memory in a program behaves normally.

If a value is written:

```zig
x = 10;
```

and then immediately overwritten:

```zig
x = 20;
```

the compiler may remove the first write because it has no observable effect.

This is an important optimization.

But some memory locations are special.

A write to a hardware register may start a device.

A read from a status register may clear a flag.

A memory location shared with another processor may change independently of the current thread.

In such cases, reads and writes must happen exactly as written in the program.

This is called volatile memory.

A volatile pointer is declared like this:

```zig
const reg: *volatile u32 = @ptrFromInt(0x4000_0000);
```

The address:

```zig
0x4000_0000
```

might represent a device register.

The type:

```zig
*volatile u32
```

means:

This pointer refers to a `u32`.

Reads and writes through the pointer are observable side effects.

The compiler must not remove, merge, or reorder them incorrectly.

Writing to the register:

```zig
reg.* = 1;
```

must generate a real store instruction.

Reading from the register:

```zig
const value = reg.*;
```

must generate a real load instruction every time.

Without `volatile`, the compiler might cache the value in a register or eliminate repeated accesses.

Volatile memory is mainly used for:

| Use | Example |
|---|---|
| Memory-mapped I/O | Hardware registers |
| Device drivers | UART, timers, GPUs |
| Embedded systems | Microcontrollers |
| Shared external memory | DMA buffers |

Ordinary program variables should almost never be volatile.

For example:

```zig
var counter: volatile u32 = 0;
```

is usually wrong.

Volatile does not make code thread-safe.

Volatile does not provide synchronization.

Volatile does not replace atomics.

If multiple threads access shared memory concurrently, use atomic operations or synchronization primitives.

For example:

```zig
const std = @import("std");

var counter: u32 = 0;

pub fn main() void {
    _ = &counter;
}
```

Making `counter` volatile would not prevent data races.

Use atomics instead.

```zig
const std = @import("std");

var counter: u32 = 0;

pub fn increment() void {
    _ = @atomicRmw(u32, &counter, .Add, 1, .seq_cst);
}
```

Volatile controls compiler optimization of memory access. Atomics control synchronization between threads and processors.

The two concepts solve different problems.

Volatile pointers are often combined with packed structures.

```zig
const Registers = packed struct {
    control: u32,
    status: u32,
};

const regs: *volatile Registers =
    @ptrFromInt(0x4000_0000);
```

This maps a struct directly onto hardware registers.

Then:

```zig
regs.control = 1;
```

writes to the hardware control register.

And:

```zig
const s = regs.status;
```

reads the hardware status register.

Such code depends completely on the exact memory layout.

This style is common in kernels, firmware, bootloaders, and embedded systems.

Volatile can also appear on many-item pointers.

```zig
const buffer: [*]volatile u8 =
    @ptrFromInt(0x5000_0000);
```

Each byte access is treated as observable.

A volatile access affects only the operation itself.

```zig
const a = reg.*;
const b = reg.*;
```

These are two distinct loads.

The compiler cannot assume the second value equals the first.

This matters because hardware can change the register between accesses.

Volatile does not freeze surrounding code motion completely. It only constrains the accesses themselves.

Precise memory ordering between CPUs requires atomics and fences.

A useful rule is:

| Situation | Tool |
|---|---|
| Hardware register | `volatile` |
| Thread synchronization | atomics |
| Shared mutable state | mutexes or atomics |
| Ordinary variables | plain memory |

Volatile should remain local and explicit.

Good style:

```zig
const uart: *volatile u8 =
    @ptrFromInt(0x1000_0000);
```

Poor style:

```zig
var everything: volatile SomeStruct = ...;
```

The first marks exactly the memory that requires special handling. The second spreads volatile semantics across unrelated operations.

Most Zig programs never need volatile memory. When it appears, the program is usually interacting directly with hardware or low-level runtime code.

Exercise 19-17. Declare a `*volatile u32` using `@ptrFromInt`.

Exercise 19-18. Explain why volatile does not make a counter thread-safe.

Exercise 19-19. Write a packed struct representing two device registers.

Exercise 19-20. Explain why repeated reads from a volatile pointer cannot be optimized into one read.

