Skip to content

Commit 6759dd1

Browse files
committed
dd mocked util/atomic
1 parent 9c1340a commit 6759dd1

File tree

1 file changed

+312
-0
lines changed

1 file changed

+312
-0
lines changed

cpp/arduino/util/atomic.h

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

0 commit comments

Comments
 (0)