AVR Libc Home Page AVRs AVR Libc Development Pages
Main Page User Manual Library Reference FAQ Alphabetical Index Example Projects

atomic.h

Go to the documentation of this file.
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

Automatically generated by Doxygen 1.7.3 on Thu May 19 2011.