1
/* Copyright (c) 2007 Dean Camera
4
Redistribution and use in source and binary forms, with or without
5
modification, are permitted provided that the following conditions are met:
7
* Redistributions of source code must retain the above copyright
8
notice, this list of conditions and the following disclaimer.
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
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.
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.
32
/* $Id: atomic.h,v 1.3 2007/12/20 14:17:56 joerg_wunsch Exp $ */
34
#ifndef _UTIL_ATOMIC_H_
35
#define _UTIL_ATOMIC_H_ 1
38
#include <avr/interrupt.h>
40
#if !defined(__DOXYGEN__)
41
/* Internal helper functions. */
42
static __inline__ uint8_t __iSeiRetVal(void)
48
static __inline__ uint8_t __iCliRetVal(void)
54
static __inline__ void __iSeiParam(const uint8_t *__s)
57
__asm__ volatile ("" ::: "memory");
61
static __inline__ void __iCliParam(const uint8_t *__s)
64
__asm__ volatile ("" ::: "memory");
68
static __inline__ void __iRestore(const uint8_t *__s)
71
__asm__ volatile ("" ::: "memory");
73
#endif /* !__DOXYGEN__ */
76
/** \defgroup util_atomic <util/atomic.h> Atomically and Non-Atomically Executed Code Blocks
79
#include <util/atomic.h>
82
\note The macros in this header file require the ISO/IEC 9899:1999
83
("ISO C99") feature of for loop variables that are declared inside
84
the for loop itself. For that reason, this header file can only
85
be used if the standard level of the compiler (option --std=) is
86
set to either \c c99 or \c gnu99.
88
The macros in this header file deal with code blocks that are
89
guaranteed to be excuted Atomically or Non-Atmomically. The term
90
"Atomic" in this context refers to the unability of the respective
91
code to be interrupted.
93
These macros operate via automatic manipulation of the Global
94
Interrupt Status (I) bit of the SREG register. Exit paths from
95
both block types are all managed automatically without the need
96
for special considerations, i. e. the interrupt status will be
97
restored to the same value it has been when entering the
100
A typical example that requires atomic access is a 16 (or more)
101
bit variable that is shared between the main execution path and an
102
ISR. While declaring such a variable as volatile ensures that the
103
compiler will not optimize accesses to it away, it does not
104
guarantee atomic access to it. Assuming the following example:
107
#include <inttypes.h>
108
#include <avr/interrupt.h>
111
volatile uint16_t ctr;
132
There is a chance where the main context will exit its wait loop
133
when the variable \c ctr just reached the value 0xFF. This happens
134
because the compiler cannot natively access a 16-bit variable
135
atomically in an 8-bit CPU. So the variable is for example at
136
0x100, the compiler then tests the low byte for 0, which succeeds.
137
It then proceeds to test the high byte, but that moment the ISR
138
triggers, and the main context is interrupted. The ISR will
139
decrement the variable from 0x100 to 0xFF, and the main context
140
proceeds. It now tests the high byte of the variable which is
141
(now) also 0, so it concludes the variable has reached 0, and
144
Using the macros from this header file, the above code can be
148
#include <inttypes.h>
149
#include <avr/interrupt.h>
151
#include <util/atomic.h>
153
volatile uint16_t ctr;
171
ATOMIC_BLOCK(ATOMIC_FORCEON)
176
while (ctr_copy != 0);
181
This will install the appropriate interrupt protection before
182
accessing variable \c ctr, so it is guaranteed to be consistently
183
tested. If the global interrupt state were uncertain before
184
entering the ATOMIC_BLOCK, it should be executed with the
185
parameter ATOMIC_RESTORESTATE rather than ATOMIC_FORCEON.
189
/** \def ATOMIC_BLOCK(type)
192
Creates a block of code that is guaranteed to be executed
193
atomically. Upon entering the block the Global Interrupt Status
194
flag in SREG is disabled, and re-enabled upon exiting the block
197
Two possible macro parameters are permitted, ATOMIC_RESTORESTATE
200
#if defined(__DOXYGEN__)
201
#define ATOMIC_BLOCK(type)
203
#define ATOMIC_BLOCK(type) for ( type, __ToDo = __iCliRetVal(); \
204
__ToDo ; __ToDo = 0 )
205
#endif /* __DOXYGEN__ */
207
/** \def NONATOMIC_BLOCK(type)
210
Creates a block of code that is executed non-atomically. Upon
211
entering the block the Global Interrupt Status flag in SREG is
212
enabled, and disabled upon exiting the block from any exit
213
path. This is useful when nested inside ATOMIC_BLOCK sections,
214
allowing for non-atomic execution of small blocks of code while
215
maintaining the atomic access of the other sections of the parent
218
Two possible macro parameters are permitted,
219
NONATOMIC_RESTORESTATE and NONATOMIC_FORCEOFF.
221
#if defined(__DOXYGEN__)
222
#define NONATOMIC_BLOCK(type)
224
#define NONATOMIC_BLOCK(type) for ( type, __ToDo = __iSeiRetVal(); \
225
__ToDo ; __ToDo = 0 )
226
#endif /* __DOXYGEN__ */
228
/** \def ATOMIC_RESTORESTATE
231
This is a possible parameter for ATOMIC_BLOCK. When used, it will
232
cause the ATOMIC_BLOCK to restore the previous state of the SREG
233
register, saved before the Global Interrupt Status flag bit was
234
disabled. The net effect of this is to make the ATOMIC_BLOCK's
235
contents guaranteed atomic, without changing the state of the
236
Global Interrupt Status flag when execution of the block
239
#if defined(__DOXYGEN__)
240
#define ATOMIC_RESTORESTATE
242
#define ATOMIC_RESTORESTATE uint8_t sreg_save \
243
__attribute__((__cleanup__(__iRestore))) = SREG
244
#endif /* __DOXYGEN__ */
246
/** \def ATOMIC_FORCEON
249
This is a possible parameter for ATOMIC_BLOCK. When used, it will
250
cause the ATOMIC_BLOCK to force the state of the SREG register on
251
exit, enabling the Global Interrupt Status flag bit. This saves on
252
flash space as the previous value of the SREG register does not
253
need to be saved at the start of the block.
255
Care should be taken that ATOMIC_FORCEON is only used when it is
256
known that interrupts are enabled before the block's execution or
257
when the side effects of enabling global interrupts at the block's
258
completion are known and understood.
260
#if defined(__DOXYGEN__)
261
#define ATOMIC_FORCEON
263
#define ATOMIC_FORCEON uint8_t sreg_save \
264
__attribute__((__cleanup__(__iSeiParam))) = 0
265
#endif /* __DOXYGEN__ */
267
/** \def NONATOMIC_RESTORESTATE
270
This is a possible parameter for NONATOMIC_BLOCK. When used, it
271
will cause the NONATOMIC_BLOCK to restore the previous state of
272
the SREG register, saved before the Global Interrupt Status flag
273
bit was enabled. The net effect of this is to make the
274
NONATOMIC_BLOCK's contents guaranteed non-atomic, without changing
275
the state of the Global Interrupt Status flag when execution of
278
#if defined(__DOXYGEN__)
279
#define NONATOMIC_RESTORESTATE
281
#define NONATOMIC_RESTORESTATE uint8_t sreg_save \
282
__attribute__((__cleanup__(__iRestore))) = SREG
283
#endif /* __DOXYGEN__ */
285
/** \def NONATOMIC_FORCEOFF
288
This is a possible parameter for NONATOMIC_BLOCK. When used, it
289
will cause the NONATOMIC_BLOCK to force the state of the SREG
290
register on exit, disabling the Global Interrupt Status flag
291
bit. This saves on flash space as the previous value of the SREG
292
register does not need to be saved at the start of the block.
294
Care should be taken that NONATOMIC_FORCEOFF is only used when it
295
is known that interrupts are disabled before the block's execution
296
or when the side effects of disabling global interrupts at the
297
block's completion are known and understood.
299
#if defined(__DOXYGEN__)
300
#define NONATOMIC_FORCEOFF
302
#define NONATOMIC_FORCEOFF uint8_t sreg_save \
303
__attribute__((__cleanup__(__iCliParam))) = 0
304
#endif /* __DOXYGEN__ */