小猫爪:嵌入式小知识11-MPU详解及其应用

2023-05-16

小猫爪:嵌入式小知识11-MPU详解及其应用

  • 1 前言
  • 2 MPU简介
  • 3 MPU相关概念
    • 3.1 Memory Map
    • 3.2 MPU Region
    • 3.3 Region优先级
    • 3.4 Background Region
    • 3.5 Cache的读写策略
      • 3.5.1 Cache的读操作
      • 3.5.1 Cache的写操作
  • 4 MPU寄存器介绍
    • 4.1 MPU_TYPE
    • 4.2 MPU_CTRL
    • 4.3 MPU_RNR
    • 4.4 MPU_RBAR
    • 4.5 MPU_RASR
      • 4.5.1 XN
      • 4.5.2 AP
      • 4.5.3 TEX, C, B, S
      • 4.5.4 SRD
      • 4.5.5 SIZE
  • 5 MPU应用注意事项
  • 6 MPU的errata和Workaround
    • 6.1 1259864
    • 6.2 1013783
    • 6.3 834922
    • 6.4 838169
    • 6.5 834923
  • END

1 前言

  前段时间被MPU(Memory Protection Unit)搞得头疼欲裂,所以就简单的学习了一下,得空做个总结,接下来就来看看这个MPU到底是个什么玩意?MPU其实是属于ARM核自带的一个外设,是跟核绑定在一起的,所以关于它的资料都在ARM核的各种参考指南中,大家请自行下载参考。

2 MPU简介

  MPU说简单点其实就是一个内存访问权限控制器,如果CPU在访问内存时不符合MPU定义的访问权限的话,那么访问就会被驳回,并且会触发一次错误异常,即Hardfault异常(或者MemManage异常,可通过SCB->SHCSR来配置)。配置内存访问权限的好处主要有:

  1. 避免应用程序破坏其他任务或者OS使用的栈和堆
  2. 避免非特权任务访问对系统可靠性和安全性很重要的外设
  3. 防止恶意代码注入攻击
  4. 控制存储器相关访问属性

3 MPU相关概念

3.1 Memory Map

  众所周知,大部分M核目前是32位寻址,那就代表了核能访问0~2^32-1地址范围,总共4G大小的内存空间。芯片厂商会根据自己的设计将内部Flash,内部SRAM,TCM,外设寄存器,还有外部存储器等等的访问地址映射分布在这4G中,这就被称为Memory Map,所以MPU管理的对象就是整个4G空间。

3.2 MPU Region

  MPU可将整个4G分成若干个区域,然后对每个区域设置地址区间(起始地址和大小)和不同的访问权限来达到预期的保护目标。一般来说,M3核和M4最大支持8个区域,而M7最大支持16个区域,而这个区域就叫做MPU Region。

3.3 Region优先级

  上面提到可对每一个MPU Region设置地址区间,那么如果两个Region有重叠的部分,那么这个时候Region Number大的优先级高。即对于同一块内存区间,Region0设置该区间可访问,而Region3设置该该区间不可访问,那么这个区间的权限遵循Region3的配置为不可访问。基本如下图所示:
在这里插入图片描述

3.4 Background Region

  由于每个区间可设置地址区间,那么就会存在一种情况,那就是等所有的Region都配置完之后,4G空间中还有一些区域没有被所有的Region覆盖到,而这些没有被Region覆盖的区域就叫做Background Region。MPU可单独配置对Background Region的属性为默认特权访问权限还是不可访问。
在这里插入图片描述

3.5 Cache的读写策略

  Cache是M核中的一个高速读写缓存区,在这里对其就不多做介绍了,在这里简单的介绍一下Cache的读写策略,另外Cache的读写策略被MPU控制。

3.5.1 Cache的读操作

  如果CPU要读取的数据在Cache中已经加载好,这就叫读命中(Cache hit),如果Cache里面没有则就叫做读失效(Cache Miss)。如果Cache Miss后,就会有下列两种情况:

  1. 读通(Read through):直接从内存区读取数据,不经过Cache
  2. 读分配(Read allocate):先把数据读取到Cache中,再从Cache中读取数据

