Skip to content

Commit 703e440

Browse files
committed
Add atomic loads and stores and barriers
Add atomic load and store functions, and add barriers to the existing atomic functions. File currently has no explicit barriers - we don't support SMP, so don't need CPU barriers. But we do need to worry about compiler barriers - particularly if link time optimisation is activated so that the compiler can see inside these functions. The assembler or intrinsics that access PRIMASK for enter/exit critical act as barriers, but LDREX, STREX and simple volatile pointer loads and stores do not.
1 parent 46bb754 commit 703e440

File tree

4 files changed

+206
-10
lines changed

4 files changed

+206
-10
lines changed

UNITTESTS/stubs/mbed_critical_stub.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ bool core_util_atomic_flag_test_and_set(volatile core_util_atomic_flag *flagPtr)
5454
return false;
5555
}
5656

57-
void core_util_atomic_flag_clear(volatile core_util_atomic_flag *flagPtr)
58-
{
59-
}
60-
6157
bool core_util_atomic_cas_u8(volatile uint8_t *ptr, uint8_t *expectedCurrentValue, uint8_t desiredValue)
6258
{
6359
return false;

platform/mbed_critical.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,6 @@ void core_util_critical_section_exit(void)
100100
}
101101
}
102102

103-
void core_util_atomic_flag_clear(volatile core_util_atomic_flag *flagPtr)
104-
{
105-
flagPtr->_flag = false;
106-
}
107-
108103
#if MBED_EXCLUSIVE_ACCESS
109104

110105
/* Supress __ldrex and __strex deprecated warnings - "#3731-D: intrinsic is deprecated" */
@@ -115,14 +110,17 @@ void core_util_atomic_flag_clear(volatile core_util_atomic_flag *flagPtr)
115110
bool core_util_atomic_flag_test_and_set(volatile core_util_atomic_flag *flagPtr)
116111
{
117112
uint8_t currentValue;
113+
MBED_BARRIER();
118114
do {
119115
currentValue = __LDREXB(&flagPtr->_flag);
120116
} while (__STREXB(true, &flagPtr->_flag));
117+
MBED_BARRIER();
121118
return currentValue;
122119
}
123120

