all
Chapter 12 of 20

Memory Protection Unit (MPU) — Tiva C

Eslam El Hefny Apr 12, 2025 7 min read
60% done

Memory Protection Unit (MPU) — Tiva C

Overview

The Memory Protection Unit (MPU) is an optional Cortex-M4 feature present on the TM4C123GH6PM. It enforces access rules on up to 8 configurable memory regions, preventing tasks from corrupting each other’s data, stopping code execution from RAM, and catching stack overflow before it corrupts critical variables.


Beginner Level — What & Why

What is the MPU?

The MPU is a hardware “access control list” for memory. You define regions (address + size + rules), and any violation — a task writing to Flash, code running from the stack, or a pointer escaping its buffer — immediately triggers a MemManage fault exception instead of silently corrupting memory.

Real-World Analogy

Imagine a building with different security zones. The MPU is the access-card system: area A (Flash) is read-only for everyone, area B (stack) is read/write only for the owning department, and area C (peripherals) requires supervisor badge. If someone tries to enter an area without the right access, an alarm sounds (MemManage fault).

What Problem Does It Solve?

  • Protects the RTOS kernel from buggy application tasks
  • Prevents stack overflow from silently overwriting heap or global data
  • Blocks code execution from RAM (XN bit) — a security and safety requirement
  • Provides the foundation for memory isolation in safety-critical systems (ISO 26262, IEC 61508)

Key Terms

Term Meaning
Region A defined address range with access rules
AP bits Access Permissions — who can read/write
XN eXecute Never — prevents code execution in a region
TEX, C, B, S Type Extension, Cacheable, Bufferable, Shareable — memory type bits
MemManage fault Exception triggered on MPU violation
PRIVDEFENA Privileged Default Enable — allows privileged access to unmapped regions

Intermediate Level — How It Works

MPU Registers

Register Address Description
MPU_TYPE 0xE000ED90 Type info: DREGION (number of regions), IREGION
MPU_CTRL 0xE000ED94 Control: ENABLE, HFNMIENA, PRIVDEFENA
MPU_RNR 0xE000ED98 Region Number Register (0–7)
MPU_RBAR 0xE000ED9C Region Base Address (must be aligned to region size)
MPU_RASR 0xE000EDA0 Region Attribute and Size Register

MPU_RASR field breakdown:

Bits Field Description
0 ENABLE 1 = region enabled
5:1 SIZE Region size = 2^(SIZE+1) bytes (minimum SIZE=4 → 32 bytes)
15:8 SRD Sub-Region Disable (one bit per 1/8 of the region)
16 B Bufferable
17 C Cacheable
18 S Shareable
21:19 TEX Type Extension
26:24 AP Access Permission
28 XN Execute Never

Access Permission (AP) values:

AP Privileged Unprivileged
0b000 No access No access
0b001 Read/Write No access
0b010 Read/Write Read only
0b011 Read/Write Read/Write
0b101 Read only No access
0b110 Read only Read only

TivaWare DriverLib API

#include "driverlib/mpu.h"

/* Check number of regions supported */
uint32_t type = MPURegionCountGet();  // should be 8 on TM4C123

/* Configure Region 0: Flash (read-only, executable, privileged only) */
MPURegionSet(0,                        // region number 0
             0x00000000,               // base address (Flash)
             MPU_RGN_SIZE_256K  |      // 256 KB (size field = 17 → 2^18)
             MPU_RGN_PERM_PRV_RO_USR_RO | // priv=RO, user=RO
             MPU_RGN_ENABLE);          // enable this region

/* Configure Region 1: SRAM (read/write, XN, privileged only) */
MPURegionSet(1,
             0x20000000,               // base address (SRAM)
             MPU_RGN_SIZE_32K   |      // 32 KB
             MPU_RGN_PERM_PRV_RW_USR_NO | // priv=RW, user=no access
             MPU_RGN_PERM_NOEXEC |     // XN = execute never
             MPU_RGN_ENABLE);

