/** * Copyright (c) 2016 - 2020, 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, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, 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 Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 #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__