STM32 H743 MPU的配置使用方法

目录

[1 STM32H743 RAM简介.... 1](#1 STM32H743 RAM简介.... 1)

[1.1 ITCM.. 1](#1.1 ITCM.. 1)

[1.2 DTCM.. 2](#1.2 DTCM.. 2)

[1.3 AXI SRAM... 2](#1.3 AXI SRAM... 2)

[1.4 SRAM1、SRAM2、SRAM3. 2](#1.4 SRAM1、SRAM2、SRAM3. 2)

[1.5 SRAM4.. 5](#1.5 SRAM4.. 5)

[1.6 Backup SRAM... 5](#1.6 Backup SRAM... 5)

[2 各部分内存实际使用方式.... 5](#2 各部分内存实际使用方式.... 5)

[2.1 通过sct文件配置各部分内存段.... 5](#2.1 通过sct文件配置各部分内存段.... 5)

[2.2 使用不同内存段定义变量.... 8](#2.2 使用不同内存段定义变量.... 8)

  1. STM32H743 MPU简介

MPU 即内存保护单元,F1和F4系列也有,但是基本不用,H7为什么要使用MPU了呢?因为要使用Cache来提高性能,使用Cache的前提是必须配置MPU,所以H7的MPU基本上是用来支持Cache来使用的。

MPU 的主要功能是定义一些存储区域的访问规则, 简单的说就是内存保护、外设保护和代码访问保护,STM32H7 整个4GB存储映射空间都可以配置,但是归根结底MPU配置主要是配合Cache使用,所以后面也主要以这个为重点讲解。

    1. MPU功能实现,区域划分

MPU可以配置保护16个内存区域,每个内存区域叫做一个region(这16个内存域是独立配置的),region号范围是0-15,还有默认区 default region,也叫作背景区,序号-1。由于这些内存区可以嵌套和重叠,所以这些区域在嵌套或者重叠的时候有个优先级的问题。序号15的优先级最高,以此递减,序号-1,即背景区的优先级最低。这些优先级是固定的。

上共有7个区,背景区和序号0-5的区。内存区4跟内存区0和1有重叠部分,那么重叠部分将按照内存区4的配置规则执行;内存区5被完全包含在内存区3里面,那么这部分内存区将按照内存区5的配置规则执行。

每个区域最小要求256字节,每个区域还可以配置为8个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是32字节,正好跟Cache的Cache Line大小一样。

特别解释:背景区在所有region中的优先级最低,是在4GB空间内除去所有定义好的region区域以外的区域,背景区不单独配置时,其内存类型和Cache的策略与所处区域的功能相关,每个区域会有不同的MPU属性和cache策略,如下:

特别注意:可以看到表中AXI SRAM、SRAM1-4等若没有配置MPU,Cache策略默认都是cacheable,write back,此时如果使能了cache,但是MPU没有配置,作为背景区,具有write back的cache的策略,就会出现cache与RAM数据一致性的问题,会导致数据错乱,因此如果使用Cache,建议将整个RAM区域中使用到的内存块,全部进行MPU配置,确定好是否使用cache,使用什么样的cache策略。

    1. MPU支持的三种内存类型:
  1. Normal(普通内存):用于存放代码和数据(如 Flash、SRAM),允许缓存,可以开启 Cache 来加速访问,可能乱序执行。常见的SRAM (DTCM, SRAM1/2/3/4) 中的变量、堆栈、缓冲区,Flash 中的代码段等。
  2. Device(设备内存):用于访问外设寄存器(如 UART、GPIO、TIM 的寄存器),不允许缓存,强制关掉 D-Cache,不会乱序执行,可缓存。包括所有外设寄存器区域(地址 0x40000000 开始)
  3. Strongly-Ordered(强顺序内存):最为严格的内存类型,通常用于系统控制区域,不允许缓存,强制关掉 D-Cache,不会乱序执行,不可缓存。包括系统控制寄存器(SCB、MPU 本身、NVIC等,地址 0xE0000000 开始)

其中前两种实际开发中用户最常用,第三种不常用,几乎不需要用户配置,这里需要注意的是,MPU的内存类型和Cache的策略类型不是一回事儿,Normal类型内存可能支持write back和write through类型的cache策略。

  1. MPU配置方法
    1. MPU参数详解

下面对MPU配置的各个参数进行说明,下图所示:

  1. Enable:表示该区域是否使能MPU的配置,这个肯定是配置成MPU_REGION_ENABLE,不然MPU的配置不生效没有意义;
  2. Number:该参数为当前配置区域的region号,范围是0-15,因为MPU支持16个区域的配置,并且区域号越大,不同区域地址重叠时优先级权限越高,有相应的宏定义,可直接使用,例如:MPU_REGION_NUMBER1;
  3. BaseAddress:MPU配置区域的起始地址,要求32字节对齐;
  4. Size:MPU配置区域的长度,要求32字节对齐,有相应的宏定义,可直接使用,例如:MPU_REGION_SIZE_512KB;
  5. SubRegionDisable:用于配置是否禁用特定的子区域。每个MPU配置的region区域可以再划分为8个子区域(固定划分为8个长度相等的子区域),每个子区域大小 = 区域总大小 / 8,每个子区域用一个 bit 来控制是否被禁用(即从该 MPU 区域中排除出去)。

Bit0-Bit7分别对应子区域0-子区域7

当bit值为0时,表示当前子区域可用,当bit值为1时,表示当前子区域禁用。

默认情况下该字段都填0,不禁用任何子区域。

  1. TypeExtField:简写为TEX,该字段配合C/B/S三个字段,共同用于设置MPU内存类型和Cache策略,该字段共三个值:MPU_TEX_LEVEL0、MPU_TEX_LEVEL1、MPU_TEX_LEVEL2,常用的只有前两个,第三个用不上;
  2. AccessPermission:表示当前区域的访问权限,一般配置MPU都是用来使用cache,所以该字段默认都赋值全部可访问:MPU_REGION_FULL_ACCESS
  3. DisableExec:表示该内存区域是否允许指令执行,一般存放数据的区域(如 SRAM、DMA Buffer)、栈空间(Stack)、堆(Heap)设置为禁止执行:MPU_INSTRUCTION_ACCESS_DISABLE;存放程序代码的区域(Flash)、用于跳转表的 RAM(如 BootLoader 跳转代码)设置为允许执行:MPU_INSTRUCTION_ACCESS_ENABLE

我们使用Cache基本都在RAM上,因此该字段直接设置成禁止执行 MPU_INSTRUCTION_ACCESS_DISABLE;

  1. IsShareable:简写为S,该字段需要配合TEX/C/B三个字段来使用,共同决定MPU内存类型和Cache策略。该字段指示当前内存区域是否可以多主控共享操作,手册中解释为多处理器之间是否可以共享一片内存的操作,例如双核CPU,两个CPU之间的共享,但是实际测试发现,如果单核CPU,在使用cache功能时使能了该字段的共享,虽然无多核操作,但是会影响cache的性能,甚至类似于no cache的情况,因此该区域配置MPU如果是为了使用cache提升性能,一定要把共享禁用,即配置为:MPU_ACCESS_NOT_SHAREABLE;

特别说明:该字段的共享指的是多处理器之间,CPU和DMA之间虽然可能都操作内存,但是不属于该字段控制的范畴,因此用于DMA数据处理的存储空间,该字段也要配置为禁止共享,禁止共享不会影响CPU和DMA都操作该部分空间的数据;

  1. IsCacheable:简写为C,指示该区域是否使用cache,该字段需要配合TEX/B/S三个字段来使用,共同决定MPU内存类型和Cache策略,有相应的宏定义,可直接使用,例如:MPU_ACCESS_CACHEABLE;
  2. IsBufferable:简写为B,指示该区域是否使用缓存,该字段需要配合TEX/C/S三个字段来使用,共同决定MPU内存类型和Cache策略,有相应的宏定义,可直接使用,例如:MPU_ACCESS_CACHEABLE;

参数设置完毕后,使用HAL_MPU_ConfigRegion()函数初始化相应区域的MPU配置

在使用HAL_MPU_Enable()函数来使能MPU功能

其中HAL_MPU_Enable()的形参有四种类型,分别是:

cpp 复制代码
#define  MPU_HFNMI_PRIVDEF_NONE      ((uint32_t)0x00000000)

#define  MPU_HARDFAULT_NMI           ((uint32_t)0x00000002)

#define  MPU_PRIVILEGED_DEFAULT      ((uint32_t)0x00000004)

#define  MPU_HFNMI_PRIVDEF           ((uint32_t)0x00000006)

这四个宏定义是用于配置MPU控制寄存器(MPU_CTRL)的参数,主要控制MPU在异常(HardFault、NMI)和特权模式下的行为,MPU_CTRL寄存器中两个相关控制位含义如下:

下面详细介绍这几个参数含义:

  1. MPU_HFNMI_PRIVDEF_NONE:

背景区:特权模式下禁用背景区,地址未命中任何 MPU 区域会触发 MemManage Fault

HardFault/NMI:MPU 在异常发生时关闭(防止异常处理程序因 MPU 而无法执行)。

  1. MPU_HARDFAULT_NMI:

背景区:特权模式下禁用背景区,地址未命中任何 MPU 区域会触发 MemManage Fault

HardFault/NMI:MPU 仍然生效(异常处理程序也受 MPU 约束,如果访问未配置区域,会陷入死循环无法恢复)。

  1. MPU_PRIVILEGED_DEFAULT:

背景区:特权模式下启用背景区,未命中区域可访问,使用默认属性

HardFault/NMI:MPU 关闭(异常处理程序不受 MPU 约束,保证异常能正常处理)。

  1. MPU_HFNMI_PRIVDEF:

背景区:特权模式下禁用背景区,地址未命中任何 MPU 区域会触发 MemManage Fault

HardFault/NMI:MPU 仍然生效(异常处理程序也受 MPU 约束)。

差异对照表:

最常用的是第三种类型:MPU_PRIVILEGED_DEFAULT

    1. MPU设置与内存类型和Cache策略的对应关系

MPU配置内存类型和Cache策略与TEX、C、B、S几个参数相关,不同参数设置对应的不同内存类型和Cache策略具体如下:

关键说明:

Write-Back 分为两种:Write-Allocate (TEX=001, C=1, B=1) 和 No-Allocate (TEX=000, C=1, B=1)。

STM32H7 的算法计算区应使用 TEX=001, C=1, B=1 (Write-Allocate) 以获得最高性能。

外设寄存器 必须使用 Device 类型 (TEX=000, C=0, B=1),不允许缓存。

对于S位的设置如下:

对于常见的应用场景可配置如下:

    1. MPU配置代码实例
cpp 复制代码
void MPU_Config(void)
{
	////↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓MPU配置实例↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓////
	/*
	 * 1.DTCM:不进行MPU和Cache的策略配置,直接使用,用于存放程序启动的栈、.bss、.data以及程序中使用的各种全局、静态变量
	 * 2.AXI SRAM:MPU内存类型全部配置为Normal,允许Cache,Cache策略全部配置为Write back,来获取最高性能,用于存放算法和程序过程中
	 * 使用的各种全局、静态变量,DMA读写的变量不要放在该区域,否则会存在数据一致性的问题,那样就需要手动维护Cache的数据
	 * 一致性,比较容易出错,风险高,如果只有CPU操作Cache和RAM,则不需要手动维护Cache数据的一致性问题
	 * 3.SRAM1-SRAM3:设置与AXI SRAM一致
	 * 4.SRAM4:MPU内存类型全部配置为Normal,不允许Cache,设置为No Cache,用于外设DMA的数据读写使用,因为没有使能Cache,
	 * 因此DMA和CPU都是直接操作内存数据,无数据一致性问题,但是性能比启用cache稍慢,如果DMA数据区只是拷贝数据,
	 * 不直接参与算法计算,则性能损耗几乎无影响
	 */
	
	//MPU初始化结构体
  MPU_Region_InitTypeDef MPU_InitStruct;

  //初始化前先禁止MPU
  HAL_MPU_Disable();
  
	//AXI SRAM区域的MPU设置
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;									//配置的该内存区域是否使能
  MPU_InitStruct.Number = MPU_REGION_NUMBER1;									//配置的该内存区域的region编号(0-15)
  MPU_InitStruct.BaseAddress = 0x24000000;										//配置的该内存区域的起始地址(要求32字节对齐)
  MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;								//配置的该内存区域的长度(要求32字节对齐)
  MPU_InitStruct.SubRegionDisable = 0x0;											//配置的该内存区域是否要有禁用的子区域
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;								//TEX,与C/B位共同决定内存类型和缓存策略
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;		//配置的该内存区域访问权限
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;//配置的该内存区域是否允许指令执行
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;			//配置的该内存区域是否允许多主控共享
  MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;					//配置的该内存区域是否允许Cache,与TEX/B位共同决定内存类型和缓存策略
  MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;				//配置的该内存区域是否允许缓存,与TEX/C位共同决定内存类型和缓存策略
	//初始化MPU配置
  HAL_MPU_ConfigRegion(&MPU_InitStruct);

	//SRAM1区域的MPU设置
  MPU_InitStruct.Number = MPU_REGION_NUMBER2;									//配置的该内存区域的region编号(0-15)
  MPU_InitStruct.BaseAddress = 0x30000000;										//配置的该内存区域的起始地址(要求32字节对齐)
  MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;								//配置的该内存区域的长度(要求32字节对齐)
	//初始化MPU配置
  HAL_MPU_ConfigRegion(&MPU_InitStruct);

	//SRAM2区域的MPU设置
  MPU_InitStruct.Number = MPU_REGION_NUMBER3;									//配置的该内存区域的region编号(0-15)
  MPU_InitStruct.BaseAddress = 0x30020000;										//配置的该内存区域的起始地址(要求32字节对齐)
  MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;								//配置的该内存区域的长度(要求32字节对齐)
	//初始化MPU配置
  HAL_MPU_ConfigRegion(&MPU_InitStruct);

	//SRAM3区域的MPU设置
  MPU_InitStruct.Number = MPU_REGION_NUMBER4;									//配置的该内存区域的region编号(0-15)
  MPU_InitStruct.BaseAddress = 0x30040000;										//配置的该内存区域的起始地址(要求32字节对齐)
  MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;									//配置的该内存区域的长度(要求32字节对齐)
	//初始化MPU配置
  HAL_MPU_ConfigRegion(&MPU_InitStruct);

	//SRAM4区域的MPU设置-DMA数据区,不可cache,不可缓存,no cache策略
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;									//配置的该内存区域是否使能
  MPU_InitStruct.Number = MPU_REGION_NUMBER5;									//配置的该内存区域的region编号(0-15)
  MPU_InitStruct.BaseAddress = 0x38000000;										//配置的该内存区域的起始地址(要求32字节对齐)
  MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;									//配置的该内存区域的长度(要求32字节对齐)
  MPU_InitStruct.SubRegionDisable = 0x0;											//配置的该内存区域是否要有禁用的子区域
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;								//TEX,与C/B位共同决定内存类型和缓存策略
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;		//配置的该内存区域访问权限
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;//配置的该内存区域是否允许指令执行
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;			//配置的该内存区域是否允许多主控共享
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;			//配置的该内存区域是否允许Cache,与TEX/B位共同决定内存类型和缓存策略
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;		//配置的该内存区域是否允许缓存,与TEX/C位共同决定内存类型和缓存策略
	//初始化MPU配置
  HAL_MPU_ConfigRegion(&MPU_InitStruct);


  //使能MPU配置
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
相关推荐
小此方1 小时前
Re:Linux系统篇(十二)工具篇 · 四:make与Makefile:高效管理 C++ 工程项目构建
linux·运维·c++·开发工具
枳实-叶1 小时前
【Linux驱动开发】第7天:总线-设备-驱动三大核心模型:通俗讲解+完整流程图
linux·驱动开发·流程图
张健11564096481 小时前
MSP主堆栈指针
单片机
BS_Li1 小时前
【Linux网络编程】应用层自定义协议与序列化
linux·服务器·网络
東隅已逝,桑榆非晚1 小时前
深⼊理解指针(3)
c语言·数据结构·笔记·算法·排序算法
泓博1 小时前
docker ubuntu源码安装openclaw的常见问题
java·linux·开发语言·ai
小此方1 小时前
Re:Linux系统篇(十一)工具篇 · 三:三分钟学会gcc/g++编译工具&&初步认识动静态库&&重温编译基本原理
linux·运维·服务器·开发工具
小吴伴学者1 小时前
Linux TX报文处理流程解析
linux
云小逸1 小时前
【Codex 使用教程:从项目规则、Skills、Rules 到 Hooks】
c++·人工智能·ai·codex