痞子衡
认证:普通会员
作者动态
i.MXRT四位数系列eFUSE及其烧写方法
2小时前
走进二维码(QR Code)的世界之初体验
2天前
非XIP程序在片内SRAM的一般/特殊链接限制
3天前
走进二维码(QR Code)的世界之引言
3天前
从文件角度看Cortex-M开发(8) - 镜像文件
1星期前

Cortex-M内核存储保护单元MPU

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是ARM Cortex-M存储保护模块(MPU)

《ARM Cortex-M内核MCU开发那些事》的内核篇连载最早是 2017 年底开始写的,但只写了 7 篇就停更了,鸽了这么久实在不好意思。最近在支持 i.MXRT 客户的过程中,发现客户对 Cortex-M 的 MPU 功能不太了解,导致项目中出了内存非法访问的问题,借此机会,痞子衡将重启这个 Cortex-M 内核篇连载,为大家系统地讲解下MPU:

本篇是MPU上篇,主要讲述PMSAv6/7架构下的Cortex-M处理器MPU设计,适用Cortex-M0+/M3/M4/M7。

一、MPU是什么?

MPU 全称"Memory Protection Unit",中文叫“存储保护单元”,它是 Cortex-M 处理器内部的一个模块(注意:并不是所有 Cortex-M 版本都支持 MPU,并且在一些支持 MPU 的 Cortex-M 版本上,MPU 也是可选组件(要看具体MCU厂商是否实现))。

让我们结合如下 Cortex-M 处理器(以 CM0+ 为例,其他版本类似)模块框图中来解释 MPU 作用,从框图中大家可以看到,MPU 介于 Core 和 Bus matrix 中间。Bus matirx 是 ARM 系统总线大管家,用以实现系统内多主(Core,DMA等)、多从(内部RAM,APB外设,外部总线等)的交联和仲裁,Core 通过 Bus matirx 可以访问到系统空间内的所有存储/外设资源,现在 MPU 挡在了 Core 和 Bus matirx 中间,这意味着从此 Core 对系统存储资源的访问需要经过 MPU 的权限控制与审核。

二、存储空间类型与属性

MPU 对存储空间的访问权限控制主要包含:Strongly-ordered(是否严格有序)、Shareable(是否共享)、Cacheable(是否缓存)、Execute Never(是否可执行)等方面,不同的访问权限配置造就了系统里不同的存储空间类型与属性,这一切都是为了能够让存储资源被 Core 高效且可靠地访问(RTOS环境下比裸机程序下更需要可靠保证)。

Shareable/Cacheable/Execute Never 属性大家应该都了解,有必要特别提一下 Strongly-ordered 属性,因为不同属性的存储空间配置会给代码执行时内存访问指令的顺序方面造成了困扰,比如两个内存访问指令A1, A2,假定它们是同一主设备接口发出的,并且 A1 在程序代码里出现在 A2 之前,根据 A1/A2 不同的属性组合其实际执行结果如下,有些时候系统无法保证 A1 操作一定比 A2 操作先完成,这时候需要软件设计里手工插入内存屏障指令(ISB, DSB)来保证最终顺序。

如果 MPU 模块不存在或者没有被使能,处理器系统 4GB 存储空间默认的属性如下(表中 XN 即 Execute Never;WT 即 Write-Throug;WBWA 即 Write-Back, write allocate):

三、MPU功能配置

MPU 模块是处理器内核自带的模块,其寄存器定义见 \CMSIS\Include\core_cm0plus/3/4/7.h 文件,具体寄存器功能解释这里就不展开了,可翻阅对应 ARMv6/7-M Architecture RM 或者 Cortex-M0+/3/4/7 Generic UG 手册找到具体解释。

简单概括一下,MPU 最多支持 8/16 个主空间划分(MPU_RNR[REGION],REGION取值 0-7 或者 0-15),每个主空间可以自由设置其属性(MPU_RASR[XN/AP/TEX/S/C/B]),空间大小是可设的,最小粒度为 32bytes,空间之间也可以重叠(高序号空间属性会覆盖低序号空间属性)。当某个主空间分配的大小超过 256 bytes 时,这个主空间还可以被等分成 8 个子空间,每个子空间有独立的开关控制(MPU_RASR[SRD])。

MPU 模块最核心的寄存器是 MPU_RASR,其提供了存储空间具体访问权限配置,XN/AP/TEX/S/C/B 位域共同决定了最终权限,用户可根据项目实际需求进行配置。

上表中关于 Cache 策略的设置 AA/BB 定义如下:
    00 Non-cacheable
    01 Write-back, write and read allocate
    10 Write-through, no write allocate
    11 Write-back, no write allocate

四、MPU配置代码示例

ARM 官方为 MPU 模块预实现了一些功能函数/宏定义,见 \CMSIS\Include\mpu_armv6/7.h 文件,其中最常用的是 ARM_MPU_RBAR 和 ARM_MPU_RASR 宏。

如下是恩智浦 i.MXRT1170 的 MPU 示例配置代码,这是颗 Cortex-M7 内核的 MCU,内部有 2MB RAM,官方 MIMXRT1170-EVK 板卡为其外挂了 16MB 的 NOR Flash 和 64MB 的 SDRAM。

代码中 Region2/3/4/5/6/8/9 是实际用户存储空间的配置,其他 Region0/1/7 是基本系统空间的配置,未定义空间的非法访问会产生 MemManage 或者 BusFault。

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

    /* Disable MPU */
    ARM_MPU_Disable();

    //////////////////////////////////////////////////////////////////////////////////////
    // 系统全部 4GB 空间先配置成 XN 属性的 Device
    /* 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);
    //////////////////////////////////////////////////////////////////////////////////////
    // 0x00000000 之后的1GB 空间配置成非 XN 属性的 Device
    /* 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);

    //////////////////////////////////////////////////////////////////////////////////////
    // 0x00000000 之后的内部 RAM 空间配置(实际 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);

    //////////////////////////////////////////////////////////////////////////////////////
    // 0x30000000 之后的外部 NOR Flash 空间配置(实际 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

    //////////////////////////////////////////////////////////////////////////////////////
    // 0x80000000 之后的外部 SDRAM 空间配置(最大 512MB,实际 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[];  // 来自链接文件里的定义
    extern uint32_t __NCACHE_REGION_SIZE[];   // 来自链接文件里的定义
    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();
}

附录一、MPU特性差异

至此,ARM Cortex-M存储保护模块(MPU)痞子衡便介绍完毕了,掌声在哪里~~~

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 1
收藏 3
关注 27
成为作者 赚取收益
全部留言
0/200
成为第一个和作者交流的人吧