2

I would like to modify a global variable which is shared by different tasks and IRQ contexts in a RTOS. Therefore I need to modify this variable atomically. In my current implementation, I have been using enable_irq/disable_irq functions to modify the statement atomically.

extern int g_var;

void set_bit_atomic(int mask)
{
    disable_irq();
    g_var |= mask;
    enable_irq();
}

I've found the __sync_bool_compare_and_swap function in GCC documentation as a helper for atomic operations.

My current toolchain is KEIL MDK, and I would like to switch to the approach shown below,

void set_bit_atomic(int mask)
{
    volatile int tmp;
    do {
        tmp = g_var;
    } while (!__sync_bool_compare_and_swap(&g_var, tmp, tmp | mask));
}

How can I write __sync_bool_compare_and_swap function in ARMv4 command set(as inline assembly)?

ldav1s
  • 15,885
  • 2
  • 53
  • 56
albin
  • 773
  • 1
  • 8
  • 27
  • 1
    You could see how GCC does it in assembly and clone it with modifications (gcc -S file.c) –  Aug 27 '13 at 21:24
  • ARM uses load-linked/store-conditional instructions to perform atomic operations. (See the links in [this question](http://stackoverflow.com/q/17733245/596781) maybe.) – Kerrek SB Aug 27 '13 at 21:57
  • 1
    I'm not convinced that there is a huge amount of difference. From what I understand, ARMV6 is required for the LDREX/STREX instruction, which is the what does a "compared and swap" pair of instructions on ARM. – Mats Petersson Aug 27 '13 at 22:03
  • @Kerrek SB Link shows ARMv6 or above instruction sets. I am using ARM7TDMI (ARMv4), and unfortunately it does not support these functions. – albin Aug 27 '13 at 22:04
  • @KerrekSB: That won't help for AMRv4 architecture, as far as I understand. – Mats Petersson Aug 27 '13 at 22:04
  • @MatsPetersson: Oh, shame. Never mind then. – Kerrek SB Aug 27 '13 at 22:33
  • @user9000 I have also tried your suggestion. You cannot see the assembly instruction of the `__sync_bool_compare_and_swap` by doing gcc -S file.c. It is linked as global symbol, so I needed to dig into gcc source, and I found the implementation of the function at this [link](https://android.googlesource.com/toolchain/gcc/+/07d0f8a9ce3cf806a3a157d86181fc343cc54bac/gcc-4.6/gcc/config/arm/linux-atomic.c). In the end `__kernel_cmpxchg` is used for ARM architecture. – albin Sep 10 '13 at 20:18

1 Answers1

1

I have found a similar implementation for __kernel_cmpxchg function in Linux kernel source.

It has been written for ARMv5 and earlier, and It seems to work for ARM7TDMI (ARMv4).

1:      ldr     r3, [r2]        @ load current val
        subs    r3, r3, r0      @ compare with oldval
2:      streq   r1, [r2]        @ store newval if eq
        rsbs    r0, r3, #0      @ set return val and C flag
        bx      lr              @ or "mov pc, lr" if no thumb support

Details can be found at this link.

There are two important issues that I would like to warn,

1- __kernel_cmpxchg returns 0 when swap occurred, while __sync_bool_compare_and_swap function returns true.

2- function prototypes are different.

typedef int (*__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
#define __kernel_cmpxchg ((__kernel_cmpxchg_t)0xffff0fc0)

bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)

Therefore I had to change the usage as below,

void set_bit_atomic(int mask)
{
    volatile int tmp;
    do {
        tmp = g_var;
    } while (my_compare_and_swap(tmp, tmp | mask, &g_var));
}

Caveat: This code does not work properly without kernel support. See the comments below.

albin
  • 773
  • 1
  • 8
  • 27
  • 1
    This function does not work properly without kernel support. If you use this function without operating system support, any interrupt or preemption between `ldr r3, [r2]` and `subs r3, r3, r0` would effect the operation. Simply ARM7TDMI (ARMv4) does not help on this issue. – albin Sep 16 '13 at 19:43
  • 1
    The question is about ARM7 (ARMv4). But let me remark that ARMv6 introduced the `SWP` instruction. This has been deprecated in ARMv7 in favour of `LDREX`/`STREX`. – Sven Feb 20 '18 at 00:37