NinjaFlight
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
atomic.h
Go to the documentation of this file.
1 /*
2  * This file is part of Ninjaflight.
3  *
4  * Ninjaflight is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Ninjaflight is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Ninjaflight. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #pragma once
19 
20 // only set_BASEPRI is implemented in device library. It does always create memory barrier
21 // missing versions are implemented here
22 
23 // set BASEPRI and BASEPRI_MAX register, but do not create memory barrier
24 __attribute__( ( always_inline ) ) static inline void __set_BASEPRI_nb(uint32_t basePri)
25 {
26  __ASM volatile ("\tMSR basepri, %0\n" : : "r" (basePri) );
27 }
28 
29 __attribute__( ( always_inline ) ) static inline void __set_BASEPRI_MAX_nb(uint32_t basePri)
30 {
31  __ASM volatile ("\tMSR basepri_max, %0\n" : : "r" (basePri) );
32 }
33 
34 __attribute__( ( always_inline ) ) static inline void __set_BASEPRI_MAX(uint32_t basePri)
35 {
36  __ASM volatile ("\tMSR basepri_max, %0\n" : : "r" (basePri) : "memory" );
37 }
38 
39 // cleanup BASEPRI restore function, with global memory barrier
40 static inline void __basepriRestoreMem(uint8_t *val)
41 {
42  __set_BASEPRI(*val);
43 }
44 
45 // set BASEPRI_MAX function, with global memory barrier, returns true
46 static inline uint8_t __basepriSetMemRetVal(uint8_t prio)
47 {
48  __set_BASEPRI_MAX(prio);
49  return 1;
50 }
51 
52 // cleanup BASEPRI restore function, no memory barrier
53 static inline void __basepriRestore(uint8_t *val)
54 {
55  __set_BASEPRI_nb(*val);
56 }
57 
58 // set BASEPRI_MAX function, no memory barrier, returns true
59 static inline uint8_t __basepriSetRetVal(uint8_t prio)
60 {
61  __set_BASEPRI_MAX_nb(prio);
62  return 1;
63 }
64 
65 // Run block with elevated BASEPRI (using BASEPRI_MAX), restoring BASEPRI on exit. All exit paths are handled
66 // Full memory barrier is placed at start and exit of block
67 #define ATOMIC_BLOCK(prio) for ( uint8_t __basepri_save __attribute__((__cleanup__(__basepriRestoreMem))) = __get_BASEPRI(), \
68  __ToDo = __basepriSetMemRetVal(prio); __ToDo ; __ToDo = 0 )
69 
70 // Run block with elevated BASEPRI (using BASEPRI_MAX), but do not create any (explicit) memory barrier.
71 // Be careful when using this, you must use some method to prevent optimizer form breaking things
72 // - lto is used for Ninjaflight compilation, so function call is not memory barrier
73 // - use ATOMIC_BARRIER or proper volatile to protect used variables
74 // - gcc 4.8.4 does write all values in registers to memory before 'asm volatile', so this optimization does not help much
75 // but that can change in future versions
76 #define ATOMIC_BLOCK_NB(prio) for ( uint8_t __basepri_save __attribute__((__cleanup__(__basepriRestore))) = __get_BASEPRI(), \
77  __ToDo = __basepriSetRetVal(prio); __ToDo ; __ToDo = 0 ) \
78 
79 // ATOMIC_BARRIER
80 // Create memory barrier
81 // - at the beginning (all data must be reread from memory)
82 // - at exit of block (all exit paths) (all data must be written, but may be cached in register for subsequent use)
83 // ideally this would only protect memory passed as parameter (any type should work), but gcc is currently creating almost full barrier
84 // this macro can be used only ONCE PER LINE, but multiple uses per block are fine
85 
86 #if (__GNUC__ > 5)
87 #warning "Please verify that ATOMIC_BARRIER works as intended"
88 // increment version number is BARRIER works
89 // TODO - use flag to disable ATOMIC_BARRIER and use full barrier instead
90 // you should check that local variable scope with cleanup spans entire block
91 #endif
92 
93 #ifndef __UNIQL
94 # define __UNIQL_CONCAT2(x,y) x ## y
95 # define __UNIQL_CONCAT(x,y) __UNIQL_CONCAT2(x,y)
96 # define __UNIQL(x) __UNIQL_CONCAT(x,__LINE__)
97 #endif
98 
99 // this macro uses local function for cleanup. CLang block can be substituted
100 #define ATOMIC_BARRIER(data) \
101  __extension__ void __UNIQL(__barrierEnd)(typeof(data) **__d) { \
102  __asm__ volatile ("\t# barier(" #data ") end\n" : : "m" (**__d)); \
103  } \
104  typeof(data) __attribute__((__cleanup__(__UNIQL(__barrierEnd)))) *__UNIQL(__barrier) = &data; \
105  __asm__ volatile ("\t# barier (" #data ") start\n" : "=m" (*__UNIQL(__barrier)))
106 
107 
108 // define these wrappers for atomic operations, use gcc buildins
109 #define ATOMIC_OR(ptr, val) __sync_fetch_and_or(ptr, val)
110 #define ATOMIC_AND(ptr, val) __sync_fetch_and_and(ptr, val)
__attribute__((always_inline)) static inline void __set_BASEPRI_nb(uint32_t basePri)
Definition: atomic.h:24