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