ldrex, strex, gcc atomic operations

Status
Not open for further replies.

Frank B

Senior Member
Hi..
today, I had a little problem with a simple loop:

while (playing>0){;}
(playing is "int")

Sometimes, the cpu "crashed" (i have no debugger) - it stopped working.
The variable "playing" is written (and read at some places) in an interrupt.

Edit: Deleted my wrong interrupt/ldrex/strex stuff.. leads to confusion :)
 
Last edited:
Hello Frank,

I believe there are two considerations at play here:

1) The variable "playing" must be declared with the volatile keyword: If not, the compiler assumes that your code "owns" the variable so all references after the first will produce identical results. This lets the compiler optimize the loop into either not executing at all if (playing <= 0) or a "while(1)" loop if (playing > 0) when executing the statement the first time.
2) Generally speaking, the Teensyduino loop() function (which I'm assuming is how your "background" process is organized) should be allowed to return occasionally to allow some basic serial and other maintenance functions to run. That may (or may not be) why your Teensy appears to lock up with the original code.

The exclusive operations are useful in situations where more than one "processor" may try to access a memory location, serving as the low-level support of a semaphore. The Teensy has only one processor and a DMA controller, so using the exclusive operations are probably not what you're looking for here, as they don't add any capability that is not available using classical interrupt controls or the optimization-disabling use of "volatile".

My best guess why your use of the atomic functions can prevent the crashing is that the atomic operations force the compiler out of its reduction-to-null optimization. You can test that by reverting to your original code but use the "volatile" keyword in declaring your loop variable.
 
Hi Len, thank you for your answer!
Yes, i really should have mentioned it.. sorry. "playing" was, and is volatile. This was the first thing i changed today.
 
Last edited:
Seems to me your implementing a spin lock which can also be implemented with GCC builtin's such as:

Code:
[COLOR=#008400][FONT=Menlo]//-----------------------------------------------------------------------
volatile unsigned int _LOCK = 0;

[/FONT][/COLOR]
[FONT=Menlo][COLOR=#bb2ca2]inline[/COLOR] [COLOR=#703daa]uint32_t[/COLOR] sys_acquire_lock( [COLOR=#bb2ca2]volatile[/COLOR] [COLOR=#bb2ca2]unsigned[/COLOR] [COLOR=#bb2ca2]int[/COLOR] *lock ) {[/FONT]
[FONT=Menlo]    [COLOR=#bb2ca2]do[/COLOR] { 
      yield();// write your own yield handler to do stuff.
   }[/FONT]
[COLOR=#31595D][FONT=Menlo][COLOR=#bb2ca2]   while[/COLOR][COLOR=#000000] ( ![/COLOR]__sync_bool_compare_and_swap[COLOR=#000000]( lock, [/COLOR][COLOR=#272ad8]0[/COLOR][COLOR=#000000], [/COLOR][COLOR=#272ad8]1[/COLOR][COLOR=#000000] ) );[/COLOR][/FONT][/COLOR]
[FONT=Menlo]    [COLOR=#bb2ca2]return[/COLOR] *lock;[/FONT]
[FONT=Menlo]}[/FONT]
[COLOR=#008400][FONT=Menlo]//-----------------------------------------------------------------------[/FONT][/COLOR]
[FONT=Menlo][COLOR=#bb2ca2]inline[/COLOR] [COLOR=#703daa]uint32_t[/COLOR] sys_release_lock( [COLOR=#bb2ca2]volatile[/COLOR] [COLOR=#bb2ca2]unsigned[/COLOR] [COLOR=#bb2ca2]int[/COLOR] *lock ) {[/FONT]
[FONT=Menlo]    [COLOR=#bb2ca2]asm[/COLOR] [COLOR=#bb2ca2]volatile[/COLOR] ( [COLOR=#d12f1b]""[/COLOR] ::: [COLOR=#d12f1b]"memory"[/COLOR] );[/FONT]
[FONT=Menlo]    *lock = [COLOR=#272ad8]0[/COLOR];[/FONT]
[FONT=Menlo]    [COLOR=#bb2ca2]return[/COLOR] *lock;[/FONT]
[FONT=Menlo]}[/FONT]

I would suggest look at your disassembly to make sure the compiler is actually using ldrex/strex in your locking code, I know the above does. Actually you can just release the lock by writing 0 to your LOCK variable. One thing you need to look out for is when using spin locks with mutilple ISR's with different priority levels is you can dead lock your teensy, you need to implement ISR masking similar to SPI stuff paul did. I used this for a simple scheduler library I made based off the fibers library. Not perfect solution but it works for my application.
 
Ahhhh....

just looked... you're right, Len!
I changed so much today... somehow i deleted the "volatile" again. :mad:

It's time for some free days...

No duff, much simpler.. :-(
 
actually the dissassembly of __atomic_store_n doesn't use ldrex or strex:

Code:
volatile int playing = 0;
void setup() {
  while (__atomic_load_n(&playing, __ATOMIC_SEQ_CST)>0) {;}
}


void loop() {
  
}

//--------------------------------------------------------------------------------------------------------
Disassembly of section .text.setup:


00000000 <setup>:
   0:    4a04          ldr    r2, [pc, #16]    ; (14 <setup+0x14>)
   2:    f3bf 8f5f     dmb    sy
   6:    6813          ldr    r3, [r2, #0]
   8:    2b00          cmp    r3, #0
   a:    f3bf 8f5f     dmb    sy
   e:    dcf8          bgt.n    2 <setup+0x2>
  10:    4770          bx    lr
  12:    bf00          nop
  14:    00000000     .word    0x00000000


Disassembly of section .text.loop:


00000000 <loop>:
   0:    4770          bx    lr
 
super. i read it somewhere. don't trust the internet :)

no, the different levels are very easy to handle. just simply ingore them.. and think of single interrupt and a main program instead. :)
This was the first obscure "crash" which I had with my current project. lol i know volatile since a long time and forgot it.. then i inserted it..and somehow removed it again..

time for hollydays :)
 
Last edited:
Status
Not open for further replies.
Back
Top