124121
bool core_util_atomic_cas_u8(volatile uint8_t *ptr, uint8_t *expectedCurrentValue, uint8_t desiredValue)
125122
{
123+
MBED_BARRIER();
126124
do {
127125
uint8_t currentValue = __LDREXB(ptr);
128126
if (currentValue != *expectedCurrentValue) {
@@ -131,11 +129,13 @@ bool core_util_atomic_cas_u8(volatile uint8_t *ptr, uint8_t *expectedCurrentValu
131129
return false;
132130
}
133131
} while (__STREXB(desiredValue, ptr));
132+
MBED_BARRIER();
134133
return true;
135134
}
136135

137136
bool core_util_atomic_cas_u16(volatile uint16_t *ptr, uint16_t *expectedCurrentValue, uint16_t desiredValue)
138137
{
138+
MBED_BARRIER();
139139
do {
140140
uint16_t currentValue = __LDREXH(ptr);
141141
if (currentValue != *expectedCurrentValue) {
@@ -144,12 +144,14 @@ bool core_util_atomic_cas_u16(volatile uint16_t *ptr, uint16_t *expectedCurrentV
144144
return false;
145145
}
146146
} while (__STREXH(desiredValue, ptr));
147+
MBED_BARRIER();
147148
return true;
148149
}
149150

150151

151152
bool core_util_atomic_cas_u32(volatile uint32_t *ptr, uint32_t *expectedCurrentValue, uint32_t desiredValue)
152153
{
154+
MBED_BARRIER();
153155
do {
154156
uint32_t currentValue = __LDREXW(ptr);
155157
if (currentValue != *expectedCurrentValue) {
@@ -158,61 +160,74 @@ bool core_util_atomic_cas_u32(volatile uint32_t *ptr, uint32_t *expectedCurrentV
158160
return false;
159161
}
160162
} while (__STREXW(desiredValue, ptr));
163+
MBED_BARRIER();
161164
return true;
162165
}
163166

164167
uint8_t core_util_atomic_incr_u8(volatile uint8_t *valuePtr, uint8_t delta)
165168
{
169+
MBED_BARRIER();
166170
uint8_t newValue;
167171
do {
168172
newValue = __LDREXB(valuePtr) + delta;
169173
} while (__STREXB(newValue, valuePtr));
174+
MBED_BARRIER();
170175
return newValue;
171176
}
172177

173178
uint16_t core_util_atomic_incr_u16(volatile uint16_t *valuePtr, uint16_t delta)
174179
{
180+
MBED_BARRIER();
175181
uint16_t newValue;
176182
do {
177183
newValue = __LDREXH(valuePtr) + delta;
178184
} while (__STREXH(newValue, valuePtr));
185+
MBED_BARRIER();
179186
return newValue;
180187
}
181188

182189
uint32_t core_util_atomic_incr_u32(volatile uint32_t *valuePtr, uint32_t delta)
183190
{
184191
uint32_t newValue;
192+
MBED_BARRIER();
185193
do {
186194
newValue = __LDREXW(valuePtr) + delta;
187195
} while (__STREXW(newValue, valuePtr));
196+
MBED_BARRIER();
188197
return newValue;
189198
}
190199

191200

192201
uint8_t core_util_atomic_decr_u8(volatile uint8_t *valuePtr, uint8_t delta)
193202
{
194203
uint8_t newValue;
204+
MBED_BARRIER();
195205
do {
196206
newValue = __LDREXB(valuePtr) - delta;
197207
} while (__STREXB(newValue, valuePtr));
208+
MBED_BARRIER();
198209
return newValue;
199210
}
200211

201212
uint16_t core_util_atomic_decr_u16(volatile uint16_t *valuePtr, uint16_t delta)
202213
{
203214
uint16_t newValue;
215+
MBED_BARRIER();
204216
do {
205217
newValue = __LDREXH(valuePtr) - delta;
206218
} while (__STREXH(newValue, valuePtr));
219+
MBED_BARRIER();
207220
return newValue;
208221
}
209222

210223
uint32_t core_util_atomic_decr_u32(volatile uint32_t *valuePtr, uint32_t delta)
211224
{
212225
uint32_t newValue;
226+
MBED_BARRIER();
213227
do {
214228
newValue = __LDREXW(valuePtr) - delta;
215229
} while (__STREXW(newValue, valuePtr));
230+
MBED_BARRIER();
216231
return newValue;
217232
}
218233

platform/mbed_critical.h

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <stdbool.h>
2323
#include <stdint.h>
2424
#include <stddef.h>
25+
#include "mbed_toolchain.h"
2526

2627
#ifdef __cplusplus
2728
extern "C" {
@@ -89,6 +90,19 @@ void core_util_critical_section_exit(void);
8990
*/
9091
bool core_util_in_critical_section(void);
9192

93+
/**@}*/
94+
95+
/**
96+
* \defgroup platform_atomic atomic functions
97+
*
98+
* Atomic functions function analogously to C11 and C++11 - loads have
99+
* acquire semantics, stores have release semantics, and atomic operations
100+
* are sequentially consistent. Atomicity is enforced both between threads and
101+
* interrupt handlers.
102+
*
103+
* @{
104+
*/
105+
92106
/**
93107
* A lock-free, primitive atomic flag.
94108
*
@@ -124,7 +138,11 @@ bool core_util_atomic_flag_test_and_set(volatile core_util_atomic_flag *flagPtr)
124138
*
125139
* @param flagPtr Target flag being cleared.
126140
*/
127-
void core_util_atomic_flag_clear(volatile core_util_atomic_flag *flagPtr);
141+
MBED_FORCEINLINE void core_util_atomic_flag_clear(volatile core_util_atomic_flag *flagPtr)
142+
{
143+
MBED_BARRIER();
144+
flagPtr->_flag = false;
145+
}
128146

129147
/**
130148
* Atomic compare and set. It compares the contents of a memory location to a
@@ -354,6 +372,102 @@ bool core_util_atomic_cas_u32(volatile uint32_t *ptr, uint32_t *expectedCurrentV
354372
*/
355373
bool core_util_atomic_cas_ptr(void *volatile *ptr, void **expectedCurrentValue, void *desiredValue);
356374

375+
/**
376+
* Atomic load.
377+
* @param valuePtr Target memory location.
378+
* @return The loaded value.
379+
*/
380+
MBED_FORCEINLINE uint8_t core_util_atomic_load_u8(const volatile uint8_t *valuePtr)
381+
{
382+
uint8_t value = *valuePtr;
383+
MBED_BARRIER();
384+
return value;
385+
}
386+
387+
/**
388+
* Atomic load.
389+
* @param valuePtr Target memory location.
390+
* @return The loaded value.
391+
*/
392+
MBED_FORCEINLINE uint16_t core_util_atomic_load_u16(const volatile uint16_t *valuePtr)
393+
{
394+
uint16_t value = *valuePtr;
395+
MBED_BARRIER();
396+
return value;
397+
}
398+
399+
/**
400+
* Atomic load.
401+
* @param valuePtr Target memory location.
402+
* @return The loaded value.
403+
*/
404+
MBED_FORCEINLINE uint32_t core_util_atomic_load_u32(const volatile uint32_t *valuePtr)
405+
{
406+
uint32_t value = *valuePtr;
407+
MBED_BARRIER();
408+
return value;
409+
}
410+
411+
/**
412+
* Atomic load.
413+
* @param valuePtr Target memory location.
414+
* @return The loaded value.
415+
*/
416+
MBED_FORCEINLINE void *core_util_atomic_load_ptr(void *const volatile *valuePtr)
417+
{
418+
void *value = *valuePtr;
419+
MBED_BARRIER();
420+
return value;
421+
}
422+
423+
/**
424+
* Atomic store.
425+
* @param valuePtr Target memory location.
426+
* @param desiredValue The value to store.
427+
*/
428+
MBED_FORCEINLINE void core_util_atomic_store_u8(volatile uint8_t *valuePtr, uint8_t desiredValue)
429+
{
430+
MBED_BARRIER();
431+
*valuePtr = desiredValue;
432+
MBED_BARRIER();
433+
}
434+
435+
/**
436+
* Atomic store.
437+
* @param valuePtr Target memory location.
438+
* @param desiredValue The value to store.
439+
*/
440+
MBED_FORCEINLINE void core_util_atomic_store_u16(volatile uint16_t *valuePtr, uint16_t desiredValue)
441+
{
442+
MBED_BARRIER();
443+
*valuePtr = desiredValue;
444+
MBED_BARRIER();
445+
}
446+
447+
/**
448+
* Atomic store.
449+
* @param valuePtr Target memory location.
450+
* @param desiredValue The value to store.
451+
*/
452+
MBED_FORCEINLINE void core_util_atomic_store_u32(volatile uint32_t *valuePtr, uint32_t desiredValue)
453+
{
454+
MBED_BARRIER();
455+
*valuePtr = desiredValue;
456+
MBED_BARRIER();
457+
}
458+
459+
/**
460+
* Atomic store.
461+
* @param valuePtr Target memory location.
462+
* @param desiredValue The value to store.
463+
*/
464+
MBED_FORCEINLINE void core_util_atomic_store_ptr(void *volatile *valuePtr, void *desiredValue)
465+
{
466+
MBED_BARRIER();
467+
*valuePtr = desiredValue;
468+
MBED_BARRIER();
469+
}
470+
357471
/**
358472
* Atomic increment.
359473
* @param valuePtr Target memory location being incremented.

platform/mbed_toolchain.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,77 @@
152152
#endif
153153
#endif
154154

155+
/** MBED_COMPILER_BARRIER
156+
* Stop the compiler moving memory accesses.
157+
*
158+
* The barrier stops memory accesses from being moved from one side of the
159+
* barrier to the other for safety against other threads and interrupts.
160+
*
161+
* This macro should only be used if we know only one CPU is accessing the data,
162+
* or we are otherwise synchronising CPUs via acquire/release instructions.
163+
* Otherwise, use MBED_BARRIER, which will act as a compiler barrier and also
164+
* a CPU barrier if necessary.
165+
*
166+
* @internal
167+
* This is not for use by normal code - it is a building block for the
168+
* higher-level functions in mbed_critical.h. Higher-level lock/unlock or
169+
* acquire/release APIs always provide ordering semantics, using this if
170+
* necessary.
171+
*
172+
* @code
173+
* #include "mbed_toolchain.h"
174+
*
175+
* void atomic_flag_clear_armv8(atomic_flag *flagPtr)
176+
* {
177+
* // ARMv8 LDA and STL instructions provide sequential consistency against
178+
* // other CPUs, so no CPU barrier is needed. But we still need compiler
179+
* // barriers to give us sequentially-consistent release semantics with
180+
* // respect to compiler reordering - __STLB does not currently
181+
* // include this.
182+
* MBED_COMPILER_BARRIER();
183+
* __STLB(&flagPtr->_flag, false);
184+
* MBED_COMPILER_BARRIER();
185+
* }
186+
*/
187+
#ifdef __CC_ARM
188+
#define MBED_COMPILER_BARRIER() __memory_changed()
189+
#elif defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__)
190+
#define MBED_COMPILER_BARRIER() asm volatile("" : : : "memory")
191+
#else
192+
#error "Missing MBED_COMPILER_BARRIER implementation"
193+
#endif
194+
195+
/** MBED_BARRIER
196+
* Stop the compiler, and CPU if SMP, from moving memory accesses.
197+
*
198+
* The barrier stops memory accesses from being moved from one side of the
199+
* barrier to the other for safety against other threads and interrupts,
200+
* potentially on other CPUs.
201+
*
202+
* In a single-CPU system, this is just a compiler barrier.
203+
* If we supported multiple CPUs, this would be a DMB (with implied compiler
204+
* barrier).
205+
*
206+
* @internal
207+
* This is not for use by normal code - it is a building block for the
208+
* higher-level functions in mbed_critical.h. Higher-level lock/unlock or
209+
* acquire/release APIs always provide ordering semantics, using this if
210+
* necessary.
211+
* @code
212+
* #include "mbed_toolchain.h"
213+
*
214+
* void atomic_flag_clear_armv7(atomic_flag *flagPtr)
215+
* {
216+
* // ARMv7 LDR and STR instructions do not provide any ordering
217+
* // consistency against other CPUs, so explicit barrier DMBs are needed
218+
* // for a multi-CPU system, otherwise just compiler barriers for single-CPU.
219+
* MBED_BARRIER();
220+
* flagPtr->_flag = false;
221+
* MBED_BARRIER();
222+
* }
223+
*/
224+
#define MBED_BARRIER() MBED_COMPILER_BARRIER()
225+
155226
/** MBED_PURE
156227
* Hint to the compiler that a function depends only on parameters
157228
*

0 commit comments

Comments
 (0)