avr-libc  2.0.0
Standard C library for AVR-GCC

AVR Libc Home Page

AVRs

AVR Libc Development Pages

Main Page

User Manual

Library Reference

FAQ

Example Projects

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