Memory-mapped I/O
- C++ course
- Expert
- Lesson 32/47
- 5 min
Learn memory-mapped I/O and volatile usage.
Memory-mapped I/O
Memory-mapped I/O (MMIO) is the technique where hardware registers are mapped to memory addresses. Writing to a specific address controls hardware; reading from it gives hardware state. Example: 0x40021000 might be a GPIO register.
In C++ these addresses are accessed with volatile std::uint32_t*: volatile std::uint32_t* reg = reinterpret_cast<volatile std::uint32_t*>(0x40021000);. volatile tells the compiler not to optimize this access. Write with *reg = 0x1;, read with *reg.
Why volatile is needed
The compiler optimizer may cache a value if it reads a variable repeatedly. But a hardware register may return different values on each read (a status flag, for example). So volatile forces every read to go to memory.
Likewise repeated writes may be optimized; the optimizer thinks only the last write matters. But every write to hardware is a side effect; it must not be skipped. volatile keeps these writes too. That is why MMIO requires volatile.
Practice Write an MMIO simulation
Define a global std::uint32_t storage = 0;. Then write volatile std::uint32_t* reg = reinterpret_cast<volatile std::uint32_t*>(&storage);. In main write *reg = 0x1; and std::cout << *reg;.
Starter code
#include <iostream>
#include <cstdint>
// Define the storage variable and reg pointer here
int main() {
// Write 0x1 to reg and read reg, print it
return 0;
}Sample solution
#include <iostream>
#include <cstdint>
std::uint32_t storage = 0;
volatile std::uint32_t* reg = reinterpret_cast<volatile std::uint32_t*>(&storage);
int main() {
*reg = 0x1;
std::cout << *reg;
return 0;
}Expected output
1
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Complete the volatile pointer
volatile std::uint32_t* ____ = reinterpret_cast<volatile std::uint32_t*>(0x40021000);
volatile std::uint32_t* reg = reinterpret_cast<volatile std::uint32_t*>(0x40021000);
Explanation: reg points to the hardware register.
2 What does volatile prevent?
volatile std::uint32_t* reg = ...;
*reg = 0x1;
*reg = 0x2; // two writes- Class creation
- Memory leaks
- Run-time errors
- Compiler read/write optimization Correct answer
Explanation: volatile forces every read and write to go to memory.
3 Match each piece to its role
| Term | Meaning |
|---|---|
volatile |
Every access goes to memory |
MMIO |
Map hardware to memory |
reinterpret_cast |
Cast address to pointer |
0x40021000 |
Hardware register address |