/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2022-07-27 flybreak the first version */ #include /** \brief LDR Exclusive (32 bit) \details Executes a exclusive LDR instruction for 32 bit values. \param [in] ptr Pointer to data \return value of type uint32_t at (*ptr) */ #if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */ #define __LDREXW (rt_atomic_t)__builtin_arm_ldrex #elif defined(__ARMCC_VERSION) /* ARM Compiler V5 */ #if __ARMCC_VERSION < 5060020 #define __LDREXW(ptr) ((rt_atomic_t ) __ldrex(ptr)) #else #define __LDREXW(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((rt_atomic_t ) __ldrex(ptr)) _Pragma("pop") #endif #elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */ _Pragma("inline=forced") __intrinsic rt_atomic_t __LDREXW(volatile rt_atomic_t volatile *ptr) { return __LDREX((unsigned long *)ptr); } #elif defined (__GNUC__) /* GNU GCC Compiler */ __attribute__((always_inline)) static inline rt_atomic_t __LDREXW(volatile rt_atomic_t *addr) { rt_atomic_t result; __asm volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); return result; } #endif /** \brief STR Exclusive (32 bit) \details Executes a exclusive STR instruction for 32 bit values. \param [in] value Value to store \param [in] ptr Pointer to location \return 0 Function succeeded \return 1 Function failed */ #if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */ #define __STREXW (rt_atomic_t)__builtin_arm_strex #elif defined(__ARMCC_VERSION) /* ARM Compiler V5 */ #if __ARMCC_VERSION < 5060020 #define __STREXW(value, ptr) __strex(value, ptr) #else #define __STREXW(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") #endif #elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */ _Pragma("inline=forced") __intrinsic rt_atomic_t __STREXW(rt_atomic_t value, volatile rt_atomic_t *ptr) { return __STREX(value, (unsigned long *)ptr); } #elif defined (__GNUC__) /* GNU GCC Compiler */ __attribute__((always_inline)) static inline rt_atomic_t __STREXW(volatile rt_atomic_t value, volatile rt_atomic_t *addr) { rt_atomic_t result; __asm volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); return result; } #endif rt_atomic_t rt_hw_atomic_load(volatile rt_atomic_t *ptr) { rt_atomic_t oldval; do { oldval = __LDREXW(ptr); } while ((__STREXW(oldval, ptr)) != 0U); return *ptr; } void rt_hw_atomic_store(volatile rt_atomic_t *ptr, rt_atomic_t val) { do { __LDREXW(ptr); } while ((__STREXW(val, ptr)) != 0U); } rt_atomic_t rt_hw_atomic_add(volatile rt_atomic_t *ptr, rt_atomic_t val) { rt_atomic_t oldval; do { oldval = __LDREXW(ptr); } while ((__STREXW(oldval + val, ptr)) != 0U); return oldval; } rt_atomic_t rt_hw_atomic_sub(volatile rt_atomic_t *ptr, rt_atomic_t val) { rt_atomic_t oldval; do { oldval = __LDREXW(ptr); } while ((__STREXW(oldval - val, ptr)) != 0U); return oldval; } rt_atomic_t rt_hw_atomic_and(volatile rt_atomic_t *ptr, rt_atomic_t val) { rt_atomic_t oldval; do { oldval = __LDREXW(ptr); } while ((__STREXW(oldval & val, ptr)) != 0U); return oldval; } rt_atomic_t rt_hw_atomic_or(volatile rt_atomic_t *ptr, rt_atomic_t val) { rt_atomic_t oldval; do { oldval = __LDREXW(ptr); } while ((__STREXW(oldval | val, ptr)) != 0U); return oldval; } rt_atomic_t rt_hw_atomic_xor(volatile rt_atomic_t *ptr, rt_atomic_t val) { rt_atomic_t oldval; do { oldval = __LDREXW(ptr); } while ((__STREXW(oldval ^ val, ptr)) != 0U); return oldval; } rt_atomic_t rt_hw_atomic_exchange(volatile rt_atomic_t *ptr, rt_atomic_t val) { rt_atomic_t oldval; do { oldval = __LDREXW(ptr); } while ((__STREXW(val, ptr)) != 0U); return oldval; } void rt_hw_atomic_flag_clear(volatile rt_atomic_t *ptr) { do { __LDREXW(ptr); } while ((__STREXW(0, ptr)) != 0U); } rt_atomic_t rt_hw_atomic_flag_test_and_set(volatile rt_atomic_t *ptr) { rt_atomic_t oldval; do { oldval = __LDREXW(ptr); } while ((__STREXW(1, ptr)) != 0U); return oldval; } rt_atomic_t rt_hw_atomic_compare_exchange_strong(volatile rt_atomic_t *ptr, rt_atomic_t *old, rt_atomic_t new) { rt_atomic_t result; rt_atomic_t temp = *old; do { result = __LDREXW(ptr); if (result != temp) { *old = result; __STREXW(result, ptr); break; } } while ((__STREXW(new, ptr)) != 0U); return (result == temp); }