AVR-LibC  2.3.0git
Standard C library for AVR-GCC
 

AVR-LibC Documen­tation

AVR-LibC Development Pages

Main Page

User Manual

Library Refe­rence

FAQ

Example Projects

File List

Index

Loading...
Searching...
No Matches
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#ifndef _UTIL_ATOMIC_H_
32#define _UTIL_ATOMIC_H_ 1
33
34#include <avr/io.h>
35#include <avr/interrupt.h>
36
37#if !defined(__DOXYGEN__)
38/* Internal helper functions. */
39static __inline__ uint8_t __iSeiRetVal(void)
40{
41 sei();
42 return 1;
43}
44
45static __inline__ uint8_t __iCliRetVal(void)
46{
47 cli();
48 return 1;
49}
50
51static __inline__ void __iSeiParam(const uint8_t *__s)
52{
53 sei();
54 __asm__ volatile ("" ::: "memory");
55 (void)__s;
56}
57
58static __inline__ void __iCliParam(const uint8_t *__s)
59{
60 cli();
61 __asm__ volatile ("" ::: "memory");
62 (void)__s;
63}
64
65static __inline__ void __iRestore(const uint8_t *__s)
66{
67 SREG = *__s;
68 __asm__ volatile ("" ::: "memory");
69}
70#endif /* !__DOXYGEN__ */
71
72/** \file */
73/** \defgroup util_atomic <util/atomic.h> Atomically and Non-Atomically Executed Code Blocks
74
75 \code
76 #include <util/atomic.h>
77 \endcode
78
79 \note The macros in this header file require the ISO/IEC 9899:1999
80 ("ISO C99") feature of for loop variables that are declared inside
81 the for loop itself. For that reason, this header file can only
82 be used if the standard level of the compiler (option --std=) is
83 set to either \c c99, \c gnu99 or higher.
84
85 The macros in this header file deal with code blocks that are
86 guaranteed to be executed Atomically or Non-Atomically. The term
87 "Atomic" in this context refers to the inability of the respective
88 code to be interrupted.
89
90 These macros operate via automatic manipulation of the Global
91 Interrupt Status (I) bit of the SREG register. Exit paths from
92 both block types are all managed automatically without the need
93 for special considerations, i.e. the interrupt status will be
94 restored to the same value it had when entering the respective
95 block (unless ATOMIC_FORCEON or NONATOMIC_FORCEOFF are used).
96 \warning The features in this header are implemented by means of
97 a for loop. This means that commands like \c break and \c continue
98 that are located in an atomic block refer to the atomic for loop,
99 not to a loop construct that hosts the atomic block.
100
101 A typical example that requires atomic access is a 16 (or more)
102 bit variable that is shared between the main execution path and an
103 ISR. While declaring such a variable as volatile ensures that the
104 compiler will not optimize accesses to it away, it does not
105 guarantee atomic access to it. Assuming the following example:
106
107 \code
108#include <stdint.h>
109#include <avr/interrupt.h>
110#include <avr/io.h>
111
112volatile uint16_t ctr;
113
114ISR(TIMER1_OVF_vect)
115{
116 ctr--;
117}
118
119...
120int
121main(void)
122{
123 ...
124 ctr = 0x200;
125 start_timer();
126 while (ctr != 0)
127 // wait
128 ;
129 ...
130}
131 \endcode
132
133 There is a chance where the main context will exit its wait loop
134 when the variable \c ctr just reached the value 0xFF. This happens
135 because the compiler cannot natively access a 16-bit variable
136 atomically in an 8-bit CPU. So the variable is for example at
137 0x100, the compiler then tests the low byte for 0, which succeeds.
138 It then proceeds to test the high byte, but that moment the ISR
139 triggers, and the main context is interrupted. The ISR will
140 decrement the variable from 0x100 to 0xFF, and the main context
141 proceeds. It now tests the high byte of the variable which is
142 (now) also 0, so it concludes the variable has reached 0, and
143 terminates the loop.
144
145 Using the macros from this header file, the above code can be
146 rewritten like:
147
148 \code
149#include <stdint.h>
150#include <avr/interrupt.h>
151#include <avr/io.h>
152#include <util/atomic.h>
153
154volatile uint16_t ctr;
155
156ISR(TIMER1_OVF_vect)
157{
158 ctr--;
159}
160
161...
162int
163main(void)
164{
165 ...
166 ctr = 0x200;
167 start_timer();
168 sei();
169 uint16_t ctr_copy;
170 do
171 {
172 ATOMIC_BLOCK(ATOMIC_FORCEON)
173 {
174 ctr_copy = ctr;
175 }
176 }
177 while (ctr_copy != 0);
178 ...
179}
180 \endcode
181
182 This will install the appropriate interrupt protection before
183 accessing variable \c ctr, so it is guaranteed to be consistently
184 tested. If the global interrupt state were uncertain before
185 entering the #ATOMIC_BLOCK, it should be executed with the
186 parameter #ATOMIC_RESTORESTATE rather than #ATOMIC_FORCEON.
187
188 See \ref optim_code_reorder for things to be taken into account
189 with respect to compiler optimizations.
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 a
255 small amount of flash space, a register, and one or more processor
256 cycles, since the previous value of the SREG register does not need
257 to be saved at the start of the block.
258
259 Care should be taken that ATOMIC_FORCEON is only used when it is
260 known that interrupts are enabled before the block's execution or
261 when the side effects of enabling global interrupts at the block's
262 completion are known and understood.
263*/
264#if defined(__DOXYGEN__)
265#define ATOMIC_FORCEON
266#else
267#define ATOMIC_FORCEON uint8_t sreg_save \
268 __attribute__((__cleanup__(__iSeiParam))) = 0
269#endif /* __DOXYGEN__ */
270
271/** \def NONATOMIC_RESTORESTATE
272 \ingroup util_atomic
273
274 This is a possible parameter for #NONATOMIC_BLOCK. When used, it
275 will cause the NONATOMIC_BLOCK to restore the previous state of
276 the SREG register, saved before the Global Interrupt Status flag
277 bit was enabled. The net effect of this is to make the
278 NONATOMIC_BLOCK's contents guaranteed non-atomic, without changing
279 the state of the Global Interrupt Status flag when execution of
280 the block completes.
281*/
282#if defined(__DOXYGEN__)
283#define NONATOMIC_RESTORESTATE
284#else
285#define NONATOMIC_RESTORESTATE uint8_t sreg_save \
286 __attribute__((__cleanup__(__iRestore))) = SREG
287#endif /* __DOXYGEN__ */
288
289/** \def NONATOMIC_FORCEOFF
290 \ingroup util_atomic
291
292 This is a possible parameter for #NONATOMIC_BLOCK. When used, it
293 will cause the NONATOMIC_BLOCK to force the state of the SREG
294 register on exit, disabling the Global Interrupt Status flag
295 bit. This saves a small amount of flash space, a register, and one
296 or more processor cycles, since the previous value of the SREG
297 register does not need to be saved at the start of the block.
298
299 Care should be taken that NONATOMIC_FORCEOFF is only used when it
300 is known that interrupts are disabled before the block's execution
301 or when the side effects of disabling global interrupts at the
302 block's completion are known and understood.
303*/
304#if defined(__DOXYGEN__)
305#define NONATOMIC_FORCEOFF
306#else
307#define NONATOMIC_FORCEOFF uint8_t sreg_save \
308 __attribute__((__cleanup__(__iCliParam))) = 0
309#endif /* __DOXYGEN__ */
310
311#endif
#define cli()
Definition: interrupt.h:88
#define sei()
Definition: interrupt.h:74
unsigned char uint8_t
Definition: stdint.h:81