[SOLVED] Setting up a non-cacheable region with ARM MPU

Thread Starter

transconductance

Joined Jun 29, 2019
80
I am using an ATSAMS70 (Cortex M7) microcontroller equipped with DMA and an ARM Memory Protection Unit (MPU). I have a high-speed serial interface coming in on a USART and being DMA'd into RAM. I need to disable caching of that region so the CPU will actually see the data that the DMA just wrote. Microchip's documentation and the Atmel START tool support DMA configuration but not the MPU as far as I can see. So I don't have an API to work with and have to configure the MPU via registers.

I got the DMA working fairly easily. And I've been able to create a region with the MPU and verify that it handling accesses to that region. The DMA can write to this memory, but if the CPU tries to read or write this in this area, the MPU throws a data access violation. I've tried various combinations of the region attributes but can't seem to crack it. It's not supposed matter but the code that trips the exception is running in privileged mode.

Can anyone help? Here is the function that configures the MPU region. This is the only region I am configuring so all other accesses are following the default mapping.

Code:
/*
* Configure the specified region, 1..16
* Returns the region size in bytes, which may be more than the requested size
*/
unsigned MPU_SetRegion(unsigned Region, u32 BaseAddress, unsigned BytesRequested, bool Cacheable, bool Executable)
{
    u16 size_exponent;
   
    // MPU uses an exponent of 2 to specify the region size.
    // Find the lowest exponent which will satisfy the request.
    size_exponent = 0;
    if (BytesRequested < 32)
    {
        BytesRequested = 32;
    }
    while (BytesRequested > 1)
    {
        size_exponent += 1;
        BytesRequested /= 2;
    }

    // Set base address and select region for configuration
    // Writing the RNR selects the region which the RBAR and RASR control
    MPU->MPU_RNR.Register = (Region-1);
    MPU->MPU_RBAR.Register = (BaseAddress & MPU_REGION_ADDRESS_MASK);

    // Configure the region
    MPU->MPU_RASR.Size = size_exponent - 1;
    MPU->MPU_RASR.XN = Executable ? 0 : 1;
    MPU->MPU_RASR.AP = 0b011; /* no privilege restriction */
    MPU->MPU_RASR.B = Cacheable ? 1 : 0;
    MPU->MPU_RASR.C = Cacheable ? 1 : 0;;
    MPU->MPU_RASR.TEX = Cacheable ? 0b000 : 0b001;
    MPU->MPU_RASR.SRD = 0; // enable all sub-regions
    MPU->MPU_RASR.S = 1; // region is shareable

    // Enable the region
    MPU->MPU_RASR.Enable = 1;

   // Return actual size of the region
    return(1 << size_exponent);
}
 
Last edited:

Thread Starter

transconductance

Joined Jun 29, 2019
80
I solved it. As you saw in ths source code, I was writing each field of the RASR separately. Presumably the compiler was generating a read/modify/write of some kind. When I changed this to a single write to the register, with the field values already compiled, it solved the problem.
 

nsaspook

Joined Aug 27, 2009
12,726
I've not had experience directly on that processor but in general MPU register access must be word aligned.
 
Last edited:

Thread Starter

transconductance

Joined Jun 29, 2019
80
You're right. Now that you mention it, I did see that somewhere. It didn't occur to me that the compiler would be used sub-word access for the RMW operations but, of course it does.
 

nsaspook

Joined Aug 27, 2009
12,726
You're right. Now that you mention it, I did see that somewhere. It didn't occur to me that the compiler would be used sub-word access for the RMW operations but, of course it does.
Never trust the compiler by default when talking to complex hardware. It usually defaults to the most efficient method unless told there are restrictions on memory placement and sequences.
 
Top