AVR Libc Home Page | AVR Libc Development Pages | ||||
Main Page | User Manual | Library Reference | FAQ | Alphabetical Index | Example Projects |
00001 /* Copyright (c) 2007 Dean Camera 00002 All rights reserved. 00003 00004 Redistribution and use in source and binary forms, with or without 00005 modification, are permitted provided that the following conditions are met: 00006 00007 * Redistributions of source code must retain the above copyright 00008 notice, this list of conditions and the following disclaimer. 00009 00010 * Redistributions in binary form must reproduce the above copyright 00011 notice, this list of conditions and the following disclaimer in 00012 the documentation and/or other materials provided with the 00013 distribution. 00014 00015 * Neither the name of the copyright holders nor the names of 00016 contributors may be used to endorse or promote products derived 00017 from this software without specific prior written permission. 00018 00019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00020 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00021 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00022 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00023 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00024 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00025 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00026 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00027 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00028 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00029 POSSIBILITY OF SUCH DAMAGE. 00030 */ 00031 00032 /* $Id: atomic.h 2158 2010-06-10 15:48:28Z joerg_wunsch $ */ 00033 00034 #ifndef _UTIL_ATOMIC_H_ 00035 #define _UTIL_ATOMIC_H_ 1 00036 00037 #include <avr/io.h> 00038 #include <avr/interrupt.h> 00039 00040 #if !defined(__DOXYGEN__) 00041 /* Internal helper functions. */ 00042 static __inline__ uint8_t __iSeiRetVal(void) 00043 { 00044 sei(); 00045 return 1; 00046 } 00047 00048 static __inline__ uint8_t __iCliRetVal(void) 00049 { 00050 cli(); 00051 return 1; 00052 } 00053 00054 static __inline__ void __iSeiParam(const uint8_t *__s) 00055 { 00056 sei(); 00057 __asm__ volatile ("" ::: "memory"); 00058 (void)__s; 00059 } 00060 00061 static __inline__ void __iCliParam(const uint8_t *__s) 00062 { 00063 cli(); 00064 __asm__ volatile ("" ::: "memory"); 00065 (void)__s; 00066 } 00067 00068 static __inline__ void __iRestore(const uint8_t *__s) 00069 { 00070 SREG = *__s; 00071 __asm__ volatile ("" ::: "memory"); 00072 } 00073 #endif /* !__DOXYGEN__ */ 00074 00075 /** \file */ 00076 /** \defgroup util_atomic <util/atomic.h> Atomically and Non-Atomically Executed Code Blocks 00077 00078 \code 00079 #include <util/atomic.h> 00080 \endcode 00081 00082 \note The macros in this header file require the ISO/IEC 9899:1999 00083 ("ISO C99") feature of for loop variables that are declared inside 00084 the for loop itself. For that reason, this header file can only 00085 be used if the standard level of the compiler (option --std=) is 00086 set to either \c c99 or \c gnu99. 00087 00088 The macros in this header file deal with code blocks that are 00089 guaranteed to be excuted Atomically or Non-Atmomically. The term 00090 "Atomic" in this context refers to the unability of the respective 00091 code to be interrupted. 00092 00093 These macros operate via automatic manipulation of the Global 00094 Interrupt Status (I) bit of the SREG register. Exit paths from 00095 both block types are all managed automatically without the need 00096 for special considerations, i. e. the interrupt status will be 00097 restored to the same value it has been when entering the 00098 respective block. 00099 00100 A typical example that requires atomic access is a 16 (or more) 00101 bit variable that is shared between the main execution path and an 00102 ISR. While declaring such a variable as volatile ensures that the 00103 compiler will not optimize accesses to it away, it does not 00104 guarantee atomic access to it. Assuming the following example: 00105 00106 \code 00107 #include <inttypes.h> 00108 #include <avr/interrupt.h> 00109 #include <avr/io.h> 00110 00111 volatile uint16_t ctr; 00112 00113 ISR(TIMER1_OVF_vect) 00114 { 00115 ctr--; 00116 } 00117 00118 ... 00119 int 00120 main(void) 00121 { 00122 ... 00123 ctr = 0x200; 00124 start_timer(); 00125 while (ctr != 0) 00126 // wait 00127 ; 00128 ... 00129 } 00130 \endcode 00131 00132 There is a chance where the main context will exit its wait loop 00133 when the variable \c ctr just reached the value 0xFF. This happens 00134 because the compiler cannot natively access a 16-bit variable 00135 atomically in an 8-bit CPU. So the variable is for example at 00136 0x100, the compiler then tests the low byte for 0, which succeeds. 00137 It then proceeds to test the high byte, but that moment the ISR 00138 triggers, and the main context is interrupted. The ISR will 00139 decrement the variable from 0x100 to 0xFF, and the main context 00140 proceeds. It now tests the high byte of the variable which is 00141 (now) also 0, so it concludes the variable has reached 0, and 00142 terminates the loop. 00143 00144 Using the macros from this header file, the above code can be 00145 rewritten like: 00146 00147 \code 00148 #include <inttypes.h> 00149 #include <avr/interrupt.h> 00150 #include <avr/io.h> 00151 #include <util/atomic.h> 00152 00153 volatile uint16_t ctr; 00154 00155 ISR(TIMER1_OVF_vect) 00156 { 00157 ctr--; 00158 } 00159 00160 ... 00161 int 00162 main(void) 00163 { 00164 ... 00165 ctr = 0x200; 00166 start_timer(); 00167 sei(); 00168 uint16_t ctr_copy; 00169 do 00170 { 00171 ATOMIC_BLOCK(ATOMIC_FORCEON) 00172 { 00173 ctr_copy = ctr; 00174 } 00175 } 00176 while (ctr_copy != 0); 00177 ... 00178 } 00179 \endcode 00180 00181 This will install the appropriate interrupt protection before 00182 accessing variable \c ctr, so it is guaranteed to be consistently 00183 tested. If the global interrupt state were uncertain before 00184 entering the ATOMIC_BLOCK, it should be executed with the 00185 parameter ATOMIC_RESTORESTATE rather than ATOMIC_FORCEON. 00186 00187 See \ref optim_code_reorder for things to be taken into account 00188 with respect to compiler optimizations. 00189 */ 00190 00191 /** \def ATOMIC_BLOCK(type) 00192 \ingroup util_atomic 00193 00194 Creates a block of code that is guaranteed to be executed 00195 atomically. Upon entering the block the Global Interrupt Status 00196 flag in SREG is disabled, and re-enabled upon exiting the block 00197 from any exit path. 00198 00199 Two possible macro parameters are permitted, ATOMIC_RESTORESTATE 00200 and ATOMIC_FORCEON. 00201 */ 00202 #if defined(__DOXYGEN__) 00203 #define ATOMIC_BLOCK(type) 00204 #else 00205 #define ATOMIC_BLOCK(type) for ( type, __ToDo = __iCliRetVal(); \ 00206 __ToDo ; __ToDo = 0 ) 00207 #endif /* __DOXYGEN__ */ 00208 00209 /** \def NONATOMIC_BLOCK(type) 00210 \ingroup util_atomic 00211 00212 Creates a block of code that is executed non-atomically. Upon 00213 entering the block the Global Interrupt Status flag in SREG is 00214 enabled, and disabled upon exiting the block from any exit 00215 path. This is useful when nested inside ATOMIC_BLOCK sections, 00216 allowing for non-atomic execution of small blocks of code while 00217 maintaining the atomic access of the other sections of the parent 00218 ATOMIC_BLOCK. 00219 00220 Two possible macro parameters are permitted, 00221 NONATOMIC_RESTORESTATE and NONATOMIC_FORCEOFF. 00222 */ 00223 #if defined(__DOXYGEN__) 00224 #define NONATOMIC_BLOCK(type) 00225 #else 00226 #define NONATOMIC_BLOCK(type) for ( type, __ToDo = __iSeiRetVal(); \ 00227 __ToDo ; __ToDo = 0 ) 00228 #endif /* __DOXYGEN__ */ 00229 00230 /** \def ATOMIC_RESTORESTATE 00231 \ingroup util_atomic 00232 00233 This is a possible parameter for ATOMIC_BLOCK. When used, it will 00234 cause the ATOMIC_BLOCK to restore the previous state of the SREG 00235 register, saved before the Global Interrupt Status flag bit was 00236 disabled. The net effect of this is to make the ATOMIC_BLOCK's 00237 contents guaranteed atomic, without changing the state of the 00238 Global Interrupt Status flag when execution of the block 00239 completes. 00240 */ 00241 #if defined(__DOXYGEN__) 00242 #define ATOMIC_RESTORESTATE 00243 #else 00244 #define ATOMIC_RESTORESTATE uint8_t sreg_save \ 00245 __attribute__((__cleanup__(__iRestore))) = SREG 00246 #endif /* __DOXYGEN__ */ 00247 00248 /** \def ATOMIC_FORCEON 00249 \ingroup util_atomic 00250 00251 This is a possible parameter for ATOMIC_BLOCK. When used, it will 00252 cause the ATOMIC_BLOCK to force the state of the SREG register on 00253 exit, enabling the Global Interrupt Status flag bit. This saves on 00254 flash space as the previous value of the SREG register does not 00255 need to be saved at the start of the block. 00256 00257 Care should be taken that ATOMIC_FORCEON is only used when it is 00258 known that interrupts are enabled before the block's execution or 00259 when the side effects of enabling global interrupts at the block's 00260 completion are known and understood. 00261 */ 00262 #if defined(__DOXYGEN__) 00263 #define ATOMIC_FORCEON 00264 #else 00265 #define ATOMIC_FORCEON uint8_t sreg_save \ 00266 __attribute__((__cleanup__(__iSeiParam))) = 0 00267 #endif /* __DOXYGEN__ */ 00268 00269 /** \def NONATOMIC_RESTORESTATE 00270 \ingroup util_atomic 00271 00272 This is a possible parameter for NONATOMIC_BLOCK. When used, it 00273 will cause the NONATOMIC_BLOCK to restore the previous state of 00274 the SREG register, saved before the Global Interrupt Status flag 00275 bit was enabled. The net effect of this is to make the 00276 NONATOMIC_BLOCK's contents guaranteed non-atomic, without changing 00277 the state of the Global Interrupt Status flag when execution of 00278 the block completes. 00279 */ 00280 #if defined(__DOXYGEN__) 00281 #define NONATOMIC_RESTORESTATE 00282 #else 00283 #define NONATOMIC_RESTORESTATE uint8_t sreg_save \ 00284 __attribute__((__cleanup__(__iRestore))) = SREG 00285 #endif /* __DOXYGEN__ */ 00286 00287 /** \def NONATOMIC_FORCEOFF 00288 \ingroup util_atomic 00289 00290 This is a possible parameter for NONATOMIC_BLOCK. When used, it 00291 will cause the NONATOMIC_BLOCK to force the state of the SREG 00292 register on exit, disabling the Global Interrupt Status flag 00293 bit. This saves on flash space as the previous value of the SREG 00294 register does not need to be saved at the start of the block. 00295 00296 Care should be taken that NONATOMIC_FORCEOFF is only used when it 00297 is known that interrupts are disabled before the block's execution 00298 or when the side effects of disabling global interrupts at the 00299 block's completion are known and understood. 00300 */ 00301 #if defined(__DOXYGEN__) 00302 #define NONATOMIC_FORCEOFF 00303 #else 00304 #define NONATOMIC_FORCEOFF uint8_t sreg_save \ 00305 __attribute__((__cleanup__(__iCliParam))) = 0 00306 #endif /* __DOXYGEN__ */ 00307 00308 #endif