Understanding ARM Cortex-M Memory Protection Unit (MPU)

Understanding ARM Cortex-M Memory Protection Unit (MPU)

Hello everyone, I am Pi Zi Heng, a serious technical person. Today, I will introduce the ARM Cortex-M Memory Protection Unit (MPU).

The kernel series of “Things about ARM Cortex-M MCU Development” started writing at the end of 2017, but only 7 articles were written before it was stopped. I am really sorry for the long wait. Recently, while supporting i.MXRT customers, I found that customers were not very familiar with the MPU function of Cortex-M, which led to illegal memory access issues in the project. Therefore, I will restart this Cortex-M kernel series to systematically explain the MPU:

Note: This article is the first part about MPU, mainly discussing the MPU design of Cortex-M processors under the PMSAv6/7 architecture, applicable to Cortex-M0+/M3/M4/M7.

1. What is MPU?

MPU stands for “Memory Protection Unit”. It is a module inside the Cortex-M processor (Note: Not all Cortex-M versions support MPU, and in some Cortex-M versions that support MPU, it is also an optional component depending on whether the specific MCU manufacturer implements it).

Let us explain the role of MPU with the following module block diagram of the Cortex-M processor (taking CM0+ as an example, other versions are similar). From the block diagram, you can see that the MPU is located between the Core and the Bus matrix. The Bus matrix is the ARM system bus manager, used to realize the interconnection and arbitration of multiple masters (Core, DMA, etc.) and multiple slaves (internal RAM, APB peripherals, external buses, etc.) in the system. The Core can access all storage/peripheral resources in the system space through the Bus matrix. Now, the MPU stands between the Core and the Bus matrix, which means that the Core’s access to the system storage resources needs to go through the MPU’s permission control and review.

Understanding ARM Cortex-M Memory Protection Unit (MPU)

2. Types and Attributes of Storage Space

The MPU’s access permission control for storage space mainly includes: Strongly-ordered (whether strictly ordered), Shareable (whether shareable), Cacheable (whether cacheable), Execute Never (whether executable), etc. Different access permission configurations create different types and attributes of storage space in the system, all of which are to ensure that the storage resources can be accessed efficiently and reliably by the Core (more reliable guarantees are needed in RTOS environments than in bare-metal programs).

Understanding ARM Cortex-M Memory Protection Unit (MPU)

Everyone should understand the Shareable/Cacheable/Execute Never attributes. It is necessary to mention the Strongly-ordered attribute because different attribute configurations of storage space can cause confusion regarding the order of memory access instructions during code execution. For example, for two memory access instructions A1 and A2, assuming that they are issued by the same master device interface and A1 appears before A2 in the program code, the actual execution results based on different attribute combinations of A1/A2 are as follows: sometimes the system cannot guarantee that A1 operation will complete before A2 operation. In such cases, software design needs to manually insert memory barrier instructions (ISB, DSB) to ensure the final order.

Understanding ARM Cortex-M Memory Protection Unit (MPU)

If the MPU module does not exist or is not enabled, the default attributes of the processor system’s 4GB storage space are as follows (in the table, XN means Execute Never; WT means Write-Through; WBWA means Write-Back, write allocate):

Understanding ARM Cortex-M Memory Protection Unit (MPU)

Understanding ARM Cortex-M Memory Protection Unit (MPU)

3. MPU Function Configuration

The MPU module is a module built into the processor core, and its register definitions can be found in \CMSIS\Include\core_cm0plus/3/4/7.h file. The specific explanations of the register functions will not be elaborated here; you can refer to the corresponding ARMv6/7-M Architecture RM or Cortex-M0+/3/4/7 Generic UG manuals for detailed explanations.

To summarize briefly, the MPU supports a maximum of 8/16 main space divisions (MPU_RNR[REGION], REGION takes values 0-7 or 0-15), and each main space can freely set its attributes (MPU_RASR[XN/AP/TEX/S/C/B]). The size of the space is configurable, with a minimum granularity of 32 bytes, and spaces can overlap (high-numbered space attributes will override low-numbered space attributes). When the size allocated to a main space exceeds 256 bytes, this main space can also be divided into 8 subspaces, each with independent control (MPU_RASR[SRD]).

Understanding ARM Cortex-M Memory Protection Unit (MPU)

The most critical register of the MPU module is MPU_RASR, which provides specific access permission configurations for storage space. The fields XN/AP/TEX/S/C/B together determine the final permissions, and users can configure them according to project requirements.

Understanding ARM Cortex-M Memory Protection Unit (MPU)

Understanding ARM Cortex-M Memory Protection Unit (MPU)
The settings for the Cache strategy in the table AA/BB are defined as follows:
    00 Non-cacheable
    01 Write-back, write and read allocate
    10 Write-through, no write allocate
    11 Write-back, no write allocate
Understanding ARM Cortex-M Memory Protection Unit (MPU)

4. MPU Configuration Code Example

ARM has pre-implemented some functional functions/macros for the MPU module, which can be found in \CMSIS\Include\mpu_armv6/7.h file. The most commonly used are the ARM_MPU_RBAR and ARM_MPU_RASR macros.

Below is an example configuration code for the MPU of NXP i.MXRT1170, which is an MCU with Cortex-M7 core and has 2MB RAM. The official MIMXRT1170-EVK board has an external 16MB NOR Flash and 64MB SDRAM.

