大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是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特性差異
處理器版本 | 存在MPU | 最大主保護區域(k) | 主保護區域可重疊 | 子保護區域 | 三組Alias空間 | |
---|---|---|---|---|---|---|
Cortex-M0/1 | 無 | N/A | N/A | N/A | N/A | |
Cortex-M0+ | 有 | 8個 | 支援 | k*8個 | 不支援 | |
Cortex-M3/4 | 有 | 8個 | 支援 | k*8個 | 支援 | |
Cortex-M7 | 有 | 8/16個 | 支援 | k*8個 | 支援 | |
Cortex-M23 | 有 | 8個 | 不支援 | 不支援 | 不支援 | |
Cortex-M33/35P | 有 | 8個 | 不支援 | 不支援 | 支援 | |
Cortex-M55 | 有 | 4/8/12/16個 | 不支援 | 不支援 | 支援 |
至此,ARM Cortex-M儲存保護模組(MPU)痞子衡便介紹完畢了,掌聲在哪裡~~~
歡迎訂閱
文章會同時釋出到我的 部落格園主頁、CSDN主頁、知乎主頁、微信公眾號 平臺上。
微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。