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