1 /*
2  * FreeRTOS Kernel V11.1.0
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  * The atomic interface can be used in FreeRTOS tasks on all FreeRTOS ports. It
38  * can also be used in Interrupt Service Routines (ISRs) on FreeRTOS ports that
39  * support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 1). The
40  * atomic interface must not be used in ISRs on FreeRTOS ports that do not
41  * support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 0)
42  * because ISRs on these ports cannot be interrupted and therefore, do not need
43  * atomics in ISRs.
44  */
45 
46 #ifndef ATOMIC_H
47 #define ATOMIC_H
48 
49 #ifndef INC_FREERTOS_H
50     #error "include FreeRTOS.h must appear in source files before include atomic.h"
51 #endif
52 
53 /* Standard includes. */
54 #include <stdint.h>
55 
56 /* *INDENT-OFF* */
57 #ifdef __cplusplus
58     extern "C" {
59 #endif
60 /* *INDENT-ON* */
61 
62 /*
63  * Port specific definitions -- entering/exiting critical section.
64  * Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h
65  *
66  * Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with
67  * ATOMIC_ENTER_CRITICAL().
68  *
69  */
70 #if ( portHAS_NESTED_INTERRUPTS == 1 )
71 
72 /* Nested interrupt scheme is supported in this port. */
73     #define ATOMIC_ENTER_CRITICAL() \
74     UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR()
75 
76     #define ATOMIC_EXIT_CRITICAL() \
77     portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType )
78 
79 #else
80 
81 /* Nested interrupt scheme is NOT supported in this port. */
82     #define ATOMIC_ENTER_CRITICAL()    portENTER_CRITICAL()
83     #define ATOMIC_EXIT_CRITICAL()     portEXIT_CRITICAL()
84 
85 #endif /* portSET_INTERRUPT_MASK_FROM_ISR() */
86 
87 /*
88  * Port specific definition -- "always inline".
89  * Inline is compiler specific, and may not always get inlined depending on your
90  * optimization level.  Also, inline is considered as performance optimization
91  * for atomic.  Thus, if portFORCE_INLINE is not provided by portmacro.h,
92  * instead of resulting error, simply define it away.
93  */
94 #ifndef portFORCE_INLINE
95     #define portFORCE_INLINE
96 #endif
97 
98 #define ATOMIC_COMPARE_AND_SWAP_SUCCESS    0x1U     /**< Compare and swap succeeded, swapped. */
99 #define ATOMIC_COMPARE_AND_SWAP_FAILURE    0x0U     /**< Compare and swap failed, did not swap. */
100 
101 /*----------------------------- Swap && CAS ------------------------------*/
102 
103 /**
104  * Atomic compare-and-swap
105  *
106  * @brief Performs an atomic compare-and-swap operation on the specified values.
107  *
108  * @param[in, out] pulDestination  Pointer to memory location from where value is
109  *                               to be loaded and checked.
110  * @param[in] ulExchange         If condition meets, write this value to memory.
111  * @param[in] ulComparand        Swap condition.
112  *
113  * @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
114  *
115  * @note This function only swaps *pulDestination with ulExchange, if previous
116  *       *pulDestination value equals ulComparand.
117  */
Atomic_CompareAndSwap_u32(uint32_t volatile * pulDestination,uint32_t ulExchange,uint32_t ulComparand)118 static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32( uint32_t volatile * pulDestination,
119                                                             uint32_t ulExchange,
120                                                             uint32_t ulComparand )
121 {
122     uint32_t ulReturnValue;
123 
124     ATOMIC_ENTER_CRITICAL();
125     {
126         if( *pulDestination == ulComparand )
127         {
128             *pulDestination = ulExchange;
129             ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
130         }
131         else
132         {
133             ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
134         }
135     }
136     ATOMIC_EXIT_CRITICAL();
137 
138     return ulReturnValue;
139 }
140 /*-----------------------------------------------------------*/
141 
142 /**
143  * Atomic swap (pointers)
144  *
145  * @brief Atomically sets the address pointed to by *ppvDestination to the value
146  *        of *pvExchange.
147  *
148  * @param[in, out] ppvDestination  Pointer to memory location from where a pointer
149  *                                 value is to be loaded and written back to.
150  * @param[in] pvExchange           Pointer value to be written to *ppvDestination.
151  *
152  * @return The initial value of *ppvDestination.
153  */
Atomic_SwapPointers_p32(void * volatile * ppvDestination,void * pvExchange)154 static portFORCE_INLINE void * Atomic_SwapPointers_p32( void * volatile * ppvDestination,
155                                                         void * pvExchange )
156 {
157     void * pReturnValue;
158 
159     ATOMIC_ENTER_CRITICAL();
160     {
161         pReturnValue = *ppvDestination;
162         *ppvDestination = pvExchange;
163     }
164     ATOMIC_EXIT_CRITICAL();
165 
166     return pReturnValue;
167 }
168 /*-----------------------------------------------------------*/
169 
170 /**
171  * Atomic compare-and-swap (pointers)
172  *
173  * @brief Performs an atomic compare-and-swap operation on the specified pointer
174  *        values.
175  *
176  * @param[in, out] ppvDestination  Pointer to memory location from where a pointer
177  *                                 value is to be loaded and checked.
178  * @param[in] pvExchange           If condition meets, write this value to memory.
179  * @param[in] pvComparand          Swap condition.
180  *
181  * @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
182  *
183  * @note This function only swaps *ppvDestination with pvExchange, if previous
184  *       *ppvDestination value equals pvComparand.
185  */
Atomic_CompareAndSwapPointers_p32(void * volatile * ppvDestination,void * pvExchange,void * pvComparand)186 static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32( void * volatile * ppvDestination,
187                                                                     void * pvExchange,
188                                                                     void * pvComparand )
189 {
190     uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
191 
192     ATOMIC_ENTER_CRITICAL();
193     {
194         if( *ppvDestination == pvComparand )
195         {
196             *ppvDestination = pvExchange;
197             ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
198         }
199     }
200     ATOMIC_EXIT_CRITICAL();
201 
202     return ulReturnValue;
203 }
204 
205 
206 /*----------------------------- Arithmetic ------------------------------*/
207 
208 /**
209  * Atomic add
210  *
211  * @brief Atomically adds count to the value of the specified pointer points to.
212  *
213  * @param[in,out] pulAddend  Pointer to memory location from where value is to be
214  *                         loaded and written back to.
215  * @param[in] ulCount      Value to be added to *pulAddend.
216  *
217  * @return previous *pulAddend value.
218  */
Atomic_Add_u32(uint32_t volatile * pulAddend,uint32_t ulCount)219 static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend,
220                                                  uint32_t ulCount )
221 {
222     uint32_t ulCurrent;
223 
224     ATOMIC_ENTER_CRITICAL();
225     {
226         ulCurrent = *pulAddend;
227         *pulAddend += ulCount;
228     }
229     ATOMIC_EXIT_CRITICAL();
230 
231     return ulCurrent;
232 }
233 /*-----------------------------------------------------------*/
234 
235 /**
236  * Atomic subtract
237  *
238  * @brief Atomically subtracts count from the value of the specified pointer
239  *        pointers to.
240  *
241  * @param[in,out] pulAddend  Pointer to memory location from where value is to be
242  *                         loaded and written back to.
243  * @param[in] ulCount      Value to be subtract from *pulAddend.
244  *
245  * @return previous *pulAddend value.
246  */
Atomic_Subtract_u32(uint32_t volatile * pulAddend,uint32_t ulCount)247 static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAddend,
248                                                       uint32_t ulCount )
249 {
250     uint32_t ulCurrent;
251 
252     ATOMIC_ENTER_CRITICAL();
253     {
254         ulCurrent = *pulAddend;
255         *pulAddend -= ulCount;
256     }
257     ATOMIC_EXIT_CRITICAL();
258 
259     return ulCurrent;
260 }
261 /*-----------------------------------------------------------*/
262 
263 /**
264  * Atomic increment
265  *
266  * @brief Atomically increments the value of the specified pointer points to.
267  *
268  * @param[in,out] pulAddend  Pointer to memory location from where value is to be
269  *                         loaded and written back to.
270  *
271  * @return *pulAddend value before increment.
272  */
Atomic_Increment_u32(uint32_t volatile * pulAddend)273 static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pulAddend )
274 {
275     uint32_t ulCurrent;
276 
277     ATOMIC_ENTER_CRITICAL();
278     {
279         ulCurrent = *pulAddend;
280         *pulAddend += 1;
281     }
282     ATOMIC_EXIT_CRITICAL();
283 
284     return ulCurrent;
285 }
286 /*-----------------------------------------------------------*/
287 
288 /**
289  * Atomic decrement
290  *
291  * @brief Atomically decrements the value of the specified pointer points to
292  *
293  * @param[in,out] pulAddend  Pointer to memory location from where value is to be
294  *                         loaded and written back to.
295  *
296  * @return *pulAddend value before decrement.
297  */
Atomic_Decrement_u32(uint32_t volatile * pulAddend)298 static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pulAddend )
299 {
300     uint32_t ulCurrent;
301 
302     ATOMIC_ENTER_CRITICAL();
303     {
304         ulCurrent = *pulAddend;
305         *pulAddend -= 1;
306     }
307     ATOMIC_EXIT_CRITICAL();
308 
309     return ulCurrent;
310 }
311 
312 /*----------------------------- Bitwise Logical ------------------------------*/
313 
314 /**
315  * Atomic OR
316  *
317  * @brief Performs an atomic OR operation on the specified values.
318  *
319  * @param [in, out] pulDestination  Pointer to memory location from where value is
320  *                                to be loaded and written back to.
321  * @param [in] ulValue            Value to be ORed with *pulDestination.
322  *
323  * @return The original value of *pulDestination.
324  */
Atomic_OR_u32(uint32_t volatile * pulDestination,uint32_t ulValue)325 static portFORCE_INLINE uint32_t Atomic_OR_u32( uint32_t volatile * pulDestination,
326                                                 uint32_t ulValue )
327 {
328     uint32_t ulCurrent;
329 
330     ATOMIC_ENTER_CRITICAL();
331     {
332         ulCurrent = *pulDestination;
333         *pulDestination |= ulValue;
334     }
335     ATOMIC_EXIT_CRITICAL();
336 
337     return ulCurrent;
338 }
339 /*-----------------------------------------------------------*/
340 
341 /**
342  * Atomic AND
343  *
344  * @brief Performs an atomic AND operation on the specified values.
345  *
346  * @param [in, out] pulDestination  Pointer to memory location from where value is
347  *                                to be loaded and written back to.
348  * @param [in] ulValue            Value to be ANDed with *pulDestination.
349  *
350  * @return The original value of *pulDestination.
351  */
Atomic_AND_u32(uint32_t volatile * pulDestination,uint32_t ulValue)352 static portFORCE_INLINE uint32_t Atomic_AND_u32( uint32_t volatile * pulDestination,
353                                                  uint32_t ulValue )
354 {
355     uint32_t ulCurrent;
356 
357     ATOMIC_ENTER_CRITICAL();
358     {
359         ulCurrent = *pulDestination;
360         *pulDestination &= ulValue;
361     }
362     ATOMIC_EXIT_CRITICAL();
363 
364     return ulCurrent;
365 }
366 /*-----------------------------------------------------------*/
367 
368 /**
369  * Atomic NAND
370  *
371  * @brief Performs an atomic NAND operation on the specified values.
372  *
373  * @param [in, out] pulDestination  Pointer to memory location from where value is
374  *                                to be loaded and written back to.
375  * @param [in] ulValue            Value to be NANDed with *pulDestination.
376  *
377  * @return The original value of *pulDestination.
378  */
Atomic_NAND_u32(uint32_t volatile * pulDestination,uint32_t ulValue)379 static portFORCE_INLINE uint32_t Atomic_NAND_u32( uint32_t volatile * pulDestination,
380                                                   uint32_t ulValue )
381 {
382     uint32_t ulCurrent;
383 
384     ATOMIC_ENTER_CRITICAL();
385     {
386         ulCurrent = *pulDestination;
387         *pulDestination = ~( ulCurrent & ulValue );
388     }
389     ATOMIC_EXIT_CRITICAL();
390 
391     return ulCurrent;
392 }
393 /*-----------------------------------------------------------*/
394 
395 /**
396  * Atomic XOR
397  *
398  * @brief Performs an atomic XOR operation on the specified values.
399  *
400  * @param [in, out] pulDestination  Pointer to memory location from where value is
401  *                                to be loaded and written back to.
402  * @param [in] ulValue            Value to be XORed with *pulDestination.
403  *
404  * @return The original value of *pulDestination.
405  */
Atomic_XOR_u32(uint32_t volatile * pulDestination,uint32_t ulValue)406 static portFORCE_INLINE uint32_t Atomic_XOR_u32( uint32_t volatile * pulDestination,
407                                                  uint32_t ulValue )
408 {
409     uint32_t ulCurrent;
410 
411     ATOMIC_ENTER_CRITICAL();
412     {
413         ulCurrent = *pulDestination;
414         *pulDestination ^= ulValue;
415     }
416     ATOMIC_EXIT_CRITICAL();
417 
418     return ulCurrent;
419 }
420 
421 /* *INDENT-OFF* */
422 #ifdef __cplusplus
423     }
424 #endif
425 /* *INDENT-ON* */
426 
427 #endif /* ATOMIC_H */
428