Global Sources
EE Times-Asia
Stay in touch with EE Times Asia
?
EE Times-Asia > Memory/Storage
?
?
Memory/Storage??

Memory access ordering in complex embedded designs

Posted: 22 Dec 2014 ?? ?Print Version ?Bookmark and Share

Keywords:embedded systems? processor? Sequential Execution Model? SEM? Compilers?

On devices with an MMU (Cortex-A), the memory regions definitions are contained within the page descriptors in the MMU page tables. On those with an MPU (all Cortex-R and some Cortex-M), they are contained in the MPU region attributes. Cortex-M devices that do not have an MPU have a fixed memory map in which the type of each region is fixed.

From the descriptions of these memory types, you can see that defining memory-mapped peripheral regions as device memory solves many of the problems we have been talking about. Indeed, when working on ARM systems, it is pretty much mandatory that you do this!

A common situation is the need to reset a hardware device before reading its status. You might write something like this:

volatile unit32 control; //write register to reset device
volatile uint32 status; //read register to access status
uint32 x;

control = 1; // reset device

// some code

x = status; // read status while ((x & 1) != 1)
{
??????x = status;
}

You might think all is well. At least the programmer has declared the memory-mapped peripheral registers using the volatile keyword. But presumably, the write to the control register should complete before we read the status register. Otherwise the device will not be reset properly before we access it. This code does not guarantee that.

For reasons we have seen above, the compiler may promote the LDR from the status port above the STR to the control port because load latency is longer than store. Similarly, a multi-issue out-of-order execution unit in the processor might issue them in a different order. We need some way of ensuring that they happen in the order in which they are written. In C, we can use an intrinsic function to insert a suitable memory barrier.

volatile unit32 control; //write register to reset device
volatile uint32 status; //read register to access status
uint32 x;

control = 1; // reset device

dsb();

// some code

x = status; // read status while ((x & 1) != 1)
{
??????x = status;
}

There are other cases where you have to be careful. For instance, the system only ensures that memory accesses are complete from the point of view of the processor. It may be that certain memory accesses either have side effects that are beyond the knowledge of the processor or that they take time to occur. Let's look at some instances of this:

When finishing up an interrupt handler, one key operation is to clear down the peripheral that is signaling the interrupt. Clearly you need to do this before returning, otherwise you'll get re- interrupted immediately. You might use code like this.

str r1, [r0] ; write interrupt clear register
dsb ; ensure operation complete
rfe sp!

Without the DSB instruction, the processor would execute the RFE immediately after the STR. Since write buffering is permitted for device memory accesses, the interrupt handler could exit before the interrupt is actually cleared. The DSB instruction ensures that the processor stalls until the memory write is complete.

It is fairly common on ARM systems to reconfigure the memory system address map at runtime. In the past, we might have done this via a memory-mapped peripheral that modified the behavior of the address decode logic, like this:

ldr f0, #REMAP
str r1, [REMAP] ; reconfigure address map
dsb ; ensure write has completed
isb
b NewCode

The DSB is required to ensure that the STR operation is complete (to the memory system and not just the processor) before the program continues. The ISB ensures that subsequent instructions are loaded in the new context. You might think that putting the REMAP port in device memory would solve this problem but it doesn't. Crucially, the ordering rules for device memory accesses only apply with respect to other device memory accesses. Normal memory accesses (including instruction fetches) can be moved around among them without hindrance. So, the DSB is important to ensure that subsequent instruction fetches do not take place until the operation is complete.

These days, we do it more often by reprogramming the MMU. That requires some TLB maintenance too.

str r11, [r1] ; update page table
dsb ; ensure write has completed
tlbimva r10 ; invalidate affected TLB addreesses
bpiall ; flush branch predictor
dsb ; ensure completion of both operations
isb ; synchronize instruction context

?First Page?Previous Page 1???2???3???4???5?Next Page?Last Page



Article Comments - Memory access ordering in complex em...
Comments:??
*? You can enter [0] more charecters.
*Verify code:
?
?
Webinars

Seminars

Visit Asia Webinars to learn about the latest in technology and get practical design tips.

?
?
Back to Top