root / demos / hunter_prey / lib / src / libdragonfly / atomic.h @ 1828
History | View | Annotate | Download (10 KB)
1 | 1828 | emullini | /* This file is in the dragonfly library directory because it seems to be
|
---|---|---|---|
2 | missing from the AVR libc distribution */
|
||
3 | |||
4 | /* Copyright (c) 2007 Dean Camera
|
||
5 | All rights reserved.
|
||
6 | |||
7 | Redistribution and use in source and binary forms, with or without
|
||
8 | modification, are permitted provided that the following conditions are met:
|
||
9 | |||
10 | * Redistributions of source code must retain the above copyright
|
||
11 | notice, this list of conditions and the following disclaimer.
|
||
12 | |||
13 | * Redistributions in binary form must reproduce the above copyright
|
||
14 | notice, this list of conditions and the following disclaimer in
|
||
15 | the documentation and/or other materials provided with the
|
||
16 | distribution.
|
||
17 | |||
18 | * Neither the name of the copyright holders nor the names of
|
||
19 | contributors may be used to endorse or promote products derived
|
||
20 | from this software without specific prior written permission.
|
||
21 | |||
22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
25 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||
26 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
27 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
28 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
29 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
30 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
32 | POSSIBILITY OF SUCH DAMAGE.
|
||
33 | */
|
||
34 | |||
35 | /* $Id: atomic.h,v 1.3 2007/12/20 14:17:56 joerg_wunsch Exp $ */
|
||
36 | |||
37 | #ifndef _UTIL_ATOMIC_H_
|
||
38 | #define _UTIL_ATOMIC_H_ 1 |
||
39 | |||
40 | #include <avr/io.h> |
||
41 | #include <avr/interrupt.h> |
||
42 | |||
43 | #if !defined(__DOXYGEN__)
|
||
44 | /* Internal helper functions. */
|
||
45 | static __inline__ uint8_t __iSeiRetVal(void) |
||
46 | { |
||
47 | sei(); |
||
48 | return 1; |
||
49 | } |
||
50 | |||
51 | static __inline__ uint8_t __iCliRetVal(void) |
||
52 | { |
||
53 | cli(); |
||
54 | return 1; |
||
55 | } |
||
56 | |||
57 | static __inline__ void __iSeiParam(const uint8_t *__s) |
||
58 | { |
||
59 | sei(); |
||
60 | __asm__ volatile ("" ::: "memory"); |
||
61 | (void)__s;
|
||
62 | } |
||
63 | |||
64 | static __inline__ void __iCliParam(const uint8_t *__s) |
||
65 | { |
||
66 | cli(); |
||
67 | __asm__ volatile ("" ::: "memory"); |
||
68 | (void)__s;
|
||
69 | } |
||
70 | |||
71 | static __inline__ void __iRestore(const uint8_t *__s) |
||
72 | { |
||
73 | SREG = *__s; |
||
74 | __asm__ volatile ("" ::: "memory"); |
||
75 | } |
||
76 | #endif /* !__DOXYGEN__ */ |
||
77 | |||
78 | /** \file */
|
||
79 | /** \defgroup util_atomic <util/atomic.h> Atomically and Non-Atomically Executed Code Blocks
|
||
80 | |||
81 | \code
|
||
82 | #include <util/atomic.h>
|
||
83 | \endcode
|
||
84 | |||
85 | \note The macros in this header file require the ISO/IEC 9899:1999
|
||
86 | ("ISO C99") feature of for loop variables that are declared inside
|
||
87 | the for loop itself. For that reason, this header file can only
|
||
88 | be used if the standard level of the compiler (option --std=) is
|
||
89 | set to either \c c99 or \c gnu99.
|
||
90 | |||
91 | The macros in this header file deal with code blocks that are
|
||
92 | guaranteed to be excuted Atomically or Non-Atmomically. The term
|
||
93 | "Atomic" in this context refers to the unability of the respective
|
||
94 | code to be interrupted.
|
||
95 | |||
96 | These macros operate via automatic manipulation of the Global
|
||
97 | Interrupt Status (I) bit of the SREG register. Exit paths from
|
||
98 | both block types are all managed automatically without the need
|
||
99 | for special considerations, i. e. the interrupt status will be
|
||
100 | restored to the same value it has been when entering the
|
||
101 | respective block.
|
||
102 | |||
103 | A typical example that requires atomic access is a 16 (or more)
|
||
104 | bit variable that is shared between the main execution path and an
|
||
105 | ISR. While declaring such a variable as volatile ensures that the
|
||
106 | compiler will not optimize accesses to it away, it does not
|
||
107 | guarantee atomic access to it. Assuming the following example:
|
||
108 | |||
109 | \code
|
||
110 | #include <inttypes.h>
|
||
111 | #include <avr/interrupt.h>
|
||
112 | #include <avr/io.h>
|
||
113 | |||
114 | volatile uint16_t ctr;
|
||
115 | |||
116 | ISR(TIMER1_OVF_vect)
|
||
117 | {
|
||
118 | ctr--;
|
||
119 | }
|
||
120 | |||
121 | ...
|
||
122 | int
|
||
123 | main(void)
|
||
124 | {
|
||
125 | ...
|
||
126 | ctr = 0x200;
|
||
127 | start_timer();
|
||
128 | while (ctr != 0)
|
||
129 | // wait
|
||
130 | ;
|
||
131 | ...
|
||
132 | }
|
||
133 | \endcode
|
||
134 | |||
135 | There is a chance where the main context will exit its wait loop
|
||
136 | when the variable \c ctr just reached the value 0xFF. This happens
|
||
137 | because the compiler cannot natively access a 16-bit variable
|
||
138 | atomically in an 8-bit CPU. So the variable is for example at
|
||
139 | 0x100, the compiler then tests the low byte for 0, which succeeds.
|
||
140 | It then proceeds to test the high byte, but that moment the ISR
|
||
141 | triggers, and the main context is interrupted. The ISR will
|
||
142 | decrement the variable from 0x100 to 0xFF, and the main context
|
||
143 | proceeds. It now tests the high byte of the variable which is
|
||
144 | (now) also 0, so it concludes the variable has reached 0, and
|
||
145 | terminates the loop.
|
||
146 | |||
147 | Using the macros from this header file, the above code can be
|
||
148 | rewritten like:
|
||
149 | |||
150 | \code
|
||
151 | #include <inttypes.h>
|
||
152 | #include <avr/interrupt.h>
|
||
153 | #include <avr/io.h>
|
||
154 | #include <util/atomic.h>
|
||
155 | |||
156 | volatile uint16_t ctr;
|
||
157 | |||
158 | ISR(TIMER1_OVF_vect)
|
||
159 | {
|
||
160 | ctr--;
|
||
161 | }
|
||
162 | |||
163 | ...
|
||
164 | int
|
||
165 | main(void)
|
||
166 | {
|
||
167 | ...
|
||
168 | ctr = 0x200;
|
||
169 | start_timer();
|
||
170 | sei();
|
||
171 | uint16_t ctr_copy;
|
||
172 | do
|
||
173 | {
|
||
174 | ATOMIC_BLOCK(ATOMIC_FORCEON)
|
||
175 | {
|
||
176 | ctr_copy = ctr;
|
||
177 | }
|
||
178 | }
|
||
179 | while (ctr_copy != 0);
|
||
180 | ...
|
||
181 | }
|
||
182 | \endcode
|
||
183 | |||
184 | This will install the appropriate interrupt protection before
|
||
185 | accessing variable \c ctr, so it is guaranteed to be consistently
|
||
186 | tested. If the global interrupt state were uncertain before
|
||
187 | entering the ATOMIC_BLOCK, it should be executed with the
|
||
188 | parameter ATOMIC_RESTORESTATE rather than ATOMIC_FORCEON.
|
||
189 | |||
190 | */
|
||
191 | |||
192 | /** \def ATOMIC_BLOCK(type)
|
||
193 | \ingroup util_atomic
|
||
194 | |||
195 | Creates a block of code that is guaranteed to be executed
|
||
196 | atomically. Upon entering the block the Global Interrupt Status
|
||
197 | flag in SREG is disabled, and re-enabled upon exiting the block
|
||
198 | from any exit path.
|
||
199 | |||
200 | Two possible macro parameters are permitted, ATOMIC_RESTORESTATE
|
||
201 | and ATOMIC_FORCEON.
|
||
202 | */
|
||
203 | #if defined(__DOXYGEN__)
|
||
204 | #define ATOMIC_BLOCK(type)
|
||
205 | #else
|
||
206 | #define ATOMIC_BLOCK(type) for ( type, __ToDo = __iCliRetVal(); \ |
||
207 | __ToDo ; __ToDo = 0 )
|
||
208 | #endif /* __DOXYGEN__ */ |
||
209 | |||
210 | /** \def NONATOMIC_BLOCK(type)
|
||
211 | \ingroup util_atomic
|
||
212 | |||
213 | Creates a block of code that is executed non-atomically. Upon
|
||
214 | entering the block the Global Interrupt Status flag in SREG is
|
||
215 | enabled, and disabled upon exiting the block from any exit
|
||
216 | path. This is useful when nested inside ATOMIC_BLOCK sections,
|
||
217 | allowing for non-atomic execution of small blocks of code while
|
||
218 | maintaining the atomic access of the other sections of the parent
|
||
219 | ATOMIC_BLOCK.
|
||
220 | |||
221 | Two possible macro parameters are permitted,
|
||
222 | NONATOMIC_RESTORESTATE and NONATOMIC_FORCEOFF.
|
||
223 | */
|
||
224 | #if defined(__DOXYGEN__)
|
||
225 | #define NONATOMIC_BLOCK(type)
|
||
226 | #else
|
||
227 | #define NONATOMIC_BLOCK(type) for ( type, __ToDo = __iSeiRetVal(); \ |
||
228 | __ToDo ; __ToDo = 0 )
|
||
229 | #endif /* __DOXYGEN__ */ |
||
230 | |||
231 | /** \def ATOMIC_RESTORESTATE
|
||
232 | \ingroup util_atomic
|
||
233 | |||
234 | This is a possible parameter for ATOMIC_BLOCK. When used, it will
|
||
235 | cause the ATOMIC_BLOCK to restore the previous state of the SREG
|
||
236 | register, saved before the Global Interrupt Status flag bit was
|
||
237 | disabled. The net effect of this is to make the ATOMIC_BLOCK's
|
||
238 | contents guaranteed atomic, without changing the state of the
|
||
239 | Global Interrupt Status flag when execution of the block
|
||
240 | completes.
|
||
241 | */
|
||
242 | #if defined(__DOXYGEN__)
|
||
243 | #define ATOMIC_RESTORESTATE
|
||
244 | #else
|
||
245 | #define ATOMIC_RESTORESTATE uint8_t sreg_save \
|
||
246 | __attribute__((__cleanup__(__iRestore))) = SREG |
||
247 | #endif /* __DOXYGEN__ */ |
||
248 | |||
249 | /** \def ATOMIC_FORCEON
|
||
250 | \ingroup util_atomic
|
||
251 | |||
252 | This is a possible parameter for ATOMIC_BLOCK. When used, it will
|
||
253 | cause the ATOMIC_BLOCK to force the state of the SREG register on
|
||
254 | exit, enabling the Global Interrupt Status flag bit. This saves on
|
||
255 | flash space as the previous value of the SREG register does not
|
||
256 | need to be saved at the start of the block.
|
||
257 | |||
258 | Care should be taken that ATOMIC_FORCEON is only used when it is
|
||
259 | known that interrupts are enabled before the block's execution or
|
||
260 | when the side effects of enabling global interrupts at the block's
|
||
261 | completion are known and understood.
|
||
262 | */
|
||
263 | #if defined(__DOXYGEN__)
|
||
264 | #define ATOMIC_FORCEON
|
||
265 | #else
|
||
266 | #define ATOMIC_FORCEON uint8_t sreg_save \
|
||
267 | __attribute__((__cleanup__(__iSeiParam))) = 0
|
||
268 | #endif /* __DOXYGEN__ */ |
||
269 | |||
270 | /** \def NONATOMIC_RESTORESTATE
|
||
271 | \ingroup util_atomic
|
||
272 | |||
273 | This is a possible parameter for NONATOMIC_BLOCK. When used, it
|
||
274 | will cause the NONATOMIC_BLOCK to restore the previous state of
|
||
275 | the SREG register, saved before the Global Interrupt Status flag
|
||
276 | bit was enabled. The net effect of this is to make the
|
||
277 | NONATOMIC_BLOCK's contents guaranteed non-atomic, without changing
|
||
278 | the state of the Global Interrupt Status flag when execution of
|
||
279 | the block completes.
|
||
280 | */
|
||
281 | #if defined(__DOXYGEN__)
|
||
282 | #define NONATOMIC_RESTORESTATE
|
||
283 | #else
|
||
284 | #define NONATOMIC_RESTORESTATE uint8_t sreg_save \
|
||
285 | __attribute__((__cleanup__(__iRestore))) = SREG |
||
286 | #endif /* __DOXYGEN__ */ |
||
287 | |||
288 | /** \def NONATOMIC_FORCEOFF
|
||
289 | \ingroup util_atomic
|
||
290 | |||
291 | This is a possible parameter for NONATOMIC_BLOCK. When used, it
|
||
292 | will cause the NONATOMIC_BLOCK to force the state of the SREG
|
||
293 | register on exit, disabling the Global Interrupt Status flag
|
||
294 | bit. This saves on flash space as the previous value of the SREG
|
||
295 | register does not need to be saved at the start of the block.
|
||
296 | |||
297 | Care should be taken that NONATOMIC_FORCEOFF is only used when it
|
||
298 | is known that interrupts are disabled before the block's execution
|
||
299 | or when the side effects of disabling global interrupts at the
|
||
300 | block's completion are known and understood.
|
||
301 | */
|
||
302 | #if defined(__DOXYGEN__)
|
||
303 | #define NONATOMIC_FORCEOFF
|
||
304 | #else
|
||
305 | #define NONATOMIC_FORCEOFF uint8_t sreg_save \
|
||
306 | __attribute__((__cleanup__(__iCliParam))) = 0
|
||
307 | #endif /* __DOXYGEN__ */ |
||
308 | |||
309 | #endif |