This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Synchronization primitives, do I need CLREX?

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

Parents
  • 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.

Reply
  • 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.

Children