1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-03-14 WangShun first version
9 * 2023-05-20 Bernard add stdc atomic detection.
10 */
11 #ifndef __RT_ATOMIC_H__
12 #define __RT_ATOMIC_H__
13
14 #include <rthw.h>
15
16 #if !defined(__cplusplus)
17
18 rt_atomic_t rt_hw_atomic_load(volatile rt_atomic_t *ptr);
19 void rt_hw_atomic_store(volatile rt_atomic_t *ptr, rt_atomic_t val);
20 rt_atomic_t rt_hw_atomic_add(volatile rt_atomic_t *ptr, rt_atomic_t val);
21 rt_atomic_t rt_hw_atomic_sub(volatile rt_atomic_t *ptr, rt_atomic_t val);
22 rt_atomic_t rt_hw_atomic_and(volatile rt_atomic_t *ptr, rt_atomic_t val);
23 rt_atomic_t rt_hw_atomic_or(volatile rt_atomic_t *ptr, rt_atomic_t val);
24 rt_atomic_t rt_hw_atomic_xor(volatile rt_atomic_t *ptr, rt_atomic_t val);
25 rt_atomic_t rt_hw_atomic_exchange(volatile rt_atomic_t *ptr, rt_atomic_t val);
26 void rt_hw_atomic_flag_clear(volatile rt_atomic_t *ptr);
27 rt_atomic_t rt_hw_atomic_flag_test_and_set(volatile rt_atomic_t *ptr);
28 rt_atomic_t rt_hw_atomic_compare_exchange_strong(volatile rt_atomic_t *ptr, rt_atomic_t *expected, rt_atomic_t desired);
29
30 #if defined(RT_USING_STDC_ATOMIC)
31
32 #ifndef __STDC_NO_ATOMICS__
33 #define rt_atomic_load(ptr) atomic_load(ptr)
34 #define rt_atomic_store(ptr, v) atomic_store(ptr, v)
35 #define rt_atomic_add(ptr, v) atomic_fetch_add(ptr, v)
36 #define rt_atomic_sub(ptr, v) atomic_fetch_sub(ptr, v)
37 #define rt_atomic_and(ptr, v) atomic_fetch_and(ptr, v)
38 #define rt_atomic_or(ptr, v) atomic_fetch_or(ptr, v)
39 #define rt_atomic_xor(ptr, v) atomic_fetch_xor(ptr, v)
40 #define rt_atomic_exchange(ptr, v) atomic_exchange(ptr, v)
41 #define rt_atomic_flag_clear(ptr) atomic_flag_clear(ptr)
42 #define rt_atomic_flag_test_and_set(ptr) atomic_flag_test_and_set(ptr)
43 #define rt_atomic_compare_exchange_strong(ptr, v,des) atomic_compare_exchange_strong(ptr, v ,des)
44 #else
45 #error "The standard library C doesn't support the atomic operation"
46 #endif /* __STDC_NO_ATOMICS__ */
47
48 #elif defined(RT_USING_HW_ATOMIC)
49 #define rt_atomic_load(ptr) rt_hw_atomic_load(ptr)
50 #define rt_atomic_store(ptr, v) rt_hw_atomic_store(ptr, v)
51 #define rt_atomic_add(ptr, v) rt_hw_atomic_add(ptr, v)
52 #define rt_atomic_sub(ptr, v) rt_hw_atomic_sub(ptr, v)
53 #define rt_atomic_and(ptr, v) rt_hw_atomic_and(ptr, v)
54 #define rt_atomic_or(ptr, v) rt_hw_atomic_or(ptr, v)
55 #define rt_atomic_xor(ptr, v) rt_hw_atomic_xor(ptr, v)
56 #define rt_atomic_exchange(ptr, v) rt_hw_atomic_exchange(ptr, v)
57 #define rt_atomic_flag_clear(ptr) rt_hw_atomic_flag_clear(ptr)
58 #define rt_atomic_flag_test_and_set(ptr) rt_hw_atomic_flag_test_and_set(ptr)
59 #define rt_atomic_compare_exchange_strong(ptr, v,des) rt_hw_atomic_compare_exchange_strong(ptr, v ,des)
60
61 #else
62 #include <rthw.h>
63 #define rt_atomic_load(ptr) rt_soft_atomic_load(ptr)
64 #define rt_atomic_store(ptr, v) rt_soft_atomic_store(ptr, v)
65 #define rt_atomic_add(ptr, v) rt_soft_atomic_add(ptr, v)
66 #define rt_atomic_sub(ptr, v) rt_soft_atomic_sub(ptr, v)
67 #define rt_atomic_and(ptr, v) rt_soft_atomic_and(ptr, v)
68 #define rt_atomic_or(ptr, v) rt_soft_atomic_or(ptr, v)
69 #define rt_atomic_xor(ptr, v) rt_soft_atomic_xor(ptr, v)
70 #define rt_atomic_exchange(ptr, v) rt_soft_atomic_exchange(ptr, v)
71 #define rt_atomic_flag_clear(ptr) rt_soft_atomic_flag_clear(ptr)
72 #define rt_atomic_flag_test_and_set(ptr) rt_soft_atomic_flag_test_and_set(ptr)
73 #define rt_atomic_compare_exchange_strong(ptr, v,des) rt_soft_atomic_compare_exchange_strong(ptr, v ,des)
74
rt_soft_atomic_exchange(volatile rt_atomic_t * ptr,rt_atomic_t val)75 rt_inline rt_atomic_t rt_soft_atomic_exchange(volatile rt_atomic_t *ptr, rt_atomic_t val)
76 {
77 rt_base_t level;
78 rt_atomic_t temp;
79 level = rt_hw_interrupt_disable();
80 temp = *ptr;
81 *ptr = val;
82 rt_hw_interrupt_enable(level);
83 return temp;
84 }
85
rt_soft_atomic_add(volatile rt_atomic_t * ptr,rt_atomic_t val)86 rt_inline rt_atomic_t rt_soft_atomic_add(volatile rt_atomic_t *ptr, rt_atomic_t val)
87 {
88 rt_base_t level;
89 rt_atomic_t temp;
90 level = rt_hw_interrupt_disable();
91 temp = *ptr;
92 *ptr += val;
93 rt_hw_interrupt_enable(level);
94 return temp;
95 }
96
rt_soft_atomic_sub(volatile rt_atomic_t * ptr,rt_atomic_t val)97 rt_inline rt_atomic_t rt_soft_atomic_sub(volatile rt_atomic_t *ptr, rt_atomic_t val)
98 {
99 rt_base_t level;
100 rt_atomic_t temp;
101 level = rt_hw_interrupt_disable();
102 temp = *ptr;
103 *ptr -= val;
104 rt_hw_interrupt_enable(level);
105 return temp;
106 }
107
rt_soft_atomic_xor(volatile rt_atomic_t * ptr,rt_atomic_t val)108 rt_inline rt_atomic_t rt_soft_atomic_xor(volatile rt_atomic_t *ptr, rt_atomic_t val)
109 {
110 rt_base_t level;
111 rt_atomic_t temp;
112 level = rt_hw_interrupt_disable();
113 temp = *ptr;
114 *ptr = (*ptr) ^ val;
115 rt_hw_interrupt_enable(level);
116 return temp;
117 }
118
rt_soft_atomic_and(volatile rt_atomic_t * ptr,rt_atomic_t val)119 rt_inline rt_atomic_t rt_soft_atomic_and(volatile rt_atomic_t *ptr, rt_atomic_t val)
120 {
121 rt_base_t level;
122 rt_atomic_t temp;
123 level = rt_hw_interrupt_disable();
124 temp = *ptr;
125 *ptr = (*ptr) & val;
126 rt_hw_interrupt_enable(level);
127 return temp;
128 }
129
rt_soft_atomic_or(volatile rt_atomic_t * ptr,rt_atomic_t val)130 rt_inline rt_atomic_t rt_soft_atomic_or(volatile rt_atomic_t *ptr, rt_atomic_t val)
131 {
132 rt_base_t level;
133 rt_atomic_t temp;
134 level = rt_hw_interrupt_disable();
135 temp = *ptr;
136 *ptr = (*ptr) | val;
137 rt_hw_interrupt_enable(level);
138 return temp;
139 }
140
rt_soft_atomic_load(volatile rt_atomic_t * ptr)141 rt_inline rt_atomic_t rt_soft_atomic_load(volatile rt_atomic_t *ptr)
142 {
143 rt_base_t level;
144 rt_atomic_t temp;
145 level = rt_hw_interrupt_disable();
146 temp = *ptr;
147 rt_hw_interrupt_enable(level);
148 return temp;
149 }
150
rt_soft_atomic_store(volatile rt_atomic_t * ptr,rt_atomic_t val)151 rt_inline void rt_soft_atomic_store(volatile rt_atomic_t *ptr, rt_atomic_t val)
152 {
153 rt_base_t level;
154 level = rt_hw_interrupt_disable();
155 *ptr = val;
156 rt_hw_interrupt_enable(level);
157 }
158
rt_soft_atomic_flag_test_and_set(volatile rt_atomic_t * ptr)159 rt_inline rt_atomic_t rt_soft_atomic_flag_test_and_set(volatile rt_atomic_t *ptr)
160 {
161 rt_base_t level;
162 rt_atomic_t temp;
163 level = rt_hw_interrupt_disable();
164 if (*ptr == 0)
165 {
166 temp = 0;
167 *ptr = 1;
168 }
169 else
170 temp = 1;
171 rt_hw_interrupt_enable(level);
172 return temp;
173 }
174
rt_soft_atomic_flag_clear(volatile rt_atomic_t * ptr)175 rt_inline void rt_soft_atomic_flag_clear(volatile rt_atomic_t *ptr)
176 {
177 rt_base_t level;
178 level = rt_hw_interrupt_disable();
179 *ptr = 0;
180 rt_hw_interrupt_enable(level);
181 }
182
rt_soft_atomic_compare_exchange_strong(volatile rt_atomic_t * ptr1,rt_atomic_t * ptr2,rt_atomic_t desired)183 rt_inline rt_atomic_t rt_soft_atomic_compare_exchange_strong(volatile rt_atomic_t *ptr1, rt_atomic_t *ptr2,
184 rt_atomic_t desired)
185 {
186 rt_base_t level;
187 rt_atomic_t temp;
188 level = rt_hw_interrupt_disable();
189 if ((*ptr1) != (*ptr2))
190 {
191 *ptr2 = *ptr1;
192 temp = 0;
193 }
194 else
195 {
196 *ptr1 = desired;
197 temp = 1;
198 }
199 rt_hw_interrupt_enable(level);
200 return temp;
201 }
202 #endif /* RT_USING_STDC_ATOMIC */
203
rt_atomic_dec_and_test(volatile rt_atomic_t * ptr)204 rt_inline rt_bool_t rt_atomic_dec_and_test(volatile rt_atomic_t *ptr)
205 {
206 return rt_atomic_sub(ptr, 1) == 1;
207 }
208
rt_atomic_fetch_add_unless(volatile rt_atomic_t * ptr,rt_atomic_t a,rt_atomic_t u)209 rt_inline rt_atomic_t rt_atomic_fetch_add_unless(volatile rt_atomic_t *ptr, rt_atomic_t a, rt_atomic_t u)
210 {
211 rt_atomic_t c = rt_atomic_load(ptr);
212
213 do {
214 if (c == u)
215 {
216 break;
217 }
218 } while (!rt_atomic_compare_exchange_strong(ptr, &c, c + a));
219
220 return c;
221 }
222
rt_atomic_add_unless(volatile rt_atomic_t * ptr,rt_atomic_t a,rt_atomic_t u)223 rt_inline rt_bool_t rt_atomic_add_unless(volatile rt_atomic_t *ptr, rt_atomic_t a, rt_atomic_t u)
224 {
225 return rt_atomic_fetch_add_unless(ptr, a, u) != u;
226 }
227
rt_atomic_inc_not_zero(volatile rt_atomic_t * ptr)228 rt_inline rt_bool_t rt_atomic_inc_not_zero(volatile rt_atomic_t *ptr)
229 {
230 return rt_atomic_add_unless(ptr, 1, 0);
231 }
232
233 /**
234 * @brief initialize a lock-less single list
235 *
236 * @param l the single list to be initialized
237 */
rt_ll_slist_init(rt_ll_slist_t * l)238 rt_inline void rt_ll_slist_init(rt_ll_slist_t *l)
239 {
240 l->next = 0;
241 }
242
rt_ll_slist_enqueue(rt_ll_slist_t * l,rt_ll_slist_t * n)243 rt_inline void rt_ll_slist_enqueue(rt_ll_slist_t *l, rt_ll_slist_t *n)
244 {
245 rt_base_t exp;
246 exp = rt_atomic_load(&l->next);
247 do
248 {
249 n->next = exp;
250 } while (!rt_atomic_compare_exchange_strong(&l->next, &exp, (rt_base_t)n));
251 }
252
rt_ll_slist_dequeue(rt_ll_slist_t * l)253 rt_inline rt_ll_slist_t *rt_ll_slist_dequeue(rt_ll_slist_t *l)
254 {
255 rt_base_t exp;
256 rt_ll_slist_t *head;
257
258 exp = rt_atomic_load(&l->next);
259 do
260 {
261 head = (rt_ll_slist_t *)exp;
262 } while (head && !rt_atomic_compare_exchange_strong(&l->next, &exp, rt_atomic_load(&head->next)));
263 return head;
264 }
265
266 #endif /* __cplusplus */
267
268 #endif /* __RT_ATOMIC_H__ */
269