We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hi all,
I'm trying to understand the LDREX/STREX commands in an ARM Cortex M3 MCU to implement atomic access to various variables (the goal is to implement semaphores/mutexes or increment/decrement shared variables).
There are several ressources available:
[1] ARM Information Center LDREX/STREX
[2] ARM Information Center CLREX
[3] ARMv7-M Architecture Reference Manual
[4] Exclusive monitors http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/CIHHCAFD.html
[5] Synchronization primitives
[6] Memory barriers
While reviewing the ressources above I realized that:
1) LDREX/STREX must occur in pairs ([1])
2) STREX must use the same address the most recently executed LDREX used too, otherwise the behaviour is unpredictable (see [1])
3) The exclusive tag is cleared if 1) clrex instruction executed, 2) strex instr executed, 3) exception occurs (see [5])
4) When a context switch occurs, the monitors have to be reset either by CLREX (see [2]) (>ARMv6K) or by a dummy strex (see [4] §Resetting monitors or [3] §A2.9.3 and §A2.9.4 "usage restrictions 1."
5) LDREX + STREX must be close to each other in code (see [4] §A2.9.4 "usage restrictions")
5) Memory barriers must be used [6]
I'm confused a bit here. On the one hand 3) states that if an exception (mainly interrupts in my case what is a type of exception) occurs, the state of the exclusive monitors will be reset. On the other hand 4) states a CLREX or a dummy STREX is required when a "context switch" is happening. Can a context switch take place without an exception?
So the big question is: Do I need to reset the exclusive monitors in each interrupt service routine or not? If I had to but don't do it the following could happen:
1) the main thread loads an address &a exclusively
2) an interrupt occurs
3) the ISR uses another address &b to increment a variable (LDREX + STREX pair). LDREX' last used address is &b then
4) the ISR returns to the main thread
5) the main thread tries to write the variable at address &a exclusively.
-> this should end in unpredictable behaviour because the last used LDREX is an other than used by STREX.
My current implementation follows:
[code]
int incVar(uint32_t * varAddr)
{
uint32_t count;
int32_t status;
do
count = (uint32_t) __ldrex( (uint32_t *)(varAddr) ); // load variable address exclusively
status = __strex( ++count, (uint32_t *)(varAddr) ); // status != 0, if no exclusive access was guaranteed
}
while ( status != 0 );
__dmb( 10 ); // recommended by arm -> make sure every memory access is done
return SUCCESS;
[/code]
Is that usable and save?
Thank you very much,
Jan