Embedded Design Handbook

ID 683689
Date 8/28/2023
Public
Document Table of Contents

4.3.3.4.1. MPU Utilities

You can find helpful MPU utility functions and macros in the mpu_utils.c and mpu_utils.h files in each software example. The following functions are the most important for you to understand:

  • nios2_mpu_data_init()—A system-specific function. In your own code, write an equivalent function to specify the MPU data regions in your design.
  • nios2_mpu_inst_init()—A system-specific function. In your own code, write an equivalent function to specify the MPU instruction regions in your design.
  • nios2_mpu_load_region()—Configures an MPU region with specific parameters.
  • nios2_mpu_enable()—Enables the entire MPU.
  • nios2_mpu_disable()—Disables the entire MPU.

Each utility function makes use of the Nios2MPURegion data structure shown in the example below.

Nios2MPURegion Data Structure

typedef struct
{
	unsigned int base;
	unsigned int index;
	unsigned int mask;
	unsigned int c;
	
	unsigned int perm;
} Nios2MPURegion;

The example below shows nios2_mpu_inst_init() for the mpu_basic software example. The constants NIOS2_MPU_NUM_INST_REGIONS and NIOS2_MPU_REGION_USES_LIMIT are defined in system.h.

In nios2_mpu_inst_init() in the mpu_basic Software Example, region[0] grants execution access to the instr_ram memory in both user and supervisor modes, as shown in Figure 41. region[1] grants execution access to the break and trace memory (starting at 0x1000) in both modes. The other two MPU instruction regions grant no execution permissions to the entire Nios® II address space. Because their priorities, 2 and 3, are lower than the first two regions, the code stored in the instr_ram runs, and the break and trace features work correctly. However, if code attempts to execute outside those regions, the MPU triggers an exception.

The final statement in nios2_mpu_inst_init() calls nios2_mpu_load_region() to configure the region with the information contained in the structure.

nios2_mpu_inst_init() in the mpu_basic Software Example

void nios2_mpu_inst_init()
{
	unsigned int mask;
	Nios2MPURegion region[NIOS2_MPU_NUM_INST_REGIONS];

	//Main instruction region.
	region[0].index = 0;
		
	region[0].base = 0x400; // Byte Address 0x10000
#ifdef NIOS2_MPU_REGION_USES_LIMIT
	region[0].mask = 0x500; // Byte Address 0x14000
#else
	region[0].mask = 0x1ffff00;
#endif
	region[0].c = 1;
	region[0].perm = MPU_INST_PERM_SUPER_EXEC_USER_EXEC;

	//Instruction region for break address.
	region[1].index = 1;
	region[1].base = 0x40; // Byte Address 0x1000
#ifdef NIOS2_MPU_REGION_USES_LIMIT
	region[1].mask = 0x60; // Byte Address 0x1800
#else
	region[1].mask = 0x1ffffe0;
#endif
	region[1].c = 1;
	region[1].perm = MPU_INST_PERM_SUPER_EXEC_USER_EXEC;

	//Rest of the regions are maximally sized and permissive.
#ifdef NIOS2_MPU_REGION_USES_LIMIT
	mask = 0x2000000;
#else
	mask = 0x0;
#endif
	unsigned int num_of_region = NIOS2_MPU_NUM_INST_REGIONS;
	unsigned int index;
	for (index = 2; index < num_of_region; index++){
		region[index].base = 0x0;
		region[index].index = index;
		region[index].mask = mask;
		region[index].c = 0;
		region[index].perm = MPU_INST_PERM_SUPER_NONE_USER_NONE;
	}
	nios2_mpu_load_region(region, num_of_region, 0);
}

The example below shows the function prototype for nios2_mpu_load_region().

nios2_mpu_load_region()

void nios2_mpu_load_region (
	Nios2MPURegion region[],
	unsigned int num_of_region,
	unsigned int d);
The following list shows the arguments to nios2_mpu_load_region():
  • Nios2MPURegion—An array of data structures, each representing an MPU region
  • num_of_region—The number of regions
  • d—The region type (instruction or data)

nios2_mpu_load_region() configures the MPU according to the arguments passed by the calling function.

The MPU is disabled by default at system restart. After the MPU is configured, the example uses nios2_mpu_enable() and nios2_mpu_disable() to enable and disable the MPU. Whenever you reconfigure the MPU, you must first disable it, and re-enable it after configuring.

The software examples accompanying this section are commented to help you understand how each example works. Most of the complexity of managing the MPU and its regions is embodied in the MPU utility functions and macros in mpu_utils.c and mpu_utils.h, allowing you to focus on the top-level software flow.