You copied the Doc URL to your clipboard.

Initializing and configuring an MPU

The MPU must be configured before it is enabled. A Data Memory Barrier (DMB) operation is recommended to force any outstanding writes to memory before enabling the MPU.

The necessary memory types must be encoded into the MAIR registers so that can be referenced from the MPU_RLAR register for each region. MPU_RNR selects which region MPU_RBAR and MPU_RLAR are currently configuring. The start and end address of each region can be programmed into the MPU_RBAR and MPU_RLAR registers, along with the required access permissions, shareability, and executability.

When all the required regions have been configured the MPU can then be enabled by setting the ENABLE bit in MPU_CTRL. To ensure that any subsequent memory accesses use the new MPU configuration, software must execute a DMB followed by an Instruction Synchronization Barrier.

When all the required regions have been configured, the MPU can then be enabled by setting the ENABLE bit in MPU_CTRL. To ensure that any subsequent memory accesses use the new MPU configuration, software must execute a DMB followed by an Instruction Synchronization Barrier (ISB).

The following example code sets up and configures a number of memory regions and sets the memory attributes for each region:

Include the CMSIS headers and use the CMSIS MPU structure to access the MPU configuration registers.

void ARMv8-Ml_mpu_config(void);
/* General MPU Masks */
#define REGION_ENABLE   0x00000001
/* Shareability */
#define NON_SHAREABLE   0x00
#define RESERVED        0x08
#define OUTER_SHAREABLE 0x10
#define INNER_SHAREABLE 0x18

/* Access Permissions */
#define EXEC_NEVER      0x01 /* All instruction fetches abort */
#define RW_P_ONLY       0x00 /* Read/Write, Privileged code only */
#define RW_P_U          0x02 /* Read/Write, Any Privilege Level */
#define RO_P_ONLY       0x04 /* Read-Only, Privileged code only */
#define RO_P_U          0x06 /* Read-Only, Any Privilege Level */

/* Read/Write Allocation Configurations for Cacheable Memory
Attr<n>[7:4] and Attr<n>[3:0] are of the format: 0bXXRW */
#define R_NON_W_NON     0x0 /* Do not allocate Read/Write */
#define R_NON_W_ALLOC   0x1 /* Do not allocate Read, Allocate Write */
#define R_ALLOC_W_NON   0x2 /* Allocate Read, Do not allocate Write */
#define R_ALLOC_W_ALLOC 0x3 /* Allocate Read/Write */

/* Memory Attribute Masks */
#define DEVICE          0x0F
#define NORMAL_OUTER    0xF0
#define NORMAL_INNER    0x0F

/* Memory Attributes */
#define DEVICE_NG_NR_NE 0x00 /* Device, Non-Gathering, Non-Reordering, Non-Early-Write-Acknowledgement */
#define DEVICE_NG_NR_E  0x04 /* Device, Non-Gathering, Non-Reordering, Early-Write-Acknowledgement */
#define DEVICE_NG_R_E   0x08 /* Device, Non-Gathering, Reordering, Early-Write-Acknowledgement */
#define DEVICE_G_R_E    0x0C /* Device, Gathering, Reordering, Early-Write-Acknowledgement */

#define NORMAL_O_WT_T   0x00 /* Normal, Outer Write-through transient (if RW not 00) */
#define NORMAL_O_NC     0x40 /* Normal, Outer Non-cacheable (if RW is 00) */
#define NORMAL_O_WB_T   0x40 /* Normal, Outer Write-back transient (if RW not 00) */
#define NORMAL_O_WT_NT  0x80 /* Normal, Outer Write-through non-transient */
#define NORMAL_O_WB_NT  0xC0 /* Normal, Outer Write-back non-transient */

#define NORMAL_I_WT_T   0x00 /* Normal, Inner Write-through transient (if RW not 00) */
#define NORMAL_I_NC     0x04 /* Normal, Inner Non-cacheable (if RW is 00) */
#define NORMAL_I_WB_T   0x04 /* Normal, Inner Write-back transient (if RW not 00) */
#define NORMAL_I_WT_NT  0x08 /* Normal, Inner Write-through non-transient */
#define NORMAL_I_WB_NT  0x0C /* Normal, Inner Write-back non-transient */

Any outstanding memory transactions must be forced to complete by executing a DMB instruction and the MPU disabled before it can be configured.

