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