/* Enable the MPU with default background region (privileged full access) */
MPUEnable(MPU_CONFIG_PRIV_DEFAULT);    // PRIVDEFENA = 1

Advanced Level — Deep Dive

Bare-Metal MPU Configuration

#include "inc/hw_types.h"
#include "inc/hw_nvic.h"   // MPU register definitions

void MPU_Init(void)
{
    /* Disable MPU before configuration */
    HWREG(NVIC_MPU_CTRL) = 0;

    /* --- Region 0: Flash 0x00000000–0x0003FFFF (256 KB) ---
     * AP=001 (priv RW, user No), XN=0 (executable), SIZE=17 (2^18=256KB) */
    HWREG(NVIC_MPU_NUMBER) = 0;
    HWREG(NVIC_MPU_BASE)   = 0x00000000;
    HWREG(NVIC_MPU_ATTR)   =
        (0x11 << 1)    |  // SIZE = 17 → 2^18 = 256 KB
        (0x03 << 24)   |  // AP = 0b011 (full access)
        (0x01 << 28)   |  // XN = 0 (executable — flash is OK)
        (1U   << 0);      // ENABLE

    /* --- Region 1: SRAM 0x20000000–0x20007FFF (32 KB) ---
     * AP=011 (full RW), XN=1 (no execute from RAM), SIZE=14 (2^15=32KB) */
    HWREG(NVIC_MPU_NUMBER) = 1;
    HWREG(NVIC_MPU_BASE)   = 0x20000000;
    HWREG(NVIC_MPU_ATTR)   =
        (0x0E << 1)    |  // SIZE = 14 → 2^15 = 32 KB
        (0x03 << 24)   |  // AP = full access
        (0x01 << 28)   |  // XN = 1 (no execute from SRAM)
        (1U   << 0);      // ENABLE

    /* --- Region 2: Peripheral 0x40000000–0x5FFFFFFF (512 MB) ---
     * AP=001 (priv RW only), XN=1, SIZE=28 (2^29=512MB) */
    HWREG(NVIC_MPU_NUMBER) = 2;
    HWREG(NVIC_MPU_BASE)   = 0x40000000;
    HWREG(NVIC_MPU_ATTR)   =
        (0x1C << 1)    |  // SIZE = 28 → 2^29 = 512 MB
        (0x01 << 24)   |  // AP = priv RW, user no access
        (0x01 << 28)   |  // XN = 1
        (1U   << 0);      // ENABLE

    /* Enable MPU: ENABLE=1, PRIVDEFENA=1 (privileged can access all) */
    HWREG(NVIC_MPU_CTRL) = NVIC_MPU_CTRL_ENABLE | NVIC_MPU_CTRL_PRIVDEF;
}

Stack Overflow Protection with Sub-Regions

Use a “guard region” at the bottom of the stack that generates a MemManage fault when overflowed:

extern uint32_t __stack_start;  // linker symbol for stack bottom

void MPU_ProtectStack(void)
{
    /* Place a 32-byte no-access guard at the bottom of the stack */
    HWREG(NVIC_MPU_NUMBER) = 3;
    /* Region must be aligned to its size (32 bytes → align to 32 bytes) */
    HWREG(NVIC_MPU_BASE)   = (uint32_t)&__stack_start;
    HWREG(NVIC_MPU_ATTR)   =
        (0x04 << 1)    |  // SIZE = 4 → 2^5 = 32 bytes
        (0x00 << 24)   |  // AP = no access (000)
        (1U   << 0);      // ENABLE
}

MemManage Fault Handler

void MemManage_Handler(void)
{
    /* Read fault address from MMFAR if MMARVALID is set */
    uint32_t ui32CFSR  = HWREG(0xE000ED28);  // CFSR
    uint32_t ui32MMFAR = HWREG(0xE000ED34);  // MMFAR

    if (ui32CFSR & 0x80)  // MMARVALID bit
    {
        /* ui32MMFAR contains the faulting address */
        (void)ui32MMFAR;
    }

    /* In production: log the fault, then reset */
    while (1);  // halt for debugging
}

