xref: /nrf52832-nimble/nordic/nrfx/soc/nrfx_atomic_internal.h (revision 150812a83cab50279bd772ef6db1bfaf255f2c5b)
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