Embedded

Memory-mapped I/O

By CourseTron Team ·  9 min read ·  Updated Oct 2026 💬 𝕏 in f
Table of Contents
    🖨  Download / Print this guide (PDF)

    In short: Memory-mapped I/O (MMIO) places hardware peripheral registers into the same address space the CPU uses for RAM, so ordinary load and store instructions read and write device registers. It contrasts with port-mapped I/O, which uses a separate address space and dedicated instructions, and it is the dominant scheme on ARM, RISC-V, and most modern SoCs.

    What Is memory mapped io?

    Memory mapped io is a hardware/software convention in which the control and data registers of peripheral devices are assigned fixed addresses inside the processor's unified physical address space. From the CPU's point of view there is no special "device" transaction: the same instructions that touch DRAM, an ordinary LDR/load or STR/store, also reach a UART, a timer, a GPIO block, or an interrupt controller. The address bus decode logic routes some ranges to the memory subsystem and other ranges to peripheral bus bridges.

    Because the interface is just addresses, software accesses a register through a pointer. In C you typically declare a pointer to a volatile integer at the register's address and dereference it. The volatile qualifier is essential: it tells the compiler the location can change outside the program's control and must not be cached in a CPU register or optimised away, so every read and write produces a real bus cycle.

    Peripherals share the CPU's address spaceCPUload / storeAddressdecodeDRAM (memory)UARTTimerGPIOCourseTron · coursetron.com
    Device registers sit at fixed addresses in the same physical address space as RAM; the decode logic routes each range.

    Why MMIO Matters and How It Works

    MMIO matters because it keeps the instruction set small and the hardware uniform. A RISC architecture that already has fast, pipelined load/store units gets device access "for free" without adding I/O-specific opcodes. The memory management unit (MMU), caches, and protection mechanisms can all be reused: an operating system can map a peripheral page into a driver's virtual address space and deny it to everyone else, giving per-device access control with the same page tables that protect normal memory.

    Mechanically, a register access flows like this:

    1. The CPU issues a load or store to a peripheral address.
    2. The system interconnect (for example an AMBA AXI or AHB fabric) decodes the address and selects the target peripheral instead of the memory controller.
    3. The peripheral latches the written value into a control register, or returns the contents of a status/data register.
    4. Side effects may occur: reading a UART data register can pop a byte from its FIFO; writing a "clear" bit can acknowledge an interrupt.
    One register access, step by stepCPUInterconnectAMBA AXI / AHBPeripheralregister1. Load or storeto a device address2. Fabric decodesselects the peripheral3. Register writtenor read backCourseTron · coursetron.com
    A register access: the CPU issues a load or store, the interconnect decodes the address, and the peripheral register responds.

    These side effects are exactly why caching and reordering must be disabled for device regions. Peripheral address ranges are marked as device or strongly-ordered memory in the MMU attributes so the core does not merge, reorder, speculate, or cache those accesses.

    Key fact: On x86, MMIO and port-mapped I/O coexist: MMIO uses ordinary instructions over the 64-bit physical address space, while port-mapped I/O uses the separate 16-bit (0x0000–0xFFFF) I/O space reached only by the dedicated IN and OUT instructions. ARM and RISC-V have no such separate I/O space, so they rely entirely on MMIO.

    MMIO vs Port-Mapped I/O

    The two classic schemes differ in where device registers live and how they are reached:

    • Memory-mapped I/O: devices share the one physical address space with RAM. Any memory instruction, and therefore any addressing mode, pointer arithmetic, or compiler construct, works on a register. No special opcodes are needed.
    • Port-mapped I/O (isolated I/O): devices occupy a separate, smaller I/O address space accessed only by distinct instructions (x86 IN/OUT). A dedicated control line tells the bus which space a transaction targets. This keeps the memory map uncluttered but needs extra ISA support and generally cannot use rich addressing modes.
    MMIO vs port-mapped I/OMemory-mapped I/OPort-mapped I/ORAMDevice registersRAM (memory space)Separate I/O space0x0000-0xFFFFOne address space, ordinary load/storeNo special opcodes neededDedicated x86 IN / OUT instructionsMemory map stays unclutteredCourseTron · coursetron.com
    Memory-mapped I/O shares one address space, while port-mapped I/O uses a separate space reached by IN and OUT.

    Trade-offs: MMIO consumes part of the usable address range, which mattered on 16- and 32-bit systems where address space was scarce; a large graphics aperture can eat hundreds of megabytes. Port-mapped I/O avoids that but limits you to a few opcodes and is harder to protect and virtualise. Modern 64-bit designs have enormous address spaces, so the address-consumption drawback is minor and MMIO wins on simplicity and MMU integration.

    Register access through pointers

    A driver usually defines a struct whose fields line up with the peripheral's register layout, then points it at the base address, so dev->DATA and dev->STATUS read as named fields. Each field is volatile, and the struct's layout must match the datasheet exactly, including reserved padding words at the right offsets. Width also matters: many peripherals require a 32-bit access and will fault or misbehave on a byte or half-word access to a word register.

    How MMIO Is Handled in Practice

    Real systems add a few more concerns that engineers must get right:

    • Memory barriers: volatile stops the compiler from reordering accesses, but the CPU and interconnect can still reorder at runtime. Drivers insert barrier instructions (ARM DMB/DSB) between a configuration write and the action that depends on it, or use barrier-wrapped accessors like Linux's readl()/writel().
    • Cacheability: device memory must be mapped non-cacheable. If a peripheral region were accidentally cacheable, a status read could return a stale cached value and a write might never reach the device until a flush.
    • Alignment and width: access each register at its native width and alignment, because partial-width writes to pack several control bits can trigger unintended read-modify-write behaviour on write-one-to-clear registers.
    • Read side effects: never read a register "just to check" if the read mutates state, such as clearing an interrupt flag or consuming a FIFO entry.

    In an operating system, a driver typically calls a mapping routine (ioremap() on Linux) to obtain a virtual pointer to the physical register block, because user and kernel code run under the MMU and cannot touch physical addresses directly. On bare-metal microcontrollers the physical address is used directly, which is why vendor headers (CMSIS for ARM Cortex-M) ship ready-made register structs.

    How Industry Tools and Hardware Handle MMIO

    At the SoC level the address map is defined in the interconnect IP. In the ARM ecosystem an AMBA AXI/AHB fabric decodes transactions and routes peripheral addresses to an APB bridge for slow registers. The map itself is usually described in a device tree (on Linux) or a vendor SVD/CMSIS file (on microcontrollers), so firmware and operating systems learn each peripheral's base address and interrupt without hard-coding magic numbers. PCI and PCIe devices expose MMIO through Base Address Registers (BARs); the OS enumerates each device and programs its BARs to place the device's register window somewhere in the physical map.

    Verification and silicon teams model these regions in RTL and in SystemVerilog/UVM register abstraction layers, where a register model mirrors the datasheet's offsets, access types (RW, RO, W1C), and reset values so tests can poke registers symbolically. Getting that model to match the hardware exactly is a core part of SoC design and validation.

    Learn this hands-on

    Real projects and industry tool flows, with certification.

    Explore CourseTron Courses →

    memory mapped io — Interview FAQ

    Why must MMIO register pointers be declared volatile in C?

    Because the value at a device address can change independently of the program, and reads or writes can have hardware side effects. volatile forces the compiler to perform every access literally, in program order relative to other volatile accesses, and to never cache the value in a CPU register or eliminate a "redundant" read. Without it, the optimiser may drop polling loops or reuse a stale value.

    What is the difference between memory-mapped and port-mapped I/O?

    Memory-mapped I/O puts device registers in the same address space as RAM, reachable by ordinary load/store instructions; port-mapped (isolated) I/O uses a separate address space accessed only by dedicated instructions such as x86 IN and OUT. MMIO is simpler and integrates with the MMU; port-mapped I/O saves address space but needs special ISA support.

    Why is volatile not enough, and when do you need a memory barrier?

    volatile constrains only compile-time ordering. The processor and bus can still reorder or buffer device accesses at runtime. When one access must complete before another, for example configuring a DMA descriptor before writing the "start" bit, you insert a memory barrier (ARM DMB/DSB) or use barrier-aware accessors so the hardware observes the accesses in the intended order.

    Why must peripheral regions be mapped non-cacheable?

    Caching assumes a location holds the last value written until overwritten. Device registers break that assumption: a status register changes on its own, and a write may trigger an action. If cached, reads could return stale data and writes could sit in the cache instead of reaching the device, so MMIO regions are marked device/non-cacheable in the MMU attributes.

    Related: a related topic · the VLSI Glossary · all courses.

    Advertisement