3.5.1 Cache的写操作

  如果CPU要写的数据在Cache中已经开辟了对应的区域(专业词汇叫Cache Line,以32字节为单位),这就叫写命中(Cache hit),如果Cache里面没有开辟对应的区域,这就叫写失效(Cache Miss)。

  如果如果Cache hit后,就会有下列两种情况:

  1. 写通(Write-through):在数据更新时,把数据同时写入Cache和存储区操作简单,写入速度慢
  2. 写回(Write-back):只有在数据被替换出Cache时,被修改的缓存数据才会被写到后端存储器,写入速度快,但是一旦Cache中更新的数据未被写入到存储器中去时出现断电,则数据会丢失

  如果如果Cache Miss后,就会有下列两种情况:

  1. 写分配(Write allocate):先把要写的数据载入到Cache中,写Cache,然后再通过flush的方式写入到内存中。
  2. 写不分配(No-write allocate):直接把要写的数据写入到存储器中,不经过Cache。

4 MPU寄存器介绍

  与MPU相关的寄存器也是非常的简单,如下:
在这里插入图片描述

4.1 MPU_TYPE

在这里插入图片描述
  里面记录了MPU的最基础信息,那就是当前的这个MPU所能支持的Region个数(DREGION),一般可以通过读这个寄存器来了解当前片子是否支持MPU。

4.2 MPU_CTRL

在这里插入图片描述
PRIVDEFENA:使能特权模式下的默认存储器映射使能,即Background Region在特权访问模式下的访问权限,1特权访问可正常访问Background Region;0禁止访问,一切访问都会触发错误异常。
HFNMIENA:MPU的规则在异常(NMI, HardFault,MemManage,BusFault等等)中是否起作用。1是,0否。

4.3 MPU_RNR

在这里插入图片描述
  决定当前配置哪一个Region,其中REGION决定Region的序号。在设置每个区域前,写入这个寄存器可以选择要配置的Region。

4.4 MPU_RBAR

在这里插入图片描述
REGION:决定当前配置哪一个Region,其中REGION决定Region的序号。
VALID:决定MPU_RBAR[REGION]是否有效。1则有效;0则无效,这个时候MPU_RNR[REGION]有效。
ADDR:配置起始地址,起始地址必须要是Region大小的整数倍。仔细看ADDR所占的bit为[31:N],这个N是由Region大小决定。举个例子,如果设置Region的大小为64K(0x10000),那么起始地址必须为64K的倍数,N则为16,即[31:16]有效。

4.5 MPU_RASR

在这里插入图片描述
  这是MPU的核心寄存器了,其控制着每个Region的访问属性。

4.5.1 XN

  决定该Region是否可以取指令,类似于Linux中的可执行和不可执行。0可执行,1不可执行。

4.5.2 AP

  决定这块区域针对特权模式和非特权模式的最基本的访问属性,具体如下:
在这里插入图片描述

4.5.3 TEX, C, B, S

  在说TEX, C, B, S之前,先得介绍一下M核访问存储器数据的一个流程如下:
在这里插入图片描述
  在图中可以看到MPU不仅控制核内的访问属性,还控制着核外的访问属性,总之及其复杂,而TEX(类型展开), C(可缓存), B(可缓冲), S(可共享)则就是控制这些和内核外的访问权限。其中S(可共享)决定在多核系统中该Region是否可被其他核访问;对于一般的M核控制器来说,它没有实现系统级缓存,所以对我们来说其实就一个写缓冲(B)需要我们去关心,即上面提到的cache读写策略,但是其它的属性也不能胡乱设置,总之复杂无比。

  系统级缓存又分为inner缓存和outer缓存两级缓存。上面提到一般M核控制器没有实现系统级缓存,那么inner缓存和outer缓存属性一致;如果控制器实现了系统级缓存,这两级缓存是可以被单独控制的,属性可不一致。

  所以在设置TEX, C, B, S这几个属性的时候,需要将这4个属性结合在一起去考虑,具体如下表:
在这里插入图片描述
  仔细看表中最后一项,当TEX最高位=1时,那么这个时候TEX的低两位(AA)决定outer缓存属性,而CB(BB)共同决定inner缓存属性,这适用于那些实现了系统级两级缓存的控制器。AA和BB具体如下:
在这里插入图片描述

4.5.4 SRD

  子Region失能位。MPU将SIZE大于128字节的Region按照大小八等分分成8个子Region,而SRD总共8bit,则依次对应这8个子Region,可以通过SRD分被对这8个Region进行控制。0使能,1失能。

4.5.5 SIZE

  决定了该Region的大小,具体请参考如下代码:

