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

atomic.8h

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

io.h

cli #define cli()

Definition: interrupt.h:97

uint8_t unsigned char uint8_t

Definition: stdint.h:83

interrupt.h