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
The local monitor will be cleared by any exception in ARMv7 Cortex M series - which includes the M3 - and you needn't worry about it. You'll always have some sort of exception for that, perhaps a pending interrupt or an SVC.
The local monitor isn't cleared for ARM mode where you have to use CLREX when switching context. Well it actually is cleared for ARMv8 where they seem to have decided the Cortex M way of doing things was the right one.
Thank you!
May I ask where you have these information from? I've linked my ressources in the first post, why are there contradictory statements, did I read the wrong chapter?
Is the example implementation usable?
Thank you very much.
The article [4] was about the Cortex-A and R series not about the Cortex M cores which handle exceptions differently. In ARMv7 they don't automatically clear the local monitor on exception entry and return. That's why it required CLREX when switching context.
As to the dmb it is normally used between setting data and then setting a flag saying the data is ready to read, or between reading a flag saying some data is ready and then reading the data. Written data is guaranteed to be flushed out to memory in some sort of reasonable time, dmb might help it go faster but is not required unless you have something like that. So whether the dmb should be before or after your code or not there at all really depends on when the incremented value must be seen. If the new value must not be seen before new data written before then a dmb is required beforehand, and if the earlier value must not be seen after any data written afterwards is read then the dmb needs to be after it. And it doesn't matter much if you're only talking about one processor and nothing else accessing it, though LDREX and STREX do because of context switching, and it is worth doing properly in case requirements change.
Hi and thank you!
I think Cortex-A and R were examples only, this articel is more general, because there are distinct articles for all the series, the article about exclusive monitors is a subtopic in "developer guides and articles".
The Cortex-M has an ARMv7 architecture, so does the monitor clear or does it not?
Did I understand your DMB paragraph correctly: The DMB is used to save all register values that might be saved later in code to memory or where it belongs to. This might be necessary if a flag is being set and processed right away? So if I read some data, write that to memory and increment the flag afterwards a DMB ensures the data is written to memory?
Thank you,
I think you understood correctly.
You don't need to use CLREX.
The whole point of DMB is to separate memory accesses before it from those after it. It is used so that if there are two different processors accessing memory then they can access shared data in a coherent manner. I do not know what you are using your counter for so I don't know where if anywhere you want a dmb. You need to use dmb in between setting or reading a flag controlling access to some shared data and accessing the data as in
write a packet of data. dmb. Set flag saying data is ready
read a flag and it says data is ready. dmb. read the data