#define ARM_MPU_REGION_SIZE_32B      ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes
#define ARM_MPU_REGION_SIZE_64B      ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes
#define ARM_MPU_REGION_SIZE_128B     ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes
#define ARM_MPU_REGION_SIZE_256B     ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes
#define ARM_MPU_REGION_SIZE_512B     ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes
#define ARM_MPU_REGION_SIZE_1KB      ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte
#define ARM_MPU_REGION_SIZE_2KB      ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes
#define ARM_MPU_REGION_SIZE_4KB      ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes
#define ARM_MPU_REGION_SIZE_8KB      ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes
#define ARM_MPU_REGION_SIZE_16KB     ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes
#define ARM_MPU_REGION_SIZE_32KB     ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes
#define ARM_MPU_REGION_SIZE_64KB     ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes
#define ARM_MPU_REGION_SIZE_128KB    ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes
#define ARM_MPU_REGION_SIZE_256KB    ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes
#define ARM_MPU_REGION_SIZE_512KB    ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes
#define ARM_MPU_REGION_SIZE_1MB      ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte
#define ARM_MPU_REGION_SIZE_2MB      ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes
#define ARM_MPU_REGION_SIZE_4MB      ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes
#define ARM_MPU_REGION_SIZE_8MB      ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes
#define ARM_MPU_REGION_SIZE_16MB     ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes
#define ARM_MPU_REGION_SIZE_32MB     ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes
#define ARM_MPU_REGION_SIZE_64MB     ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes
#define ARM_MPU_REGION_SIZE_128MB    ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes
#define ARM_MPU_REGION_SIZE_256MB    ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes
#define ARM_MPU_REGION_SIZE_512MB    ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes
#define ARM_MPU_REGION_SIZE_1GB      ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte
#define ARM_MPU_REGION_SIZE_2GB      ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes
#define ARM_MPU_REGION_SIZE_4GB      ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes

5 MPU应用注意事项

  下面列出一些MPU的一些使用注意事项,如下:

  1. Region的起始地址必须是SIZE的整数倍
  2. 下面根据一些常见的存储器属性列出TEX, C, B, S的配置:
    在这里插入图片描述
  3. MPU_CTRL[ENABLE]可使能和失能MPU,正确配置MPU流程为:失能MPU,配置Region属性,使能MPU。
  4. 在配置MPU过程中需要使用存储器屏障指令:DMB(数据存储器屏障),在禁止MPU前使用,确保数据的传输不会重新排序,并且如果有未完成的传输,会等到传输完成之后在写入MPU相应寄存器。DSB(数据同步屏障),在使能MPU后使用,确保接下来的ISB指令只会在写入MPU控制寄存器结束后才执行,可以确保后续的数据传输使用新的MPU设置。ISB(指令同步屏障),用于DSB之后,确保处理器流水线被清空且接下来指令利用更新后的MPU设置被重新读出。
  5. 在实际调试过程中,如果出现问题,可查看SCB寄存器组的CFSR寄存器和MMFAR查看MPU错误标志和触发MPU错误的地址:
    在这里插入图片描述

6 MPU的errata和Workaround

  在ARM官网中,查看相关errata文件,会发现有几条跟MPU相关的errata,就以M7为例,对应文件《Cortex-M7 (AT610) and Cortex-M7 with FPU (AT611) Product Revision r0p1 Software Developers Errata Notice》,每一条errata都标明了有此errata的core版本已经已经在哪一版已被修复。

6.1 1259864

在这里插入图片描述
  这一条的大概意思就是M7在执行cache的写通操作时,如果满足一定条件并满足一定时序则会偶尔发生写通操作时序被打断导致最后数据不正确。该errata暂时没有直接的Workaround,可以通过将该Region的缓冲属性设置成写回,或者将其设置成不可缓冲(no-cacheable)。

6.2 1013783

在这里插入图片描述
  这一条的大概意思就是如果使能了MPU,并使能了PRIVDEFENA位,那么如果执行PLD指令去访问了那些没有被MPU Region覆盖的地址区域的话,可能会导致触发异常(HardFault或者MemManage)。另外M核的Speculative Access可能就会触发一个PLD指令,所以当存在这个bug的时候就会出现一个非常离奇的现象,那就是程序跑着跑着突然就挂了,而且这个挂死的程序位置还在不停的变化。

  这条errata的Workaround也是及其的简单,那就是让MPU的Region覆盖整个4G空间,并且将未被使用的区域的属性设置成no-access。具体做法就是让Region0覆盖整个4G区间,并且配置其AP属性为no-access,再配置其他的Region配置其他已被使用的内存空间,因为Region的优先级特性,后面的Region会覆盖掉Region0的属性。具体操作如下:
