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