ARM Technical Support Knowledge Articles

Can the ARM compiler generate exclusive loads and stores (SWP, LDREX, STREX)?

Applies to: ARM Architecture and Instruction Sets, ARM Developer Suite (ADS), DS-5, RealView Compilation Tools (RVCT) for BREW, RealView Development Suite (RVDS)

Answer

SWP (pre-ARMv6 architectures) and LDREX/STREX (ARMv6 and later) are instructions designed to support multi-master systems. For example, a multi-master system could be a system with multiple cores and other bus masters like DMA controllers. The primary purpose of these instructions is to maintain the integrity of shared data structures during inter-master communication by preventing masters from making conflicting accesses at the same time.

SWP provides an atomic load and store operation which can be used as the basic building block for mutexes and semaphores.

ARMv6 also introduced a pair of synchronization primitives, LDREX and STREX, which allow a bus master to detect that another master has written to an address that requires exclusive access. The advantage of LDREX/STREX is that it does not prevent other transactions on the bus from executing. In contrast, a core executing SWP takes over the bus until the instruction is completed, by asserting the HLOCK signal.

These instructions are also useful on a single master system to implement mutexes, semaphores, etc. without needing to disable interrupts. In the same way, they are also useful for multi-threaded systems.

SWP has been deprecated in ARMv7 and beyond. On a v6 or later core (ARM11 onwards) you should use the LDREX/STREX instructions instead. ARMv7 also adds byte, halfword and doubleword versions of the synchronization primitives:

LDREXB/STREXB
LDREXH/STREXH
LDREXD/STREXD

Here we have a very simple spin-lock example showing LDREX and STREX in action. We check to see if an address is "locked" using the LDREX instruction. If it isn't locked and is available to use, we then store the "locked" value to the address using the STREX instruction:

  ; void lock(lock_t* pAddr)
lock
  ; Is locked?
  LDREX   r1, [r0]           ; Check if locked
  CMP     r1, #LOCKED        ; Compare with “locked“
  BEQ     lock               ; If LOCKED, try again
 
  ; Attempt to lock
  MOV     r1, #LOCKED
  STREX   r2, r1, [r0]       ; Attempt to lock
  CMP     r2, #0x0           ; Check whether store completed
  BNE     lock               ; If store failed, try again
  DMB
  BX      lr

For more information please see the ARM Architecture Reference Manual and the AMBA documentation.

Compiler behavior

The compiler will not generate either SWP or LDREX/STREX when compiling normal C or C++ source code. This is because the C and C++ language specifications do not mention these kind of operations and therefore there are no operators in the language that map to these instructions.

You can, however, use one of the following methods to access these instructions in your program:

  • Compiler intrinsics. For example:

    __ldrex();
    __strex();

    The __ldrex and __strex intrinsics generate LDREXB/STREXB, LDREXH/STREXH or LDREX/STREX based on the size of the parameter. For example:

    __ldrex( (char *) ptr ) // generates LDREXB
    __ldrex( (short *) ptr) // generates LDREXH
    __ldrex( (int *) ptr )  // generates LDREX

    The __swp, __ldrex and __strex intrinsics were introduced in RVCT 3.1. The __ldrexd and __strexd intrinsics were added in RVCT 4.0. The compiler reports an error if __ldrexd or __strexd are compiled for --cpu targets that do not support LDREXD and STREXD instructions.

  • The embedded assembler or inline assembly. For example:

    /* Embedded assembler to access the SWP instruction */
    __asm atomic_swap(unsigned int read, unsigned int write, unsigned int addr)
    {
      SWP r0,r1,[r2]
    }

ARM Compiler 5.06 deprecated the __ldrex and __strex family of intrinsics, and also deprecated the use of the LDREX and STREX family of instructions in inline assembly code. The appropriate workaround is to use these instructions in an embedded assembler function or to use the __sync_* family of GNU built-in functions instead for development using ARM Compiler 5.06 and later.

See also:

Article last edited on: 2016-05-27 16:22:17

Rate this article

[Bad]
|
|
[Good]
Disagree? Move your mouse over the bar and click

Did you find this article helpful? Yes No

How can we improve this article?

Link to this article
Copyright © 2011 ARM Limited. All rights reserved. External (Open), Non-Confidential