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