You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
10 KiB
326 lines
10 KiB
/*
|
|
* Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef NRFX_ATOMIC_INTERNAL_H__
|
|
#define NRFX_ATOMIC_INTERNAL_H__
|
|
|
|
#include <nrfx.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Only Cortex-M cores > 3 support LDREX/STREX instructions. */
|
|
#if ((__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)) == 0
|
|
#error "Unsupported core version"
|
|
#endif
|
|
|
|
#if defined ( __CC_ARM )
|
|
static __asm uint32_t nrfx_atomic_internal_mov(nrfx_atomic_u32_t * p_ptr,
|
|
uint32_t value,
|
|
uint32_t * p_new)
|
|
{
|
|
/* The base standard specifies that arguments are passed in the core registers
|
|
* r0-r3 and on the stack. Registers r4 and r5 must be saved on the stack.
|
|
* Only even number of register pushes are allowed. This is a requirement
|
|
* of the Procedure Call Standard for the ARM Architecture [AAPCS].
|
|
*/
|
|
push {r4, r5}
|
|
mov r4, r0
|
|
|
|
loop_mov
|
|
ldrex r0, [r4]
|
|
mov r5, r1
|
|
strex r3, r5, [r4]
|
|
cmp r3, #0
|
|
bne loop_mov
|
|
|
|
str r5, [r2]
|
|
pop {r4, r5}
|
|
bx lr
|
|
}
|
|
|
|
|
|
static __asm uint32_t nrfx_atomic_internal_orr(nrfx_atomic_u32_t * p_ptr,
|
|
uint32_t value,
|
|
uint32_t * p_new)
|
|
{
|
|
push {r4, r5}
|
|
mov r4, r0
|
|
|
|
loop_orr
|
|
ldrex r0, [r4]
|
|
orr r5, r0, r1
|
|
strex r3, r5, [r4]
|
|
cmp r3, #0
|
|
bne loop_orr
|
|
|
|
str r5, [r2]
|
|
pop {r4, r5}
|
|
bx lr
|
|
}
|
|
|
|
static __asm uint32_t nrfx_atomic_internal_and(nrfx_atomic_u32_t * p_ptr,
|
|
uint32_t value,
|
|
uint32_t * p_new)
|
|
{
|
|
push {r4, r5}
|
|
mov r4, r0
|
|
|
|
loop_and
|
|
ldrex r0, [r4]
|
|
and r5, r0, r1
|
|
strex r3, r5, [r4]
|
|
cmp r3, #0
|
|
bne loop_and
|
|
|
|
str r5, [r2]
|
|
pop {r4, r5}
|
|
bx lr
|
|
}
|
|
|
|
static __asm uint32_t nrfx_atomic_internal_eor(nrfx_atomic_u32_t * p_ptr,
|
|
uint32_t value,
|
|
uint32_t * p_new)
|
|
{
|
|
push {r4, r5}
|
|
mov r4, r0
|
|
|
|
loop_eor
|
|
ldrex r0, [r4]
|
|
eor r5, r0, r1
|
|
strex r3, r5, [r4]
|
|
cmp r3, #0
|
|
bne loop_eor
|
|
|
|
str r5, [r2]
|
|
pop {r4, r5}
|
|
bx lr
|
|
}
|
|
|
|
static __asm uint32_t nrfx_atomic_internal_add(nrfx_atomic_u32_t * p_ptr,
|
|
uint32_t value,
|
|
uint32_t * p_new)
|
|
{
|
|
push {r4, r5}
|
|
mov r4, r0
|
|
|
|
loop_add
|
|
ldrex r0, [r4]
|
|
add r5, r0, r1
|
|
strex r3, r5, [r4]
|
|
cmp r3, #0
|
|
bne loop_add
|
|
|
|
str r5, [r2]
|
|
pop {r4, r5}
|
|
bx lr
|
|
}
|
|
|
|
static __asm uint32_t nrfx_atomic_internal_sub(nrfx_atomic_u32_t * p_ptr,
|
|
uint32_t value,
|
|
uint32_t * p_new)
|
|
{
|
|
push {r4, r5}
|
|
mov r4, r0
|
|
|
|
loop_sub
|
|
ldrex r0, [r4]
|
|
sub r5, r0, r1
|
|
strex r3, r5, [r4]
|
|
cmp r3, #0
|
|
bne loop_sub
|
|
|
|
str r5, [r2]
|
|
pop {r4, r5}
|
|
bx lr
|
|
}
|
|
|
|
static __asm bool nrfx_atomic_internal_cmp_exch(nrfx_atomic_u32_t * p_data,
|
|
uint32_t * p_expected,
|
|
uint32_t value)
|
|
{
|
|
#define RET_REG r0
|
|
#define P_EXPC r1
|
|
#define VALUE r2
|
|
#define STR_RES r3
|
|
#define P_DATA r4
|
|
#define EXPC_VAL r5
|
|
#define ACT_VAL r6
|
|
|
|
push {r4-r6}
|
|
mov P_DATA, r0
|
|
mov RET_REG, #0
|
|
|
|
loop_cmp_exch
|
|
ldrex ACT_VAL, [P_DATA]
|
|
ldr EXPC_VAL, [P_EXPC]
|
|
cmp ACT_VAL, EXPC_VAL
|
|
ittee eq
|
|
strexeq STR_RES, VALUE, [P_DATA]
|
|
moveq RET_REG, #1
|
|
strexne STR_RES, ACT_VAL, [P_DATA]
|
|
strne ACT_VAL, [P_EXPC]
|
|
cmp STR_RES, #0
|
|
itt ne
|
|
movne RET_REG, #0
|
|
bne loop_cmp_exch
|
|
|
|
pop {r4-r6}
|
|
bx lr
|
|
|
|
#undef RET_REG
|
|
#undef P_EXPC
|
|
#undef VALUE
|
|
#undef STR_RES
|
|
#undef P_DATA
|
|
#undef EXPC_VAL
|
|
#undef ACT_VAL
|
|
}
|
|
|
|
static __asm uint32_t nrfx_atomic_internal_sub_hs(nrfx_atomic_u32_t * p_ptr,
|
|
uint32_t value,
|
|
uint32_t * p_new)
|
|
{
|
|
push {r4, r5}
|
|
mov r4, r0
|
|
|
|
loop_sub_ge
|
|
ldrex r0, [r4]
|
|
cmp r0, r1
|
|
ite hs
|
|
subhs r5, r0, r1
|
|
movlo r5, r0
|
|
strex r3, r5, [r4]
|
|
cmp r3, #0
|
|
bne loop_sub_ge
|
|
|
|
str r5, [r2]
|
|
pop {r4, r5}
|
|
bx lr
|
|
}
|
|
|
|
|
|
#define NRFX_ATOMIC_OP(asm_op, old_val, new_val, ptr, value) \
|
|
old_val = nrfx_atomic_internal_##asm_op(ptr, value, &new_val)
|
|
|
|
#elif defined ( __ICCARM__ ) || defined ( __GNUC__ )
|
|
|
|
/**
|
|
* @brief Atomic operation generic macro.
|
|
*
|
|
* @param[in] asm_op Operation: mov, orr, and, eor, add, sub.
|
|
* @param[out] old_val Atomic object output (uint32_t); value before operation.
|
|
* @param[out] new_val Atomic object output (uint32_t); value after operation.
|
|
* @param[in] value Atomic operation operand.
|
|
*/
|
|
#define NRFX_ATOMIC_OP(asm_op, old_val, new_val, ptr, value) \
|
|
{ \
|
|
uint32_t tmp_reg; \
|
|
__ASM volatile( \
|
|
"1: ldrex %["#old_val"], [%["#ptr"]]\n" \
|
|
NRFX_ATOMIC_OP_##asm_op(new_val, old_val, value) \
|
|
" strex %[tmp_reg], %["#new_val"], [%["#ptr"]]\n" \
|
|
" teq %[tmp_reg], #0\n" \
|
|
" bne.n 1b" \
|
|
: \
|
|
[old_val] "=&r" (old_val), \
|
|
[new_val] "=&r" (new_val), \
|
|
[tmp_reg] "=&r" (tmp_reg) \
|
|
: \
|
|
[ptr] "r" (ptr), \
|
|
[value] "r" (value) \
|
|
: "cc"); \
|
|
(void)tmp_reg; \
|
|
}
|
|
|
|
#define NRFX_ATOMIC_OP_mov(new_val, old_val, value) "mov %["#new_val"], %["#value"]\n"
|
|
#define NRFX_ATOMIC_OP_orr(new_val, old_val, value) "orr %["#new_val"], %["#old_val"], %["#value"]\n"
|
|
#define NRFX_ATOMIC_OP_and(new_val, old_val, value) "and %["#new_val"], %["#old_val"], %["#value"]\n"
|
|
#define NRFX_ATOMIC_OP_eor(new_val, old_val, value) "eor %["#new_val"], %["#old_val"], %["#value"]\n"
|
|
#define NRFX_ATOMIC_OP_add(new_val, old_val, value) "add %["#new_val"], %["#old_val"], %["#value"]\n"
|
|
#define NRFX_ATOMIC_OP_sub(new_val, old_val, value) "sub %["#new_val"], %["#old_val"], %["#value"]\n"
|
|
#define NRFX_ATOMIC_OP_sub_hs(new_val, old_val, value) \
|
|
"cmp %["#old_val"], %["#value"]\n " \
|
|
"ite hs\n" \
|
|
"subhs %["#new_val"], %["#old_val"], %["#value"]\n" \
|
|
"movlo %["#new_val"], %["#old_val"]\n"
|
|
|
|
static inline bool nrfx_atomic_internal_cmp_exch(nrfx_atomic_u32_t * p_data,
|
|
uint32_t * p_expected,
|
|
uint32_t value)
|
|
{
|
|
bool res = false;
|
|
/* Temporary register used in the inline asm code for getting the result
|
|
* of the strex* operations (no need to initialize it).
|
|
*/
|
|
uint32_t tmp_reg;
|
|
uint32_t act_val = 0;
|
|
uint32_t exp_val = 0;
|
|
__ASM volatile(
|
|
"1: ldrex %[act_val], [%[ptr]]\n"
|
|
" ldr %[exp_val], [%[expc]]\n"
|
|
" cmp %[act_val], %[exp_val]\n"
|
|
" ittee eq\n"
|
|
" strexeq %[tmp_reg], %[value], [%[ptr]]\n"
|
|
" moveq %[res], #1\n"
|
|
" strexne %[tmp_reg], %[act_val], [%[ptr]]\n"
|
|
" strne %[act_val], [%[expc]]\n"
|
|
" cmp %[tmp_reg], #0\n"
|
|
" itt ne\n"
|
|
" movne %[res], #0\n"
|
|
" bne.n 1b"
|
|
:
|
|
[res] "=&r" (res),
|
|
[exp_val] "=&r" (exp_val),
|
|
[act_val] "=&r" (act_val),
|
|
[tmp_reg] "=&r" (tmp_reg)
|
|
:
|
|
"0" (res),
|
|
"1" (exp_val),
|
|
"2" (act_val),
|
|
[expc] "r" (p_expected),
|
|
[ptr] "r" (p_data),
|
|
[value] "r" (value)
|
|
: "cc");
|
|
(void)tmp_reg;
|
|
return res;
|
|
}
|
|
|
|
#else
|
|
#error "Unsupported compiler"
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // NRFX_ATOMIC_INTERNAL_H__
|
|
|