In the code, Region2/3/4/5/6/8/9 are the configurations of actual user storage space, while other Region0/1/7 are the configurations of basic system space. Illegal access to undefined space will result in MemManage or BusFault.

void BOARD_ConfigMPU(void)
{
    /* Disable I cache and D cache */
    SCB_DisableICache();
    SCB_DisableDCache();

    /* Disable MPU */
    ARM_MPU_Disable();

    //////////////////////////////////////////////////////////////////////////////////////
    // All 4GB space of the system is first configured as Device with XN attribute
    /* Region 0 setting: Instruction access disabled, No data access permission. */
    MPU->RBAR = ARM_MPU_RBAR(0, 0x00000000U);
    MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_NONE, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_4GB);
    //////////////////////////////////////////////////////////////////////////////////////
    // The 1GB space after 0x00000000 is configured as Device with non-XN attribute
    /* Region 1 setting: Memory with Device type, not shareable, non-cacheable. */
    MPU->RBAR = ARM_MPU_RBAR(1, 0x00000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_1GB);

    //////////////////////////////////////////////////////////////////////////////////////
    // The internal RAM space after 0x00000000 (actual 2MB)
    /* Region 2 setting: Memory with Normal type, not shareable, outer/inner write back */
    MPU->RBAR = ARM_MPU_RBAR(2, 0x00000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_256KB);
    /* Region 3 setting: Memory with Normal type, not shareable, outer/inner write back */
    MPU->RBAR = ARM_MPU_RBAR(3, 0x20000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_256KB);
    /* Region 4 setting: Memory with Normal type, not shareable, outer/inner write back */
    MPU->RBAR = ARM_MPU_RBAR(4, 0x20200000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_1MB);
    /* Region 5 setting: Memory with Normal type, not shareable, outer/inner write back */
    MPU->RBAR = ARM_MPU_RBAR(5, 0x20300000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_512KB);

    //////////////////////////////////////////////////////////////////////////////////////
    // The external NOR Flash space after 0x30000000 (actual 16MB)
#if defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1)
    /* Region 6 setting: Memory with Normal type, not shareable, outer/inner write back. */
    MPU->RBAR = ARM_MPU_RBAR(6, 0x30000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_RO, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_16MB);
#endif

    //////////////////////////////////////////////////////////////////////////////////////
    // The external SDRAM space after 0x80000000 (maximum 512MB, actual 64MB)
    /* Region 7 setting: Memory with Device type, not shareable, non-cacheable. */
    MPU->RBAR = ARM_MPU_RBAR(7, 0x80000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_512MB);
#ifdef USE_SDRAM
    /* Region 8 setting: Memory with Normal type, not shareable, outer/inner write back */
    MPU->RBAR = ARM_MPU_RBAR(8, 0x80000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_64MB);
#endif
#if defined(__ICCARM__) || defined(__GNUC__)
    extern uint32_t __NCACHE_REGION_START[];  // Defined in the linker file
    extern uint32_t __NCACHE_REGION_SIZE[];   // Defined in the linker file
    uint32_t nonCacheStart = (uint32_t)__NCACHE_REGION_START;
    uint32_t size          = (uint32_t)__NCACHE_REGION_SIZE;
#endif
    volatile uint32_t i = 0;
    while ((size >> i) > 0x1U)
    {
        i++;
    }
    if (i != 0)
    {
        /* The MPU region size should be 2^N, 5<=N<=32, region base should be multiples of size. */
        assert(!(nonCacheStart % size));
        assert(size == (uint32_t)(1 << i));
        assert(i >= 5);

        /* Region 9 setting: Memory with Normal type, not shareable, non-cacheable */
        MPU->RBAR = ARM_MPU_RBAR(9, nonCacheStart);
        MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 1, 0, 0, 0, 0, i - 1);
    }

    /* Enable MPU */
    ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk);

    /* Enable I cache and D cache */
    SCB_EnableDCache();
    SCB_EnableICache();
}

Appendix 1: Differences in MPU Features

Processor Version Exists MPU Maximum Main Protection Regions (k) Main Protection Regions Overlap Sub Protection Regions Three Groups of Alias Space
Cortex-M0/1 No N/A N/A N/A N/A
Cortex-M0+ Yes 8 Supported k*8 Not supported
Cortex-M3/4 Yes 8 Supported k*8 Supported
Cortex-M7 Yes 8/16 Supported k*8 Supported
Cortex-M23 Yes 8 Not supported Not supported Not supported
Cortex-M33/35P Yes 8 Not supported Not supported Supported
Cortex-M55 Yes 4/8/12/16 Not supported Not supported Supported

Thus, the introduction of the ARM Cortex-M Memory Protection Unit (MPU) by Pi Zi Heng comes to an end, where is the applause~~~

Understanding ARM Cortex-M Memory Protection Unit (MPU)

1. Welcome to the new era of open-source innovative industrial operating systems~

2. Teacher He Xiaoqing’s embedded and IoT course video collection has been updated!

3. A master with ten years of experience talks about how to learn STM32 embedded development

4. Detailed explanation of OpenHarmony compilation and construction (Windows version)

5. Practical | Build an embedded web server in 10 minutes

6. Without a serial port, how would you print debug logs?

Understanding ARM Cortex-M Memory Protection Unit (MPU)

Disclaimer: This article is a network reprint, and the copyright belongs to the original author. If there are copyright issues, please contact us, and we will confirm the copyright and pay the remuneration or delete the content based on the copyright certificate you provide.

Leave a Comment

×