#include <CMSDK_ARMv8-ML.h>
#include <core_ARMv8-Ml.h>
#include "mpu.h"
void ARMv8-Ml_mpu_config(void)
{
	/* Force any outstanding transfers to complete before disabling MPU */
	__asm volatile("dmb\n");
	/* Disable MPU */
MPU->CTRL = 0;
/* Enable Memory Management Fault Handler using SHCSR */
	SCB->SHCSR |= (1 << SCB_SHCSR_MEMFAULTENA_Pos);

	/* Check number of supported MPU regions */
	uint32_t MPU_regions = (MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos;

Any memory types that are to be assigned to a region must be encoded into the MAIR registers. The encoded types can then be indexed by the Region Limit Register for that region. Region 0 is configured as Normal memory, inner or outer non-cacheable. Region 1 is Device-nGnRnE.

	/* Configure MPU Memory Attribution Indirection Registers */
	uint32_t non_cacheable_attr = NORMAL_O_NC | NORMAL_I_NC;
	uint32_t device_attr = DEVICE_NG_NR_NE;
	MPU->MAIR0 = MPU->MAIR0 | ((device_attr << MPU_MAIR0_Attr1_Pos) | (non_cacheable_attr));
	/* MPU_MAIR0 index 0: SRAM1, SRAM2&3, System ROM
	   MPU_MAIR0 index 1: Device */

Regions are configured by encoding the region start address, shareability, executability and read/writeability in the Region Base Address Register. The Region Limit Register holds the limit address of the region and an index value that selects the appropriate type from those encoded in the MAIRs.

This example selects region zero and configure it to cover the addresses 0x0 to 0x007FFFFF. The memory type is indexed to MAIR region 0 (Normal, non-cacheable) and the regions is configured as Non-shareable, executable, and read-only. This memory type might be used to cover the initial boot code and exception handler stored in ROM or Flash.

	/* Configure region 0: NS SRAM1 (Normal, Non-Shareable, RO, Any Privilege Level)*/
	MPU->RNR = 0;
	MPU->RBAR = (0x00000000 & MPU_RBAR_ADDR_Msk) | NON_SHAREABLE | RO_P_U;
	MPU->RLAR = (0x007FFFFF & MPU_RLAR_LIMIT_Msk) | ((0 << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | REGION_ENABLE;

Data regions could be configured in a similar way but would usually be configured to be writeable and eXecute Never.

Normal memory is unsuitable for peripheral control registers as it allows speculative read accesses. Code must configure one or more device regions to cover peripheral register space.

Device memory should always be configured as eXecute Never to prevent instruction speculation.

Other regions can be configured in the same way. In a system with caches some regions of Normal memory might be configured as inner or outer cacheable. Some Normal memory regions might be only accessible to privileged code and peripheral region access might also be limited to privileged code.

When all regions have been programmed any unused regions should be disabled. The total number of regions can be read from the MUP_TYPE register.

	/* Configure region 1: NS SRAM2&3 (Normal, Non-Shareable, RW, Any Privilege Level) */
	MPU->RNR = 1;		
	MPU->RBAR = (0x20000000 & MPU_RBAR_ADDR_Msk) | NON_SHAREABLE | RW_P_U;
	MPU->RLAR = (0x207FFFFF & MPU_RLAR_LIMIT_Msk) | ((0 << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | REGION_ENABLE;

	/* Configure region 2: NS CMSDK APB (Device-nGnRnE, Non-Shareable, RW, Any Privilege Level, XN) */
	MPU->RNR = 2;
	MPU->RBAR = (0x40000000 & MPU_RBAR_ADDR_Msk) | NON_SHAREABLE | RW_P_U | EXEC_NEVER;
	MPU->RLAR = (0x4000FFFF & MPU_RLAR_LIMIT_Msk) | ((1 << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | REGION_ENABLE;

	/* Configure region 3: NS GPIO (Device-nGnRnE, Non-Shareable, RW, Any Privilege Level, XN) */
	MPU->RNR = 3;
	MPU->RBAR = (0x40010000 & MPU_RBAR_ADDR_Msk) | NON_SHAREABLE | RW_P_U | EXEC_NEVER;
	MPU->RLAR = (0x40013FFF & MPU_RLAR_LIMIT_Msk) | ((1 << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | REGION_ENABLE;

	/* Configure region 4: NS Default AHB Slave (Device-nGnRnE, Non-Shareable, RO, Any Privilege Level, XN) */
	MPU->RNR = 4;
	MPU->RBAR = (0x40014000 & MPU_RBAR_ADDR_Msk) | NON_SHAREABLE | RO_P_U | EXEC_NEVER;
	MPU->RLAR = (0x40017FFF & MPU_RLAR_LIMIT_Msk) | ((1 << MPU_RLAR_AttrIndx_Pos) amp; MPU_RLAR_AttrIndx_Msk) | REGION_ENABLE;
	
	/* Configure region 5: System ROM (Normal, Non-Shareable, RO, Any Privilege Level) */
	MPU->RNR = 5;
	MPU->RBAR = (0xF0000000 & MPU_RBAR_ADDR_Msk) | NON_SHAREABLE | RO_P_U;
	MPU->RLAR = (0xF0000FFF & MPU_RLAR_LIMIT_Msk) | ((0 << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | REGION_ENABLE;
	
	/* Disable all other regions */
	for (uint32_t i=6; i<MPU_regions; i++) {
		MPU->RNR = i;
		MPU->RLAR &= 0x0;
	}

The final step is to enable the MPU by writing to MPU_CTRL. Code should then execute a memory barrier to ensure that the register updates are seen by any subsequent memory accesses. An Instruction Synchronization Barrier (ISB) ensures the updated configuration used by any subsequent instructions.

	/* Enable MPU and the default memory map as a background region (acts as region number -1) for privileged access only (MPU_CTRL.PRIVDEFENA). */
	MPU->CTRL = 5;
	/* Insert DSB followed by ISB */
	__asm volatile(
		"dsb\n"
	  "isb\n"
		);
}

#include <CMSDK_ARMv8-ML.h>
#include <core_ARMv8-Ml.h>
#include <stdio.h>

extern void ARMv8-Ml_mpu_config(void);
int main(void)
{
	ARMv8-Ml_mpu_config();    /* configure MPU regions */
	printf("MPU configured successfully!\r\n");
	while(1);
}
Was this page helpful? Yes No