A Memory Management Unit (MMU) is a hardware block that translates virtual addresses generated by the CPU into physical addresses in memory, enabling virtual memory, memory protection, and process isolation. MMUs are found in application-class processors running operating systems such as Linux; most microcontrollers omit the MMU in favor of simpler or no memory protection hardware.
In practice
In embedded Linux systems running on application processors such as the Cortex-A series (e.g., NXP i.MX, Raspberry Pi's BCM2711, TI AM335x), the MMU is central to the OS memory model. Each process gets its own virtual address space, and the OS kernel configures page tables that the MMU walks on every memory access. This lets multiple processes coexist without corrupting each other's memory, and allows tricks like demand paging, shared memory regions, and memory-mapped files.
On ARMv7-A and ARMv8-A cores specifically, the MMU operates through a hierarchy of page tables stored in RAM, and the hardware page-table walker reads these structures automatically on a TLB (Translation Lookaside Buffer) miss, then caches the result in the TLB for subsequent accesses. (Other MMU architectures may differ in page-table format or walk behavior.) Getting page-table setup wrong -- incorrect attributes, stale TLB entries after a table update, or cache/MMU ordering violations -- produces hard-to-reproduce faults. Developers porting bare-metal or RTOS code to an MMU-equipped Cortex-A core must initialize the MMU explicitly and flush the TLB and caches at the right moments.
The MMU also enforces memory attributes such as read/write/execute permissions, cacheability, and shareability. These attributes must match the physical nature of each region: marking device-mapped registers as cacheable, for instance, causes incorrect device behavior because the CPU may coalesce or reorder accesses. The blog post "Memory Mapped I/O in C" covers related pitfalls around volatile and compiler optimization that compound these hardware-level concerns.
Bare-metal MCU developers who never use an OS rarely encounter a true MMU. Cortex-M parts instead offer the MPU (Memory Protection Unit), which partitions the flat physical address space into protected regions without address translation. The blog post "NULL pointer protection with ARM Cortex-M MPU" illustrates that pattern. If a project migrates from a Cortex-M to a Cortex-A running Linux, understanding the MMU becomes unavoidable.
Frequently asked
What is the difference between an MMU and an MPU?
An MMU provides full virtual-to-physical address translation, enabling separate virtual address spaces for each process and features like demand paging. An MPU (Memory Protection Unit) applies access permissions and memory attributes to regions of the physical address space but does not translate addresses. Many Cortex-M MCUs (STM32, nRF52, SAMD51, etc.) include an MPU; Cortex-A application processors include an MMU, though the specific memory protection features available vary by implementation.
Do I need to worry about the MMU when writing bare-metal code on a Cortex-A device?
Yes. On Cortex-A cores the MMU is present but starts disabled after reset. Running with the MMU off means caches are often also off or require careful
cache-maintenance handling, and memory attributes are not enforced. Most production bare-metal or RTOS ports enable the MMU early in startup, set up a flat virtual-to-physical identity map, and configure correct memory attributes (strongly-ordered or device for peripherals, normal cacheable for
RAM) to get correct and performant behavior.
What is a TLB and why does it matter in embedded Linux development?
The Translation Lookaside Buffer (TLB) is a small hardware
cache inside the MMU that stores recent virtual-to-physical translations. On a TLB miss the hardware (on ARMv7-A/ARMv8-A) performs a page-table walk in
RAM, which costs tens to hundreds of cycles. In embedded Linux on memory-constrained devices with small or fragmented working sets, TLB pressure can be a measurable overhead. Operations that invalidate the TLB -- such as
context switches or large munmap calls -- can cause brief
latency spikes relevant to soft-real-time applications.
Can an RTOS like FreeRTOS use an MMU?
Most bare-metal RTOS designs, including
FreeRTOS, do not require or use an MMU because they run all tasks in a single shared address space. FreeRTOS does have MPU-aware ports for Cortex-M and some Cortex-R targets that use the MPU for task isolation without address translation. Running FreeRTOS on a Cortex-A part is possible with the MMU disabled or with an identity-mapped flat map, but you lose the process-isolation guarantees that justify the MMU's complexity. If you need true isolation on a Cortex-A, a Linux or RTOS-with-MMU-support (e.g., RTEMS, Zephyr on Cortex-A) is the more natural fit.
What goes wrong if memory attributes are set incorrectly in the MMU page tables?
Incorrect attributes cause a range of failures. Marking peripheral
registers as Normal Cacheable memory allows the CPU to buffer, merge, or reorder writes, so register sequences that depend on strict ordering may silently misbehave or
deadlock a peripheral. Marking normal
RAM as Strongly Ordered or Device memory disables caching and write buffering, severely hurting performance. On multi-core SoCs, wrong shareability attributes can cause one core to see stale data written by another. These bugs are often intermittent and difficult to reproduce under a
debugger because the act of halting the core changes timing.
Differentiators vs similar concepts
An MMU is often confused with an MPU (Memory Protection Unit). The key distinction is address translation: an MMU maps virtual addresses to physical addresses, making separate per-process address spaces possible; an MPU only guards regions of the existing physical address space with access-permission attributes. Many Cortex-M MCUs (STM32, nRF52, SAM D/E, Kinetis) carry an MPU, not an MMU, though not all Cortex-M implementations include one. Cortex-A application processors carry an MMU, with the specific memory protection mechanisms available beyond that varying by implementation. The MMU is also distinct from a
cache controller, though on ARM cores the two are tightly coupled -- cache lookups use the physical address produced by the MMU, and memory attributes set in the page tables govern cacheability.