Arm m4 mpu

Hi, I am trying to read and understand the MPU related part. The FreeRTOS version is 10.5.1, and I am using \portable\IAR\ARM_CM4F_MPU.

In the “port.c” file, Line 596, function “prvSetupMPU”. I can see we are trying to setup MPU for the unprivileged flash, privileged flash, privileged data RAM region and peripherals. What confuse me is that we are writing all their attribute(RBAR and RASR) in the same address(portMPU_REGION_BASE_ADDRESS_REG and portMPU_REGION_ATTRIBUTE_REG).

When will the data written first be used? Will it not be overwritten by data written later?

static void prvSetupMPU( void )
{
    extern uint32_t __privileged_functions_start__[];
    extern uint32_t __privileged_functions_end__[];
    extern uint32_t __FLASH_segment_start__[];
    extern uint32_t __FLASH_segment_end__[];
    extern uint32_t __privileged_data_start__[];
    extern uint32_t __privileged_data_end__[];

    /* The only permitted number of regions are 8 or 16. */
    configASSERT( ( configTOTAL_MPU_REGIONS == 8 ) || ( configTOTAL_MPU_REGIONS == 16 ) );

    /* Ensure that the configTOTAL_MPU_REGIONS is configured correctly. */
    configASSERT( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE );

    /* Check the expected MPU is present. */
    if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE )
    {
        /* First setup the unprivileged flash for unprivileged read only access. */
        portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */
                                          ( portMPU_REGION_VALID ) |
                                          ( portUNPRIVILEGED_FLASH_REGION );

        portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_READ_ONLY ) |
                                       ( ( configTEX_S_C_B_FLASH & portMPU_RASR_TEX_S_C_B_MASK ) << portMPU_RASR_TEX_S_C_B_LOCATION ) |
                                       ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |
                                       ( portMPU_REGION_ENABLE );

        /* Setup the privileged flash for privileged only access.  This is where
         * the kernel code is placed. */
        portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_functions_start__ ) | /* Base address. */
                                          ( portMPU_REGION_VALID ) |
                                          ( portPRIVILEGED_FLASH_REGION );

        portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) |
                                       ( ( configTEX_S_C_B_FLASH & portMPU_RASR_TEX_S_C_B_MASK ) << portMPU_RASR_TEX_S_C_B_LOCATION ) |
                                       ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __privileged_functions_start__ ) ) |
                                       ( portMPU_REGION_ENABLE );

        /* Setup the privileged data RAM region.  This is where the kernel data
         * is placed. */
        portMPU_REGION_BASE_ADDRESS_REG = ( ( uint32_t ) __privileged_data_start__ ) | /* Base address. */
                                          ( portMPU_REGION_VALID ) |
                                          ( portPRIVILEGED_RAM_REGION );

        portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_PRIVILEGED_READ_WRITE ) |
                                       ( portMPU_REGION_EXECUTE_NEVER ) |
                                       ( ( configTEX_S_C_B_SRAM & portMPU_RASR_TEX_S_C_B_MASK ) << portMPU_RASR_TEX_S_C_B_LOCATION ) |
                                       prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_data_end__ - ( uint32_t ) __privileged_data_start__ ) |
                                       ( portMPU_REGION_ENABLE );

        /* By default allow everything to access the general peripherals.  The
         * system peripherals and registers are protected. */
        portMPU_REGION_BASE_ADDRESS_REG = ( portPERIPHERALS_START_ADDRESS ) |
                                          ( portMPU_REGION_VALID ) |
                                          ( portGENERAL_PERIPHERALS_REGION );

        portMPU_REGION_ATTRIBUTE_REG = ( portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER ) |
                                       ( prvGetMPURegionSizeSetting( portPERIPHERALS_END_ADDRESS - portPERIPHERALS_START_ADDRESS ) ) |
                                       ( portMPU_REGION_ENABLE );

        /* Enable the memory fault exception. */
        portNVIC_SYS_CTRL_STATE_REG |= portNVIC_MEM_FAULT_ENABLE;

        /* Enable the MPU with the background region configured. */
        portMPU_CTRL_REG |= ( portMPU_ENABLE | portMPU_BACKGROUND_ENABLE );
    }
}

By the way, I have another question but that might be more ARM related. According to “ARM Cortex™-M4 Devices Generic User Guide”, MPU_RBAR (portMPU_REGION_BASE_ADDRESS_REG) have 3 alias of RBAR called, MPU_RBAR_A1, MPU_RBAR_A2, MPU_RBAR_A3. I am not sure what is their usage. They have 4 different address but seems update at the same time, all the data write into MPU_RBAR will be written into its 3 alias registers. I’d greatly appreciate it if you could help me understand this part.

These are memory mapped registers and are used to program different MPU regions using Region Number field in RBAR.

These enable efficient programming of multiple MPU regions using one stm instruction. We use these to program MPU on context swich.

Thank you for your reply! May I ask a few additional questions about the first point in my post?

Since the registers are used to program MPU regions, when is the data we pass into the register consumed? If it is not consumed in time, will it be overwritten?

From my point of view, the code is like:

RBAR_REG = RBAR_attribute1;
RASR_REG = RASR_attribute1;
RBAR_REG = RBAR_attribute2;
RASR_REG = RASR_attribute2;

We write attribute1 first, then we do nothing( seems we do not use attribute1 at all?), and write attribute2 immediately.

If we use attribute1 somewhere, I wonder where do we use it. If we don’t use attribute1, does that mean attribute1 will be overwritten by attribute2?

The Bottom 4 bits of RBAR set the region number being defined if Bit4 is set (avoiding the need to write to MPU_RNR between each region.

The MPU has internally an array of regions, but they are all accessed through the same set of registers with a region number controlling which one is being accessed.

Can you point me to the exact code where you think we are not using some data which is stored?