在这里插入图片描述

  插入个小经历:之前接触RT1170的时候遇到了一个bug,就是程序跑着跑着程序就挂了,挂了之后,也没有进入异常,调试器连接不上核。后来经过一大堆人的努力,最后就查到了M核的这个errata,而这个errata出现错误是会触发HardFault的,而在RT1170上的现象并不一样。之所以没有触发异常,而是直接挂死,这跟RT1170本身的存储器控制器结构有关,RT1170去访问一个未被使用的一些特定区域时,RT1170是不会触发异常的,而是会把总线直接卡死。所以对这个现象所能给出的推测是:Speculative Access触发了PLD指令,因为被访问目的地址所在Region的访问属性被配置为可访问(比如这个区域处于在Background Region中,或者用户人为将其属性配置成可访问),所以PLD指令就会偶发的去访问该Region,而该Region在RT1170中正好又是未被使用特殊的区域(如未使用外部SDRAM),所以就会导致RT1170内部总线卡死。 (其实至今也没办法证明确实就是这个问题导致的,但是目前只有这一个合理的解释,而且这样修改后,死机问题也没有再次出现了)。解决办法其实跟上述一致,那就是让MPU的Region覆盖整个4G空间,并且将未被使用的区域的属性设置成no-access,具体操作方法同上所述。下面贴出RT1170的errata截图,上面描述了具体的描述和Workaround,感兴趣的人可以自行查阅。
在这里插入图片描述

6.3 834922

在这里插入图片描述
  这一条的大概意思就是当HFNMIENA没有被使能时,即MPU的规则在异常中不生效,而且还要使用MPU的默认的Memory Map配置,这个时候去设置FAULTMASK的时候如果被打断了,那么接下来就会导致不可预估的事情发生(其实这个bug大概率是遇不到了,不仅产生条件苛刻,而且在r0p2版本早就修复了,不用太过担心)。

  这条errata的Workaround也是及其的简单,就是在置位FAULTMASK之前,先把PRIMASK置位,具体操作如下:
在这里插入图片描述

6.4 838169

在这里插入图片描述
  这一条errata其实是cache的bug,跟MPU本身并没有多大的关系,只是说在配置MPU和cache存储之间如果没有DSB屏障指令的话可能会间接导致这个bug的发生。所以在这里再次强调,在配置MPU的时候,一定要按照5 MPU应用注意事项中的第四条的要求插入存储器屏障指令。

6.5 834923

在这里插入图片描述
这一条的大概意思就是有一种情况可能会导致MPU工作不正常,由于导致这种情况出现的条件非常苛刻,而且也没有workaround去避免,所以只能做的就是尽量去避免这种情况发生,在这里就不多作解释和介绍了,具体的条件如下:
在这里插入图片描述

END

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

