1 /* 2 * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * 3. Neither the name of the copyright holder nor the names of its 16 * contributors may be used to endorse or promote products derived from this 17 * software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #ifndef NRFX_ATOMIC_INTERNAL_H__ 33 #define NRFX_ATOMIC_INTERNAL_H__ 34 35 #include <nrfx.h> 36 37 #ifdef __cplusplus 38 extern "C" { 39 #endif 40 41 /* Only Cortex-M cores > 3 support LDREX/STREX instructions. */ 42 #if ((__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)) == 0 43 #error "Unsupported core version" 44 #endif 45 46 #if defined ( __CC_ARM ) 47 static __asm uint32_t nrfx_atomic_internal_mov(nrfx_atomic_u32_t * p_ptr, 48 uint32_t value, 49 uint32_t * p_new) 50 { 51 /* The base standard specifies that arguments are passed in core registers r0-r3 and on the stack. 52 * Registers r4 and r5 must be saved on the stack. Note that only even number of register pushes are 53 * allowed. This is a requirement of the Procedure Call Standard for the ARM Architecture [AAPCS]. 54 */ 55 push {r4, r5} 56 mov r4, r0 57 58 loop_mov 59 ldrex r0, [r4] 60 mov r5, r1 61 strex r3, r5, [r4] 62 cmp r3, #0 63 bne loop_mov 64 65 str r5, [r2] 66 pop {r4, r5} 67 bx lr 68 } 69 70 71 static __asm uint32_t nrfx_atomic_internal_orr(nrfx_atomic_u32_t * p_ptr, 72 uint32_t value, 73 uint32_t * p_new) 74 { 75 push {r4, r5} 76 mov r4, r0 77 78 loop_orr 79 ldrex r0, [r4] 80 orr r5, r0, r1 81 strex r3, r5, [r4] 82 cmp r3, #0 83 bne loop_orr 84 85 str r5, [r2] 86 pop {r4, r5} 87 bx lr 88 } 89 90 static __asm uint32_t nrfx_atomic_internal_and(nrfx_atomic_u32_t * p_ptr, 91 uint32_t value, 92 uint32_t * p_new) 93 { 94 push {r4, r5} 95 mov r4, r0 96 97 loop_and 98 ldrex r0, [r4] 99 and r5, r0, r1 100 strex r3, r5, [r4] 101 cmp r3, #0 102 bne loop_and 103 104 str r5, [r2] 105 pop {r4, r5} 106 bx lr 107 } 108 109 static __asm uint32_t nrfx_atomic_internal_eor(nrfx_atomic_u32_t * p_ptr, 110 uint32_t value, 111 uint32_t * p_new) 112 { 113 push {r4, r5} 114 mov r4, r0 115 116 loop_eor 117 ldrex r0, [r4] 118 eor r5, r0, r1 119 strex r3, r5, [r4] 120 cmp r3, #0 121 bne loop_eor 122 123 str r5, [r2] 124 pop {r4, r5} 125 bx lr 126 } 127 128 static __asm uint32_t nrfx_atomic_internal_add(nrfx_atomic_u32_t * p_ptr, 129 uint32_t value, 130 uint32_t * p_new) 131 { 132 push {r4, r5} 133 mov r4, r0 134 135 loop_add 136 ldrex r0, [r4] 137 add r5, r0, r1 138 strex r3, r5, [r4] 139 cmp r3, #0 140 bne loop_add 141 142 str r5, [r2] 143 pop {r4, r5} 144 bx lr 145 } 146 147 static __asm uint32_t nrfx_atomic_internal_sub(nrfx_atomic_u32_t * p_ptr, 148 uint32_t value, 149 uint32_t * p_new) 150 { 151 push {r4, r5} 152 mov r4, r0 153 154 loop_sub 155 ldrex r0, [r4] 156 sub r5, r0, r1 157 strex r3, r5, [r4] 158 cmp r3, #0 159 bne loop_sub 160 161 str r5, [r2] 162 pop {r4, r5} 163 bx lr 164 } 165 166 static __asm bool nrfx_atomic_internal_cmp_exch(nrfx_atomic_u32_t * p_data, 167 uint32_t * p_expected, 168 uint32_t value) 169 { 170 #define RET_REG r0 171 #define P_EXPC r1 172 #define VALUE r2 173 #define STR_RES r3 174 #define P_DATA r4 175 #define EXPC_VAL r5 176 #define ACT_VAL r6 177 178 push {r4-r6} 179 mov P_DATA, r0 180 mov RET_REG, #0 181 182 loop_cmp_exch 183 ldrex ACT_VAL, [P_DATA] 184 ldr EXPC_VAL, [P_EXPC] 185 cmp ACT_VAL, EXPC_VAL 186 ittee eq 187 strexeq STR_RES, VALUE, [P_DATA] 188 moveq RET_REG, #1 189 strexne STR_RES, ACT_VAL, [P_DATA] 190 strne ACT_VAL, [P_EXPC] 191 cmp STR_RES, #0 192 itt ne 193 movne RET_REG, #0 194 bne loop_cmp_exch 195 196 pop {r4-r6} 197 bx lr 198 199 #undef RET_REG 200 #undef P_EXPC 201 #undef VALUE 202 #undef STR_RES 203 #undef P_DATA 204 #undef EXPC_VAL 205 #undef ACT_VAL 206 } 207 208 static __asm uint32_t nrfx_atomic_internal_sub_hs(nrfx_atomic_u32_t * p_ptr, 209 uint32_t value, 210 uint32_t * p_new) 211 { 212 push {r4, r5} 213 mov r4, r0 214 215 loop_sub_ge 216 ldrex r0, [r4] 217 cmp r0, r1 218 ite hs 219 subhs r5, r0, r1 220 movlo r5, r0 221 strex r3, r5, [r4] 222 cmp r3, #0 223 bne loop_sub_ge 224 225 str r5, [r2] 226 pop {r4, r5} 227 bx lr 228 } 229 230 231 #define NRFX_ATOMIC_OP(asm_op, old_val, new_val, ptr, value) \ 232 old_val = nrfx_atomic_internal_##asm_op(ptr, value, &new_val) 233 234 #elif defined ( __ICCARM__ ) || defined ( __GNUC__ ) 235 236 /** 237 * @brief Atomic operation generic macro. 238 * 239 * @param[in] asm_op Operation: mov, orr, and, eor, add, sub. 240 * @param[out] old_val Atomic object output (uint32_t), value before operation. 241 * @param[out] new_val Atomic object output (uint32_t), value after operation. 242 * @param[in] value Atomic operation operand. 243 */ 244 #define NRFX_ATOMIC_OP(asm_op, old_val, new_val, ptr, value) \ 245 { \ 246 uint32_t tmp_reg; \ 247 __ASM volatile( \ 248 "1: ldrex %["#old_val"], [%["#ptr"]]\n" \ 249 NRFX_ATOMIC_OP_##asm_op(new_val, old_val, value) \ 250 " strex %[tmp_reg], %["#new_val"], [%["#ptr"]]\n" \ 251 " teq %[tmp_reg], #0\n" \ 252 " bne.n 1b" \ 253 : \ 254 [old_val] "=&r" (old_val), \ 255 [new_val] "=&r" (new_val), \ 256 [tmp_reg] "=&r" (tmp_reg) \ 257 : \ 258 [ptr] "r" (ptr), \ 259 [value] "r" (value) \ 260 : "cc"); \ 261 (void)tmp_reg; \ 262 } 263 264 #define NRFX_ATOMIC_OP_mov(new_val, old_val, value) "mov %["#new_val"], %["#value"]\n" 265 #define NRFX_ATOMIC_OP_orr(new_val, old_val, value) "orr %["#new_val"], %["#old_val"], %["#value"]\n" 266 #define NRFX_ATOMIC_OP_and(new_val, old_val, value) "and %["#new_val"], %["#old_val"], %["#value"]\n" 267 #define NRFX_ATOMIC_OP_eor(new_val, old_val, value) "eor %["#new_val"], %["#old_val"], %["#value"]\n" 268 #define NRFX_ATOMIC_OP_add(new_val, old_val, value) "add %["#new_val"], %["#old_val"], %["#value"]\n" 269 #define NRFX_ATOMIC_OP_sub(new_val, old_val, value) "sub %["#new_val"], %["#old_val"], %["#value"]\n" 270 #define NRFX_ATOMIC_OP_sub_hs(new_val, old_val, value) \ 271 "cmp %["#old_val"], %["#value"]\n " \ 272 "ite hs\n" \ 273 "subhs %["#new_val"], %["#old_val"], %["#value"]\n" \ 274 "movlo %["#new_val"], %["#old_val"]\n" 275 276 static inline bool nrfx_atomic_internal_cmp_exch(nrfx_atomic_u32_t * p_data, 277 uint32_t * p_expected, 278 uint32_t value) 279 { 280 bool res = false; 281 /* Temporary register used in the inline asm code for getting the result 282 * of the strex* operations (no need to initialize it). 283 */ 284 uint32_t tmp_reg; 285 uint32_t act_val = 0; 286 uint32_t exp_val = 0; 287 __ASM volatile( 288 "1: ldrex %[act_val], [%[ptr]]\n" 289 " ldr %[exp_val], [%[expc]]\n" 290 " cmp %[act_val], %[exp_val]\n" 291 " ittee eq\n" 292 " strexeq %[tmp_reg], %[value], [%[ptr]]\n" 293 " moveq %[res], #1\n" 294 " strexne %[tmp_reg], %[act_val], [%[ptr]]\n" 295 " strne %[act_val], [%[expc]]\n" 296 " cmp %[tmp_reg], #0\n" 297 " itt ne\n" 298 " movne %[res], #0\n" 299 " bne.n 1b" 300 : 301 [res] "=&r" (res), 302 [exp_val] "=&r" (exp_val), 303 [act_val] "=&r" (act_val), 304 [tmp_reg] "=&r" (tmp_reg) 305 : 306 "0" (res), 307 "1" (exp_val), 308 "2" (act_val), 309 [expc] "r" (p_expected), 310 [ptr] "r" (p_data), 311 [value] "r" (value) 312 : "cc"); 313 (void)tmp_reg; 314 return res; 315 } 316 317 #else 318 #error "Unsupported compiler" 319 #endif 320 321 #ifdef __cplusplus 322 } 323 #endif 324 325 #endif // NRFX_ATOMIC_INTERNAL_H__ 326