CodeBlanks Logo Icon CodeBlanks

Memory-mapped I/O

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

main.cpp
#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

main.cpp
#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

Question
volatile std::uint32_t* ____ = reinterpret_cast<volatile std::uint32_t*>(0x40021000);
Answer
volatile std::uint32_t* reg = reinterpret_cast<volatile std::uint32_t*>(0x40021000);

Explanation: reg points to the hardware register.

2 What does volatile prevent?

main.cpp
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

All lessons in Expert