xref: /Kernel-v10.6.2/include/atomic.h (revision ef7b253b56c9788077f5ecd6c9deb4021923d646)
1 /*
2  * FreeRTOS Kernel V10.6.2
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * https://www.FreeRTOS.org
25  * https://github.com/FreeRTOS
26  *
27  */
28 
29 /**
30  * @file atomic.h
31  * @brief FreeRTOS atomic operation support.
32  *
33  * This file implements atomic functions by disabling interrupts globally.
34  * Implementations with architecture specific atomic instructions can be
35  * provided under each compiler directory.
36  */
37 
38 #ifndef ATOMIC_H
39 #define ATOMIC_H
40 
41 #ifndef INC_FREERTOS_H
42     #error "include FreeRTOS.h must appear in source files before include atomic.h"
43 #endif
44 
45 /* Standard includes. */
46 #include <stdint.h>
47 
48 /* *INDENT-OFF* */
49 #ifdef __cplusplus
50     extern "C" {
51 #endif
52 /* *INDENT-ON* */
53 
54 /*
55  * Port specific definitions -- entering/exiting critical section.
56  * Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h
57  *
58  * Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with
59  * ATOMIC_ENTER_CRITICAL().
60  *
61  */
62 #if defined( portSET_INTERRUPT_MASK_FROM_ISR )
63 
64 /* Nested interrupt scheme is supported in this port. */
65     #define ATOMIC_ENTER_CRITICAL() \
66     UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR()
67 
68     #define ATOMIC_EXIT_CRITICAL() \
69     portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType )
70 
71 #else
72 
73 /* Nested interrupt scheme is NOT supported in this port. */
74     #define ATOMIC_ENTER_CRITICAL()    portENTER_CRITICAL()
75     #define ATOMIC_EXIT_CRITICAL()     portEXIT_CRITICAL()
76 
77 #endif /* portSET_INTERRUPT_MASK_FROM_ISR() */
78 
79 /*
80  * Port specific definition -- "always inline".
81  * Inline is compiler specific, and may not always get inlined depending on your
82  * optimization level.  Also, inline is considered as performance optimization
83  * for atomic.  Thus, if portFORCE_INLINE is not provided by portmacro.h,
84  * instead of resulting error, simply define it away.
85  */
86 #ifndef portFORCE_INLINE
87     #define portFORCE_INLINE
88 #endif
89 
90 #define ATOMIC_COMPARE_AND_SWAP_SUCCESS    0x1U     /**< Compare and swap succeeded, swapped. */
91 #define ATOMIC_COMPARE_AND_SWAP_FAILURE    0x0U     /**< Compare and swap failed, did not swap. */
92 
93 /*----------------------------- Swap && CAS ------------------------------*/
94 
95 /**
96  * Atomic compare-and-swap
97  *
98  * @brief Performs an atomic compare-and-swap operation on the specified values.
99  *
100  * @param[in, out] pulDestination  Pointer to memory location from where value is
101  *                               to be loaded and checked.
102  * @param[in] ulExchange         If condition meets, write this value to memory.
103  * @param[in] ulComparand        Swap condition.
104  *
105  * @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
106  *
107  * @note This function only swaps *pulDestination with ulExchange, if previous
108  *       *pulDestination value equals ulComparand.
109  */
Atomic_CompareAndSwap_u32(uint32_t volatile * pulDestination,uint32_t ulExchange,uint32_t ulComparand)110 static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32( uint32_t volatile * pulDestination,
111                                                             uint32_t ulExchange,
112                                                             uint32_t ulComparand )
113 {
114     uint32_t ulReturnValue;
115 
116     ATOMIC_ENTER_CRITICAL();
117     {
118         if( *pulDestination == ulComparand )
119         {
120             *pulDestination = ulExchange;
121             ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
122         }
123         else
124         {
125             ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
126         }
127     }
128     ATOMIC_EXIT_CRITICAL();
129 
130     return ulReturnValue;
131 }
132 /*-----------------------------------------------------------*/
133 
134 /**
135  * Atomic swap (pointers)
136  *
137  * @brief Atomically sets the address pointed to by *ppvDestination to the value
138  *        of *pvExchange.
139  *
140  * @param[in, out] ppvDestination  Pointer to memory location from where a pointer
141  *                                 value is to be loaded and written back to.
142  * @param[in] pvExchange           Pointer value to be written to *ppvDestination.
143  *
144  * @return The initial value of *ppvDestination.
145  */
Atomic_SwapPointers_p32(void * volatile * ppvDestination,void * pvExchange)146 static portFORCE_INLINE void * Atomic_SwapPointers_p32( void * volatile * ppvDestination,
147                                                         void * pvExchange )
148 {
149     void * pReturnValue;
150 
151     ATOMIC_ENTER_CRITICAL();
152     {
153         pReturnValue = *ppvDestination;
154         *ppvDestination = pvExchange;
155     }
156     ATOMIC_EXIT_CRITICAL();
157 
158     return pReturnValue;
159 }
160 /*-----------------------------------------------------------*/
161 
162 /**
163  * Atomic compare-and-swap (pointers)
164  *
165  * @brief Performs an atomic compare-and-swap operation on the specified pointer
166  *        values.
167  *
168  * @param[in, out] ppvDestination  Pointer to memory location from where a pointer
169  *                                 value is to be loaded and checked.
170  * @param[in] pvExchange           If condition meets, write this value to memory.
171  * @param[in] pvComparand          Swap condition.
172  *
173  * @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
174  *
175  * @note This function only swaps *ppvDestination with pvExchange, if previous
176  *       *ppvDestination value equals pvComparand.
177  */
Atomic_CompareAndSwapPointers_p32(void * volatile * ppvDestination,void * pvExchange,void * pvComparand)178 static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32( void * volatile * ppvDestination,
179                                                                     void * pvExchange,
180                                                                     void * pvComparand )
181 {
182     uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
183 
184     ATOMIC_ENTER_CRITICAL();
185     {
186         if( *ppvDestination == pvComparand )
187         {
188             *ppvDestination = pvExchange;
189             ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
190         }
191     }
192     ATOMIC_EXIT_CRITICAL();
193 
194     return ulReturnValue;
195 }
196 
197 
198 /*----------------------------- Arithmetic ------------------------------*/
199 
200 /**
201  * Atomic add
202  *
203  * @brief Atomically adds count to the value of the specified pointer points to.
204  *
205  * @param[in,out] pulAddend  Pointer to memory location from where value is to be
206  *                         loaded and written back to.
207  * @param[in] ulCount      Value to be added to *pulAddend.
208  *
209  * @return previous *pulAddend value.
210  */
Atomic_Add_u32(uint32_t volatile * pulAddend,uint32_t ulCount)211 static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend,
212                                                  uint32_t ulCount )
213 {
214     uint32_t ulCurrent;
215 
216     ATOMIC_ENTER_CRITICAL();
217     {
218         ulCurrent = *pulAddend;
219         *pulAddend += ulCount;
220     }
221     ATOMIC_EXIT_CRITICAL();
222 
223     return ulCurrent;
224 }
225 /*-----------------------------------------------------------*/
226 
227 /**
228  * Atomic subtract
229  *
230  * @brief Atomically subtracts count from the value of the specified pointer
231  *        pointers to.
232  *
233  * @param[in,out] pulAddend  Pointer to memory location from where value is to be
234  *                         loaded and written back to.
235  * @param[in] ulCount      Value to be subtract from *pulAddend.
236  *
237  * @return previous *pulAddend value.
238  */
Atomic_Subtract_u32(uint32_t volatile * pulAddend,uint32_t ulCount)239 static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAddend,
240                                                       uint32_t ulCount )
241 {
242     uint32_t ulCurrent;
243 
244     ATOMIC_ENTER_CRITICAL();
245     {
246         ulCurrent = *pulAddend;
247         *pulAddend -= ulCount;
248     }
249     ATOMIC_EXIT_CRITICAL();
250 
251     return ulCurrent;
252 }
253 /*-----------------------------------------------------------*/
254 
255 /**
256  * Atomic increment
257  *
258  * @brief Atomically increments the value of the specified pointer points to.
259  *
260  * @param[in,out] pulAddend  Pointer to memory location from where value is to be
261  *                         loaded and written back to.
262  *
263  * @return *pulAddend value before increment.
264  */
Atomic_Increment_u32(uint32_t volatile * pulAddend)265 static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pulAddend )
266 {
267     uint32_t ulCurrent;
268 
269     ATOMIC_ENTER_CRITICAL();
270     {
271         ulCurrent = *pulAddend;
272         *pulAddend += 1;
273     }
274     ATOMIC_EXIT_CRITICAL();
275 
276     return ulCurrent;
277 }
278 /*-----------------------------------------------------------*/
279 
280 /**
281  * Atomic decrement
282  *
283  * @brief Atomically decrements the value of the specified pointer points to
284  *
285  * @param[in,out] pulAddend  Pointer to memory location from where value is to be
286  *                         loaded and written back to.
287  *
288  * @return *pulAddend value before decrement.
289  */
Atomic_Decrement_u32(uint32_t volatile * pulAddend)290 static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pulAddend )
291 {
292     uint32_t ulCurrent;
293 
294     ATOMIC_ENTER_CRITICAL();
295     {
296         ulCurrent = *pulAddend;
297         *pulAddend -= 1;
298     }
299     ATOMIC_EXIT_CRITICAL();
300 
301     return ulCurrent;
302 }
303 
304 /*----------------------------- Bitwise Logical ------------------------------*/
305 
306 /**
307  * Atomic OR
308  *
309  * @brief Performs an atomic OR operation on the specified values.
310  *
311  * @param [in, out] pulDestination  Pointer to memory location from where value is
312  *                                to be loaded and written back to.
313  * @param [in] ulValue            Value to be ORed with *pulDestination.
314  *
315  * @return The original value of *pulDestination.
316  */
Atomic_OR_u32(uint32_t volatile * pulDestination,uint32_t ulValue)317 static portFORCE_INLINE uint32_t Atomic_OR_u32( uint32_t volatile * pulDestination,
318                                                 uint32_t ulValue )
319 {
320     uint32_t ulCurrent;
321 
322     ATOMIC_ENTER_CRITICAL();
323     {
324         ulCurrent = *pulDestination;
325         *pulDestination |= ulValue;
326     }
327     ATOMIC_EXIT_CRITICAL();
328 
329     return ulCurrent;
330 }
331 /*-----------------------------------------------------------*/
332 
333 /**
334  * Atomic AND
335  *
336  * @brief Performs an atomic AND operation on the specified values.
337  *
338  * @param [in, out] pulDestination  Pointer to memory location from where value is
339  *                                to be loaded and written back to.
340  * @param [in] ulValue            Value to be ANDed with *pulDestination.
341  *
342  * @return The original value of *pulDestination.
343  */
Atomic_AND_u32(uint32_t volatile * pulDestination,uint32_t ulValue)344 static portFORCE_INLINE uint32_t Atomic_AND_u32( uint32_t volatile * pulDestination,
345                                                  uint32_t ulValue )
346 {
347     uint32_t ulCurrent;
348 
349     ATOMIC_ENTER_CRITICAL();
350     {
351         ulCurrent = *pulDestination;
352         *pulDestination &= ulValue;
353     }
354     ATOMIC_EXIT_CRITICAL();
355 
356     return ulCurrent;
357 }
358 /*-----------------------------------------------------------*/
359 
360 /**
361  * Atomic NAND
362  *
363  * @brief Performs an atomic NAND operation on the specified values.
364  *
365  * @param [in, out] pulDestination  Pointer to memory location from where value is
366  *                                to be loaded and written back to.
367  * @param [in] ulValue            Value to be NANDed with *pulDestination.
368  *
369  * @return The original value of *pulDestination.
370  */
Atomic_NAND_u32(uint32_t volatile * pulDestination,uint32_t ulValue)371 static portFORCE_INLINE uint32_t Atomic_NAND_u32( uint32_t volatile * pulDestination,
372                                                   uint32_t ulValue )
373 {
374     uint32_t ulCurrent;
375 
376     ATOMIC_ENTER_CRITICAL();
377     {
378         ulCurrent = *pulDestination;
379         *pulDestination = ~( ulCurrent & ulValue );
380     }
381     ATOMIC_EXIT_CRITICAL();
382 
383     return ulCurrent;
384 }
385 /*-----------------------------------------------------------*/
386 
387 /**
388  * Atomic XOR
389  *
390  * @brief Performs an atomic XOR operation on the specified values.
391  *
392  * @param [in, out] pulDestination  Pointer to memory location from where value is
393  *                                to be loaded and written back to.
394  * @param [in] ulValue            Value to be XORed with *pulDestination.
395  *
396  * @return The original value of *pulDestination.
397  */
Atomic_XOR_u32(uint32_t volatile * pulDestination,uint32_t ulValue)398 static portFORCE_INLINE uint32_t Atomic_XOR_u32( uint32_t volatile * pulDestination,
399                                                  uint32_t ulValue )
400 {
401     uint32_t ulCurrent;
402 
403     ATOMIC_ENTER_CRITICAL();
404     {
405         ulCurrent = *pulDestination;
406         *pulDestination ^= ulValue;
407     }
408     ATOMIC_EXIT_CRITICAL();
409 
410     return ulCurrent;
411 }
412 
413 /* *INDENT-OFF* */
414 #ifdef __cplusplus
415     }
416 #endif
417 /* *INDENT-ON* */
418 
419 #endif /* ATOMIC_H */
420