小猫爪:嵌入式小知识11-MPU详解及其应用 的相关文章

  • Pixhawk原生固件PX4之MPU6000驱动分析

    欢迎交流 个人 Gitter 交流平台 xff0c 点击直达 xff1a 要想自己添加一个传感器的话 xff0c 最好先搞明白已有的传感器的工作过程 这里记录一下PX4中MPU6000加速度计陀螺仪的解读过程 xff0c 从mpu6000
  • Pixhawk原生固件PX4之日期时间的确定

    欢迎交流 个人 Gitter 交流平台 xff0c 点击直达 xff1a 偶然注意到PX4日志中老是出现类似于2000 01 01 00 00 00这种日期 有兴趣的可以搜索一下千年虫问题 xff0c 于是结合代码进行了一波分析 最后定位到
  • Pixhawk原生固件PX4之添外置传感器MPU6500

    欢迎交流 个人 Gitter 交流平台 xff0c 点击直达 xff1a 成功的在Pixhawk上添加了一个自定义的传感器MPU6500 Pixhawk飞控板上空余出一个SPI4接口 提示 xff1a 多出来的GPIO EXT引脚可以作为片
  • 多旋翼无人机进阶教程

    无人机是一个系统的工程 xff0c 不可谓不庞大 开源飞控盛行 xff0c 重复造轮子的工作实在无需再做 但是若决定真正的去研究飞控 xff0c 必须从本质出发 xff0c 熟悉并了解其实现原理 纷繁复杂的资料 xff0c 让人无法分辨 笔
  • Pixhawk原生固件PX4之MAVLink外部通讯

    欢迎交流 个人 Gitter 交流平台 xff0c 点击直达 xff1a 目前的需求是 xff1a 一台电脑连数传 xff0c Pixhawk飞控上电连数传 xff0c 电脑向Pixhawk发送MAVlink消息 至少有5种方案 xff1a
  • rtsp流浏览器播放方案

    rtsp流在主流浏览器并不支持直接播放 比如大华的视频流 xff1a rtsp admin 123456 64 192 168 10 129 cam realmonitor channel 61 1 amp subtype 61 0 xff
  • PX4开发指南中文版维护说明

    PX4中文维基现已与PX4开发者官网合并 现在大家可以直接进入官网进行语言切换 PX4中文版的维护依然需要你的贡献 贡献说明 xff1a 官方的GitHub仓库为https github com PX4 Devguide 我将其Fork后的
  • OpenCV图像坐标系与行列宽高的关系

    刚开始接触图像处理 xff0c 关于图像坐标系与行列宽高的关系感到纠结 xff0c 但是似乎没有更好的处理方法了 xff0c 其对应关系大致如下 row 61 61 height 61 61 Point y col 61 61 width
  • Pixhawk原生固件PX4之位姿控制算法解读

    欢迎交流 个人 Gitter 交流平台 xff0c 点击直达 xff1a 参考文献 xff1a Minimum Snap Trajectory Generation and Control for Quadrotors PX4中多旋翼无人机
  • Pixhawk精准着陆之IRLock配置

    安装说明 下载Pixymon和pixy对应markone的固件 xff0c 在这里 固件必须是firmware IRLOCKpixy 1 0 1 hex irlock 61 markone 然后固件里 irlock 61 pixy 给Pix
  • Pixhawk原生固件PX4之offboard

    offboard PX4中的offboard 暂译作外部控制 是一个非常强大的功能 可以接受来自外部的控制指令 xff0c 按照目前的了解来看 xff0c offboard搭配上MAVROS以及类似于TX1 NUC板载计算器 xff0c 在
  • 相机标定原理

    cnblogs上的这篇讲相机标定的博文值得一看 csdn上这篇也可以参考 相机标定基础知识 相机标定技术涉及到一些数学原理和几何模型 xff0c 这些数学原理和几何模型是相机标定算法使用和进一步发展的基础 下面对相机标定技术中涉及到的齐次坐
  • VS Code的Git插件

    Visual Studio Code是微软公司推出的一款跨平台代码编辑 Edit 编译 Build 调试 Debug 工具 笔者认为其相当于是Sublime Text这款代码编辑器的升级版 集成了丰富的插件 xff0c 包括代码管理中极为常
  • Ubuntu缺少libncurses.so.5的解决办法

    执行arm none eabi gdb时候出错 xff1a arm none eabi gdb error while loading shared libraries libncurses so 5 cannot open shared
  • freeRTOS的任务抢占和时间片轮转

    实时操作系统的一个特点就是可以任务抢占 xff0c 高优先级的任务可以抢占比自己优先级低的任务 xff0c 如果新任务优先级和当前人任务优先级一样 xff0c 且在使能了时间片的方式的话 xff0c 二者以时间片的方式共享cpu xff0c
  • RH850 F1L freeRTOS 任务栈的切换

    pxCurrentTCB指向的任务块中 xff0c 有2个和栈相关的变量pxTopOfStack和pxStack pxTopOfStack指向当前堆栈栈顶 xff0c 随着进栈出栈 xff0c pxTopOfStack指向的位置是会变化的
  • opencv 3.0 图像去畸变 undistortion

    主要用到的是 initUndistortRectif yMap这个函数 在opencv中这个函数是用于 去除镜头畸变的图像拉伸 为了快速算法 xff1a 使用了坐标查找变和双线性差值的方法 先上结果图 原图 去畸变至全图 去畸变并保留最大图
  • 多传感器融合MSF算法源码阅读(三)

    文章目录 1 触发测量更新回调函数2 测量更新状态量3 总结 无人驾驶算法学习 xff08 六 xff09 xff1a 多传感器融合MSF算法 多传感器融合MSF算法源码阅读 一 多传感器融合MSF算法源码阅读 二 1 触发测量更新回调函数
  • 视觉VIO:S-MSCKF算法学习(一)

    文章目录 1 算法简介2 算法调试3 代码解读3 1 前端 ImageProcessor3 2 后端 Msckf Vio 1 算法简介 Mingyang Li博士于2007年提出MSCKF Multi State Constraint Ka
  • 无人驾驶算法学习(六):多传感器融合MSF算法

    文章目录 1 引言2 算法理论2 1 MSF基本模型2 2 预测2 3 测量与更新 3 核心代码分析4 代码实战 1 引言 本文的多传感器融合是建立在读懂 Quaternion kinematics for the error state

随机推荐