In short: An MPU enforces region-based access permissions on physical addresses to keep faulty or hostile code from corrupting memory it should not touch, while an MMU adds full address translation (virtual memory, paging, per-process address spaces) — so you pick the MPU for deterministic real-time microcontrollers and the MMU for anything running Linux or a rich multi-process OS.
Ask ten embedded engineers whether their chip has an MPU or an MMU and half will hesitate. The two acronyms differ by one letter and both sit between the CPU core and memory, yet they solve fundamentally different problems. Getting this distinction right decides whether your firmware can run FreeRTOS on a Cortex-M or whether it needs the hardware to host Linux. This article walks through what each block actually does, the silicon cost, concrete Arm examples, and how to choose.
What an MPU actually does
A Memory Protection Unit (MPU) is a permission checker. It does not translate addresses — the address the CPU emits is the physical address that reaches the bus. Instead, the MPU holds a small table of regions (typically 8 or 16 on Arm Cortex-M), each described by a base address, a size, and a set of attributes: read/write/execute, privileged-vs-unprivileged access, cacheability, and shareability. On every load, store, or instruction fetch, hardware checks the target address against the active regions in a single cycle. If a region says "unprivileged code may not write here" and unprivileged code tries, the core takes a MemManage fault instead of silently corrupting data.
Key fact: An MPU adds zero latency in the common case. The permission check happens in parallel with the access, so a correctly-permitted load on a Cortex-M7 costs exactly the same cycles whether the MPU is on or off. That is why hard-real-time systems can afford protection.
The classic use is a two-privilege split under an RTOS. The kernel runs privileged; each task runs unprivileged with an MPU region covering only its own stack and data. A stack overflow or a wild pointer in one task faults immediately rather than quietly overwriting another task's variables three milliseconds before the bug shows up somewhere unrelated. In safety-certified code (IEC 61508, ISO 26262, DO-178C) this spatial isolation is often mandatory — you must prove that a QM-level task cannot corrupt an ASIL-D task, and an MPU region is how you do it.
What an MMU adds on top
A Memory Management Unit (MMU) does everything an MPU does and then adds the big feature: virtual-to-physical address translation. The CPU emits a virtual address; the MMU walks a multi-level page table in memory to find the matching physical page, and caches recent translations in a Translation Lookaside Buffer (TLB). Permissions ride along in the page-table entries, so protection is per-page (commonly 4 KB) rather than per-region.
Translation is what enables a modern OS. Every process gets its own virtual address space starting at the same virtual base, so two instances of the same program can both think they own address 0x8000 while living in different physical RAM. It enables demand paging (load a page from flash or swap only when touched), copy-on-write fork(), memory-mapped files, and ASLR. None of that is possible with an MPU, because an MPU cannot make two different physical pages look like one virtual address.
Key fact: The convenience of the MMU is not free. A TLB miss forces a hardware page-table walk — on Armv8-A that can be up to four memory accesses for a 48-bit address with 4 KB pages. That jitter is exactly why hard-real-time control loops avoid full virtual memory: worst-case latency becomes data-dependent.
Side-by-side comparison
| Property | MPU | MMU |
|---|---|---|
| Address translation | None (physical = virtual) | Full virtual→physical |
| Granularity | 8–16 regions, power-of-two sized | Per-page, typically 4 KB |
| Per-process address space | No | Yes |
| Demand paging / swap | No | Yes |
| Timing determinism | Deterministic, single-cycle | TLB-miss jitter |
| Typical OS | FreeRTOS, Zephyr, bare metal | Linux, QNX, Android |
| Arm example | Cortex-M3/M4/M7, Cortex-R | Cortex-A53/A72, Cortex-A520 |
| Silicon / power cost | Small | Larger (page tables, TLB, walk logic) |
Real silicon: where the line falls in the Arm world
The Arm Cortex-M family (M3, M4, M7, M33) ships with an optional MPU and no MMU — you cannot run mainline Linux on them, which is why they host FreeRTOS, Zephyr, or Arm's own RTOS. The Cortex-R family (R5, R52) targets real-time with an MPU because safety-critical automotive and storage workloads need determinism more than virtual memory. Only when you cross into the Cortex-A application cores (A53, A72, the newer A520/A720) do you get a full MMU with a two-stage translation scheme that also supports hypervisor-level (stage-2) translation for virtualization.
This split shows up directly in SoC design and verification. When a team builds a custom SoC at a firm like Avecas, which does VLSI and SoC design services, choosing a protected microcontroller subsystem versus a Linux-capable application cluster changes the memory subsystem, the page-table walker RTL, and the whole verification plan. Functional verification of an MMU — proving the page walker, TLB coherence, and fault generation are correct — is a substantial effort typically driven with UVM testbenches in Synopsys VCS or Cadence Xcelium, with coverage closure tracked in Siemens Questa. An MPU's region-check logic is far smaller to verify by comparison.
How to choose
Pick an MPU when: you have hard real-time deadlines, you run an RTOS or bare metal, you need functional-safety spatial isolation, and your working set fits in on-chip SRAM. Pick an MMU when: you run Linux or another process-based OS, you need many independent applications, you want demand paging because your program image exceeds RAM, or you need virtualization. Many modern SoCs do both — an Armv8 application cluster with an MMU for Linux, plus a Cortex-M safety island with an MPU running the lock-step control firmware. Understanding caches is the natural next step, since both units interact tightly with cache attributes; see our deep dive on cache in embedded systems and the companion piece on RTOS memory management. These topics are covered hands-on in our embedded and VLSI courses.
Want to configure a real MPU region table on a Cortex-M and watch a fault fire on a stack overflow? Our CourseTron Embedded Systems track walks you through it on actual hardware, with the memory-protection labs interviewers love to ask about.
Frequently asked questions
Can a chip have both an MPU and an MMU?
Yes. Many Armv8-A cores expose an MMU for the OS, and heterogeneous SoCs pair a Cortex-A cluster (MMU) with a Cortex-M or Cortex-R safety island (MPU). They serve different cores or different privilege contexts.
Does an MPU slow down memory access?
No. The permission check runs in parallel with the access, so a permitted load takes the same cycles as with the MPU disabled. Only a violation costs anything — and that cost is a fault exception, which is exactly what you want.
Why can't I run Linux on a Cortex-M?
Mainline Linux requires virtual memory — per-process address spaces and demand paging — which needs an MMU. Cortex-M cores have only an MPU, so they run an RTOS instead. (uClinux once ran MMU-less but is effectively obsolete for new designs.)
Is the TLB part of the MPU or the MMU?
The TLB belongs to the MMU. It caches recent virtual-to-physical translations to avoid repeated page-table walks. An MPU has no translations to cache, so it has no TLB.