完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第23章 STM32H7的MPU內存保護單元(重要)
本章節為大家講解STM32H7學習中的一個重要知識點MPU(Memory Protection Unit,內存保護單元),早在STM32F1和F4芯片上面也是有這個功能的,但是基本用不上。但是到了H7就得用上了,因為要設置Cache。
23.1 初學者重要提示
23.2 MPU簡介
23.3 MPU的功能實現
23.4 MPU可以配置的三種內存類型
23.5 MPU的寄存器和對應的庫參數
23.6 MPU的配置函數
23.7 總結
23.1 初學者重要提示
- 本章節主要是為下一章Cache的講解做個鋪墊,需要初學者把本章節涉及到的基礎知識點掌握了。
- 初學MPU時,可能有些知識點無法一下子就搞明白,在后續章節的各種應用后,會有一個較深的認識。這個知識點基本會貫穿整個教程。
23.2 MPU簡介
MPU可以將memory map內存映射區分為多個具有一定訪問規則的區域,通過這些規則可以實現如下功能:
- 防止不受信任的應用程序訪問受保護的內存區域。
- 防止用戶應用程序破壞操作系統使用的數據。
- 通過阻止任務訪問其它任務的數據區。
- 允許將內存區域定義為只讀,以便保護重要數據。
- 檢測意外的內存訪問。
簡單的說就是內存保護、外設保護和代碼訪問保護。
- 內存映射
內存映射就是32位的CM7內核整體可以尋址的0 到2^32 -1共計4GB的尋址空間。通過這些地址可以訪問RAM、Flash、外設等。下面是內存映射的輪廓圖,IC廠家使用時,再做細分,添加相應的硬件功能。
23.3 MPU的功能實現
MPU可以配置保護16個內存區域(這16個內存域是獨立配置的),每個區域最小要求256字節,每個區域還可以配置為8個子區域。由於子區域一般都相同大小,這樣每個子區域的大小就是32字節,正好跟Cache的Cache Line大小一樣。
MPU可以配置的16個內存區的序號范圍是0到15,還有默認區 default region,也叫作背景區,序號-1。由於這些內存區可以嵌套和重疊,所以這些區域在嵌套或者重疊的時候有個優先級的問題。序號15的優先級最高,以此遞減,序號-1,即背景區的優先級最低。這些優先級是固定的。
下面通過一個具體的實例幫助大家理解。如下所示共有7個區,背景區和序號0-5的區。內存區4跟內存區0和1有重疊部分,那么重疊部分將按照內存區4的配置規則執行;內存區5被完全包含在內存區3里面,那么這部分內存區將按照內存區5的配置規則執行。
23.4 MPU可以配置的三種內存類型
MPU可以配置的三種內存類型如下:
- Normal memory
CPU以最高效的方式加載和存儲字節、半字和字,對於這種內存區,CPU的加載或存儲不一定要按照程序列出的順序執行。
- Device memory
對於這種類型的內存區,加載和存儲要嚴格按照次序進行,這樣是為了確保寄存器按照正確順序設置。
- Strongly ordered memory
程序完全按照代碼順序執行,CPU需要等待當前的加載/存儲指令執行完畢后才執行下一條指令。這樣會導致性能下降。
23.5 MPU的寄存器和對應的庫參數
關於MPU的寄存器介紹在STM32H7的編程手冊有專門的講解說明,我們這里重點講解寄存器MPU_RASR和控制寄存器,此寄存器的定義如下:
23.5.1 RASR寄存器的XN位
XN=0表示使能指令提取,即這塊內存區可以執行程序代碼,XN=1表示禁止指令提取,即這塊內存區禁止執行程序代碼。
對應的HAL庫MPU參數如下:
/** @defgroup CORTEX_MPU_Instruction_Access CORTEX MPU Instruction Access * @{ */ #define MPU_INSTRUCTION_ACCESS_ENABLE ((uint8_t)0x00) #define MPU_INSTRUCTION_ACCESS_DISABLE ((uint8_t)0x01)
23.5.2 RASR寄存器的AP位
AP的具體定義如下:
這幾個參數對應的HAL庫MPU參數如下:
/** @defgroup CORTEX_MPU_Region_Permission_Attributes CORTEX MPU Region Permission Attributes * @{ */ #define MPU_REGION_NO_ACCESS ((uint8_t)0x00) #define MPU_REGION_PRIV_RW ((uint8_t)0x01) #define MPU_REGION_PRIV_RW_URO ((uint8_t)0x02) #define MPU_REGION_FULL_ACCESS ((uint8_t)0x03) #define MPU_REGION_PRIV_RO ((uint8_t)0x05) #define MPU_REGION_PRIV_RO_URO ((uint8_t)0x06)
23.5.3 RASR寄存器的TEX,C,B和S位
(注,這幾個位非常重要,當前先了解知識點即可,下個章節專門講解具體的作用)
TEX,C,B和S的定義如下,這僅關注TEX = 0b000和0b001,其它的TEX配置基本用不到。
TEX用於配置Cache策略,支持如下四種情況,需要配合C和B位的配置才能實現。
TEX對應的HAL庫MPU參數給了三個,實際應用中僅用到前兩個MPU_TEX_LEVEL0和MPU_TEX_LEVEL1
/** @defgroup CORTEX_MPU_TEX_Levels MPU TEX Levels * @{ */ #define MPU_TEX_LEVEL0 ((uint8_t)0x00) #define MPU_TEX_LEVEL1 ((uint8_t)0x01) #define MPU_TEX_LEVEL2 ((uint8_t)0x02)
C位對應的HAL庫MPU參數如下,用於使能或者禁止Cache。
/** @defgroup CORTEX_MPU_Access_Cacheable CORTEX MPU Instruction Access Cacheable * @{ */ #define MPU_ACCESS_CACHEABLE ((uint8_t)0x01) #define MPU_ACCESS_NOT_CACHEABLE ((uint8_t)0x00)
B位對應的HAL庫MPU參數如下,用於配合C位實現Cache模式下是否使用緩沖。
/** @defgroup CORTEX_MPU_Access_Bufferable CORTEX MPU Instruction Access Bufferable * @{ */ #define MPU_ACCESS_BUFFERABLE ((uint8_t)0x01) #define MPU_ACCESS_NOT_BUFFERABLE ((uint8_t)0x00)
S位對應的HAL庫MPU參數如下,用於解決多總線或者多核訪問的共享問題。
/** @defgroup CORTEX_MPU_Access_Shareable CORTEX MPU Instruction Access Shareable * @{ */ #define MPU_ACCESS_SHAREABLE ((uint8_t)0x01) #define MPU_ACCESS_NOT_SHAREABLE ((uint8_t)0x00)
23.5.4 RASR寄存器的SRD位
這個位用於控制內存區的子區域,使用的是bit[15:8],共計8個bit,一個bit控制一個子區域,0表示使能此子區域,1表示禁止此子區域。
一般情況,基本不使用子區域的禁止功能,所以配置HAL庫的SubRegionDisable參數時,直接取值0x00即可,表示8個子區域均使能。
23.5.5 RASR寄存器的SIZE位
SIZE位使用的是bit[5:1],共計5個bit,可以表示2^5 = 32種大小。 對應的HAL庫給出了可以配置的28個參數:
/** @defgroup CORTEX_MPU_Region_Size CORTEX MPU Region Size * @{ */ #define MPU_REGION_SIZE_32B ((uint8_t)0x04) #define MPU_REGION_SIZE_64B ((uint8_t)0x05) #define MPU_REGION_SIZE_128B ((uint8_t)0x06) #define MPU_REGION_SIZE_256B ((uint8_t)0x07) #define MPU_REGION_SIZE_512B ((uint8_t)0x08) #define MPU_REGION_SIZE_1KB ((uint8_t)0x09) #define MPU_REGION_SIZE_2KB ((uint8_t)0x0A) #define MPU_REGION_SIZE_4KB ((uint8_t)0x0B) #define MPU_REGION_SIZE_8KB ((uint8_t)0x0C) #define MPU_REGION_SIZE_16KB ((uint8_t)0x0D) #define MPU_REGION_SIZE_32KB ((uint8_t)0x0E) #define MPU_REGION_SIZE_64KB ((uint8_t)0x0F) #define MPU_REGION_SIZE_128KB ((uint8_t)0x10) #define MPU_REGION_SIZE_256KB ((uint8_t)0x11) #define MPU_REGION_SIZE_512KB ((uint8_t)0x12) #define MPU_REGION_SIZE_1MB ((uint8_t)0x13) #define MPU_REGION_SIZE_2MB ((uint8_t)0x14) #define MPU_REGION_SIZE_4MB ((uint8_t)0x15) #define MPU_REGION_SIZE_8MB ((uint8_t)0x16) #define MPU_REGION_SIZE_16MB ((uint8_t)0x17) #define MPU_REGION_SIZE_32MB ((uint8_t)0x18) #define MPU_REGION_SIZE_64MB ((uint8_t)0x19) #define MPU_REGION_SIZE_128MB ((uint8_t)0x1A) #define MPU_REGION_SIZE_256MB ((uint8_t)0x1B) #define MPU_REGION_SIZE_512MB ((uint8_t)0x1C) #define MPU_REGION_SIZE_1GB ((uint8_t)0x1D) #define MPU_REGION_SIZE_2GB ((uint8_t)0x1E) #define MPU_REGION_SIZE_4GB ((uint8_t)0x1F)
23.5.6 CTRL寄存器的各個位
控制寄存器各個位定義如下如下:
23.6 MPU的配置函數
HAL庫的stm32h7xx_hal_cortex.c文件為MPU的配置提供了三個函數:
- HAL_MPU_Disable
- HAL_MPU_Enable
- HAL_MPU_ConfigRegion
三個函數的使用都比較簡單,但是要讓配置的內存區最大限度的發揮性能是需要大量的經驗積累和測試的。具體工程要具體分析。
23.6.1 函數HAL_MPU_Disable
函數原型:
void HAL_MPU_Disable(void) { /* Make sure outstanding transfers are done */ __DMB(); /* Disable fault exceptions */ SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; /* Disable the MPU and clear the control register*/ MPU->CTRL = 0; }
函數描述:
此函數用於禁止MPU。配置MPU前要優先調用此函數禁止MPU,然后才可以配置。
23.6.2 函數HAL_MPU_Enable
函數原型:
void HAL_MPU_Enable(uint32_t MPU_Control) { /* Enable the MPU */ MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; /* Enable fault exceptions */ SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; /* Ensure MPU setting take effects */ __DSB(); __ISB(); }
函數描述:
此函數用於使能MPU,一般情況使用參數MPU_PRIVILEGED_DEFAULT。
函數參數:
此函數支持以下幾個參數:
1、 MPU_HFNMI_PRIVDEF_NONE ((uint32_t)0x00000000)
- 此參數設置MPU的CTL控制寄存器的PRIVDEFENA位為0。
表示禁止了背景區,訪問任何未使能MPU的區域均會造成內存異常MemFault。
- 此參數設置MPU的CTL控制寄存器的HFNMIENA位為0。
表示NMI不可屏蔽中斷服務程序和硬件異常中斷服務程序執行期間會關閉MPU。
2、 MPU_HARDFAULT_NMI ((uint32_t)0x00000002)
- 此參數設置MPU的CTL控制寄存器的PRIVDEFENA位為0。
表示禁止了背景區,訪問任何未使能MPU的區域均會造成內存異常MemFault。
- 此參數設置MPU的CTL控制寄存器的HFNMIENA位為1。
表示NMI不可屏蔽中斷服務程序和硬件異常中斷服務程序執行期間會保持繼續開啟MPU。
3、 MPU_PRIVILEGED_DEFAULT ((uint32_t)0x00000004)
- 此參數設置MPU的CTL控制寄存器的PRIVDEFENA位為1。
表示使能了背景區,特權級模式可以正常訪問任何未使能MPU的區域。
- 此參數設置MPU的CTL控制寄存器的HFNMIENA位為0。
表示NMI不可屏蔽中斷服務程序和硬件異常中斷服務程序執行期間會關閉MPU。
4、 MPU_HFNMI_PRIVDEF ((uint32_t)0x00000006)
- 此參數設置MPU的CTL控制寄存器的PRIVDEFENA位為1。
表示禁止了背景區,訪問任何未使能MPU的區域均會造成內存異常MemFault。
- 此參數設置MPU的CTL控制寄存器的HFNMIENA位為1。
表示NMI不可屏蔽中斷服務程序和硬件異常中斷服務程序執行期間會保持繼續開啟MPU。
23.6.3 函數HAL_MPU_ConfigRegion
函數原型:
void HAL_MPU_ConfigRegion(MPU_Region_InitTypeDef *MPU_Init) { /* Check the parameters */ assert_param(IS_MPU_REGION_NUMBER(MPU_Init->Number)); assert_param(IS_MPU_REGION_ENABLE(MPU_Init->Enable)); /* Set the Region number */ MPU->RNR = MPU_Init->Number; if ((MPU_Init->Enable) != RESET) { /* Check the parameters */ assert_param(IS_MPU_INSTRUCTION_ACCESS(MPU_Init->DisableExec)); assert_param(IS_MPU_REGION_PERMISSION_ATTRIBUTE(MPU_Init->AccessPermission)); assert_param(IS_MPU_TEX_LEVEL(MPU_Init->TypeExtField)); assert_param(IS_MPU_ACCESS_SHAREABLE(MPU_Init->IsShareable)); assert_param(IS_MPU_ACCESS_CACHEABLE(MPU_Init->IsCacheable)); assert_param(IS_MPU_ACCESS_BUFFERABLE(MPU_Init->IsBufferable)); assert_param(IS_MPU_SUB_REGION_DISABLE(MPU_Init->SubRegionDisable)); assert_param(IS_MPU_REGION_SIZE(MPU_Init->Size)); MPU->RBAR = MPU_Init->BaseAddress; MPU->RASR = ((uint32_t)MPU_Init->DisableExec << MPU_RASR_XN_Pos) | ((uint32_t)MPU_Init->AccessPermission << MPU_RASR_AP_Pos) | ((uint32_t)MPU_Init->TypeExtField << MPU_RASR_TEX_Pos) | ((uint32_t)MPU_Init->IsShareable << MPU_RASR_S_Pos) | ((uint32_t)MPU_Init->IsCacheable << MPU_RASR_C_Pos) | ((uint32_t)MPU_Init->IsBufferable << MPU_RASR_B_Pos) | ((uint32_t)MPU_Init->SubRegionDisable << MPU_RASR_SRD_Pos) | ((uint32_t)MPU_Init->Size << MPU_RASR_SIZE_Pos) | ((uint32_t)MPU_Init->Enable << MPU_RASR_ENABLE_Pos); } else { MPU->RBAR = 0x00; MPU->RASR = 0x00; } }
函數描述:
此函數用於配置MPU。
函數參數:
此函數的形參是一個MPU_Region_InitTypeDef類型的結構體變量,定義如下:
typedef struct { uint8_t Enable; uint8_t Number; uint32_t BaseAddress; uint8_t Size; uint8_t SubRegionDisable; uint8_t TypeExtField; uint8_t AccessPermission; uint8_t DisableExec; uint8_t IsShareable; uint8_t IsCacheable; uint8_t IsBufferable; }MPU_Region_InitTypeDef;
除了參數Number和BaseAddress,其它幾個參數在本章23.5小節進行了說明。
- 結構體成員Number
這個成員是用來設置內存區序號的,用戶配置的時候,推薦從Number0開始配置,最多到Number15,共計16個。對應的HAL庫參數如下:
/** @defgroup CORTEX_MPU_Region_Number CORTEX MPU Region Number * @{ */ #define MPU_REGION_NUMBER0 ((uint8_t)0x00) #define MPU_REGION_NUMBER1 ((uint8_t)0x01) #define MPU_REGION_NUMBER2 ((uint8_t)0x02) #define MPU_REGION_NUMBER3 ((uint8_t)0x03) #define MPU_REGION_NUMBER4 ((uint8_t)0x04) #define MPU_REGION_NUMBER5 ((uint8_t)0x05) #define MPU_REGION_NUMBER6 ((uint8_t)0x06) #define MPU_REGION_NUMBER7 ((uint8_t)0x07) #define MPU_REGION_NUMBER8 ((uint8_t)0x08) #define MPU_REGION_NUMBER9 ((uint8_t)0x09) #define MPU_REGION_NUMBER10 ((uint8_t)0x0A) #define MPU_REGION_NUMBER11 ((uint8_t)0x0B) #define MPU_REGION_NUMBER12 ((uint8_t)0x0C) #define MPU_REGION_NUMBER13 ((uint8_t)0x0D) #define MPU_REGION_NUMBER14 ((uint8_t)0x0E) #define MPU_REGION_NUMBER15 ((uint8_t)0x0F)
- 結構體成員BaseAddress
這個結構體成員用來設置內存區的首地址。這個參數跟結構體成員Size的配置是比較考究的,一定要保證首地址跟內存區的大小對齊,比如配置的是64KB大小的內存區,那么設置的首地址務必是64KB對齊的,也就是這個地址對64KB,即0x00010000求余數等於0,切記。
使用舉例:
下面配置了兩組。
/* ********************************************************************************************************* * 函 數 名: MPU_Config * 功能說明: 配置MPU * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); }
23.7 總結
本章節就為大家講解這么多,還是本章開頭那句話,可能有些知識點無法一下子就搞明白,在學習了后續章節的各種應用后,會有一個較深的認識。