A single stray pointer can corrupt memory and crash a system in ways that are agony to debug. The Memory Protection Unit stops that at the hardware level: it polices who may touch which memory and how, turning a silent corruption into an instant, catchable fault. It's the foundation of safe and secure embedded software.
By default, any code can write anywhere — over a constant table, into a peripheral, off the end of the stack, even through a null pointer at address 0. The bug doesn't crash where it happens; it corrupts something and the system fails mysteriously later. The MPU's job is to make illegal accesses fail immediately and visibly.
The MPU divides the address space into a small number of regions (typically 8 or 16). For each region you define a base address, a size, and a set of rules:
| Attribute | Controls |
|---|---|
| Access permission | no-access / read-only / read-write, and whether unprivileged code may access it |
| Execute-never (XN) | whether instructions may be fetched from the region (data regions → XN) |
| Memory type | normal vs device/strongly-ordered (controls caching & reordering) |
| Shareability/cache | cache & coherency behaviour for the region |
Classic safe setup: flash = read-only + executable, RAM = read-write + execute-never, peripherals = read-write + device + XN. Now a wild jump into data, or a write into code, is impossible.
If software violates a region's rules, the MPU blocks the access and raises a MemManage fault, vectoring to a fault handler (an exception, per Day 17). The handler reads fault-status registers to learn what and where:
Without an MPU, a memory bug is a slow gas leak — you only notice when something explodes far away. The MPU is a smoke detector wired to each room: the instant something's wrong, it goes off at the source, so you know exactly which line and which address.
| MPU | MMU | |
|---|---|---|
| Address translation | none — uses physical addresses | virtual → physical (page tables) |
| Granularity | a few fixed regions | fine-grained pages (e.g. 4 KB) |
| Per-process address space | no | yes |
| Typical core | Cortex-M / Cortex-R | Cortex-A |
| Runs | bare-metal / RTOS | Linux, rich OSes |
An MPU only protects; an MMU also translates, giving every process its own virtual view of memory. That's a later lesson in the course — the MPU is the lightweight, deterministic cousin built for real-time systems.
The MPU is a hardware access policy over a handful of memory regions: each gets read/write/execute permissions and attributes, and any violation becomes an instant MemManage fault instead of silent corruption. Unlike an MMU, it does no translation — it just protects — which makes it perfect for real-time isolation, privilege separation and safety.
A hardware block that enforces access permissions and attributes on a few memory regions, faulting on any disallowed access.
An MPU only protects fixed physical regions; an MMU also translates virtual→physical addresses with page tables for per-process spaces.
Safety, security and reliability — task isolation, privilege separation, and catching stray accesses as faults.