Как сравнить и поменять атомарно в ARM7?
Я хотел бы изменить глобальную переменную, которая совместно используется различными задачами и контекстами IRQ в ОСРВ. Поэтому мне нужно изменить эту переменную атомарно. В моей текущей реализации я использовал функции enable_irq/disable_irq для атомарного изменения оператора.
extern int g_var;
void set_bit_atomic(int mask)
{
disable_irq();
g_var |= mask;
enable_irq();
}
Я нашел __sync_bool_compare_and_swap
функция в документации GCC как помощник для атомарных операций.
Мой текущий набор инструментов - KEIL MDK, и я хотел бы перейти к подходу, показанному ниже,
void set_bit_atomic(int mask)
{
volatile int tmp;
do {
tmp = g_var;
} while (!__sync_bool_compare_and_swap(&g_var, tmp, tmp | mask));
}
Как я могу написать __sync_bool_compare_and_swap
функция в наборе команд ARMv4 (как встроенная сборка)?
1 ответ
Я нашел похожую реализацию для __kernel_cmpxchg
функция в исходном коде ядра Linux.
Он был написан для ARMv5 и более ранних версий, и, похоже, работает для 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
Подробности можно найти по этой ссылке.
Есть два важных вопроса, которые я хотел бы предупредить,
1- __kernel_cmpxchg
возвращает 0, когда произошел обмен, в то время как __sync_bool_compare_and_swap
функция возвращает истину.
2- функциональные прототипы разные.
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, ...)
Поэтому мне пришлось изменить использование, как показано ниже,
void set_bit_atomic(int mask)
{
volatile int tmp;
do {
tmp = g_var;
} while (my_compare_and_swap(tmp, tmp | mask, &g_var));
}
Предостережение: этот код не работает должным образом без поддержки ядра. Смотрите комментарии ниже.