Size Field Table

SIZE value Region size
4 32 bytes
7 256 bytes
10 1 KB
14 32 KB (SRAM)
17 256 KB (Flash)
28 512 MB (peripheral space)

Formula: region size = 2^(SIZE+1)

Gotchas

  • Region base address must be aligned to the region size. A 32 KB region at 0x20000000 is fine; at 0x20001000 is invalid if 0x1000 < 32 KB.
  • Overlapping regions: the highest-numbered region takes precedence. Use this to grant sub-region exceptions.
  • MemManage fault must be enabled — set bit 16 of SCB SHCSR: HWREG(NVIC_SYS_HND_CTRL) |= (1 << 16).
  • Without PRIVDEFENA, the kernel in privileged mode cannot access unconfigured memory — this will cause immediate faults.
  • The MPU does not apply in NMI and HardFault handlers — these always execute with full access.

Step-by-Step Example

/*
 * mpu_stack_guard.c
 * Demonstrates MPU stack overflow detection with MemManage fault
 * Board  : TM4C123GXL EK LaunchPad
 * SDK    : TivaWare_C_Series-2.2.x
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_nvic.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/mpu.h"
#include "driverlib/interrupt.h"

/* Linker-provided stack bottom symbol (defined in TM4C123.ld) */
extern uint32_t _stack;  // bottom of stack in linker script

void MemManage_Handler(void)
{
    /* Stack overflow detected — blink all LEDs rapidly */
    GPIOPinWrite(GPIO_PORTF_BASE,
                 GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3,
                 GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3); // all LEDs on
    while (1);
}

/* Recursive function to force stack overflow (for demonstration only) */
static void overflow_stack(uint32_t depth)
{
    volatile uint32_t buf[32];  // 128 bytes per frame
    buf[0] = depth;
    overflow_stack(depth + 1);  // infinite recursion → overflow
}

int main(void)
{
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,
                          GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);

    /* Configure Flash region: full access, executable */
    MPURegionSet(0, 0x00000000,
                 MPU_RGN_SIZE_256K |
                 MPU_RGN_PERM_PRV_RW_USR_RW |
                 MPU_RGN_ENABLE);

    /* Configure SRAM region: full RW, execute-never */
    MPURegionSet(1, 0x20000000,
                 MPU_RGN_SIZE_32K |
                 MPU_RGN_PERM_PRV_RW_USR_RW |
                 MPU_RGN_PERM_NOEXEC |
                 MPU_RGN_ENABLE);

    /* Guard region: 256 bytes at bottom of stack, no access */
    /* Stack bottom is typically 0x20000000 for TM4C123 */
    MPURegionSet(2, 0x20000000,
                 MPU_RGN_SIZE_256B |
                 MPU_RGN_PERM_PRV_NO_USR_NO |  // no access at all
                 MPU_RGN_ENABLE);

    /* Enable MemManage fault */
    HWREG(NVIC_SYS_HND_CTRL) |= (1U << 16);

    /* Enable MPU with privileged default region */
    MPUEnable(MPU_CONFIG_PRIV_DEFAULT);
    IntMasterEnable();

    /* Green LED on = MPU configured successfully */
    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);
    SysCtlDelay(SysCtlClockGet() / 3);

    /* Trigger a stack overflow — MemManage_Handler will fire */
    overflow_stack(0);

    /* Should never reach here */
    while (1);
}

Summary

Key Point Details
Regions 8 configurable regions (Region 0–7)
Region size 2^(SIZE+1), minimum 32 bytes
Base alignment Must align to region size
AP=000 No access (use for guard regions)
AP=011 Full read/write access
XN=1 Execute Never (block code from RAM)
Priority Higher region number wins on overlap
MemManage Enable via SHCSR bit 16
PRIVDEFENA Allows privileged code to access unconfigured memory

Next Chapter

Wire Master Module (1-Wire)

Share: