From 988ae54e4f0b84f51eac1637ba7cb8880d675207 Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Wed, 14 Jan 2015 12:40:07 +0100 Subject: [PATCH 1/8] cortex-m3_common: Homogenize documentation tags against Cortex-M0, Cortex-M4. --- cpu/cortex-m3_common/atomic_arch.c | 2 +- cpu/cortex-m3_common/doc.txt | 5 ----- cpu/cortex-m3_common/include/cpu.h | 4 +++- cpu/cortex-m3_common/irq_arch.c | 2 +- cpu/cortex-m3_common/thread_arch.c | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 cpu/cortex-m3_common/doc.txt diff --git a/cpu/cortex-m3_common/atomic_arch.c b/cpu/cortex-m3_common/atomic_arch.c index 1228ac2061..9b51df0e58 100644 --- a/cpu/cortex-m3_common/atomic_arch.c +++ b/cpu/cortex-m3_common/atomic_arch.c @@ -7,7 +7,7 @@ */ /** - * @ingroup cpu_cortex-m3 + * @ingroup cpu_cortexm3_common * @{ * * @file atomic_arch.c diff --git a/cpu/cortex-m3_common/doc.txt b/cpu/cortex-m3_common/doc.txt deleted file mode 100644 index 27fa90cf7b..0000000000 --- a/cpu/cortex-m3_common/doc.txt +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @defgroup cpu_cortex-m3 Cortex-M3 common - * @brief ARM Cortex-M specific code - * @ingroup cpu - */ diff --git a/cpu/cortex-m3_common/include/cpu.h b/cpu/cortex-m3_common/include/cpu.h index 8fe47d80d8..16b8728ba5 100644 --- a/cpu/cortex-m3_common/include/cpu.h +++ b/cpu/cortex-m3_common/include/cpu.h @@ -7,7 +7,9 @@ */ /** - * @addtogroup cpu_cortex-m3 + * @defgroup cpu_cortexm3_common ARM Cortex-M3 common + * @ingroup cpu + * @brief Common implementations and headers for Cortex-M3 family based micro-controllers * @{ * * @file diff --git a/cpu/cortex-m3_common/irq_arch.c b/cpu/cortex-m3_common/irq_arch.c index 1ba09fcb0b..df9ef26fc3 100644 --- a/cpu/cortex-m3_common/irq_arch.c +++ b/cpu/cortex-m3_common/irq_arch.c @@ -7,7 +7,7 @@ */ /** - * @ingroup cpu_cortex-m3 + * @ingroup cpu_cortexm3_common * @{ * * @file irq_arch.c diff --git a/cpu/cortex-m3_common/thread_arch.c b/cpu/cortex-m3_common/thread_arch.c index b779e9a3fb..7b777c7f51 100644 --- a/cpu/cortex-m3_common/thread_arch.c +++ b/cpu/cortex-m3_common/thread_arch.c @@ -7,7 +7,7 @@ */ /** - * @ingroup cpu_cortex-m3 + * @ingroup cpu_cortexm3_common * @{ * * @file thread_arch.c From 215ccc1213d7e53592766b1cb4e0c50592580c54 Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Tue, 28 Apr 2015 22:57:56 +0200 Subject: [PATCH 2/8] core: Introduce atomic counters - Move generic implementation of atomic_set_return to core/atomic.c - Generic implementation of atomic compare and swap in core/atomic.c - atomic_cas is used to implement atomic counters in core/include/atomic.h - atomic_int_t is an atomic integer type - ATOMIC_INIT can be used as an initializer for atomic_int_t - ATOMIC_VALUE gets a reference to the value of an atomic integer --- core/atomic.c | 59 +++++++++++++ core/include/atomic.h | 133 +++++++++++++++++++++++++++-- cpu/atmega_common/atomic_arch.c | 33 ------- cpu/cortex-m0_common/atomic_arch.c | 33 ------- cpu/cortex-m3_common/include/cpu.h | 6 ++ cpu/cortex-m4_common/include/cpu.h | 6 ++ cpu/msp430-common/atomic.c | 32 ------- cpu/x86/include/cpu.h | 5 ++ 8 files changed, 203 insertions(+), 104 deletions(-) create mode 100644 core/atomic.c delete mode 100644 cpu/atmega_common/atomic_arch.c delete mode 100644 cpu/cortex-m0_common/atomic_arch.c delete mode 100644 cpu/msp430-common/atomic.c diff --git a/core/atomic.c b/core/atomic.c new file mode 100644 index 0000000000..c267a23cbc --- /dev/null +++ b/core/atomic.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2015 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + + +/** + * @ingroup core_util + * @{ + * + * @file + * @brief Generic implementation of the kernel's atomic interface + * + * @author Kaspar Schleiser + * @author Oliver Hahm + * @author Joakim Gebart + * + * @} + */ + +#include "irq.h" +#include "cpu.h" +#include "atomic.h" + +#if (ARCH_HAS_ATOMIC_SET_RETURN == 0) + +unsigned int atomic_set_return(unsigned int *val, unsigned int set) +{ + unsigned int mask = disableIRQ(); + unsigned int old_val = *val; + *val = set; + restoreIRQ(mask); + return old_val; +} + +#endif + +/* Set ARCH_HAS_ATOMIC_COMPARE_AND_SWAP within cpu.h to override this function */ +#if (ARCH_HAS_ATOMIC_COMPARE_AND_SWAP == 0) + +int atomic_cas(atomic_int_t *var, int old, int now) +{ + unsigned int mask = disableIRQ(); + + if (ATOMIC_VALUE(*var) != old) { + restoreIRQ(mask); + return 0; + } + + ATOMIC_VALUE(*var) = now; + restoreIRQ(mask); + return 1; +} + +#endif diff --git a/core/include/atomic.h b/core/include/atomic.h index 0d6b64a32d..e53a80eb33 100644 --- a/core/include/atomic.h +++ b/core/include/atomic.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2015 Eistec AB * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -10,21 +11,31 @@ * @addtogroup core_util * @{ * - * @file atomic.h - * @brief Atomic getter and setter functions + * @file + * @brief Functions for atomic handling of variables * * @author Kaspar Schleiser + * @author Joakim Gebart */ -#ifndef _ATOMIC_H -#define _ATOMIC_H +#ifndef ATOMIC_H_ +#define ATOMIC_H_ #include "arch/atomic_arch.h" #ifdef __cplusplus - extern "C" { +extern "C" { #endif +/** + * @brief Integer variable for use in atomic counters. + */ +typedef struct atomic_int { + volatile int value; +} atomic_int_t; + + + /** * @brief Sets a new and returns the old value of a variable atomically * @@ -35,9 +46,119 @@ */ unsigned int atomic_set_return(unsigned int *val, unsigned int set); +/** + * @brief Initializer for atomic variables + * + * @param[in] val initial value for the atomic integer + */ +#define ATOMIC_INIT(val) {(val)} + +/** + * @brief Atomic Compare and Swap + * + * Updates the value in var iff the current value of var is equal to old. + * + * @param[inout] var Atomic variable to update + * @param[in] old The old value to compare against + * @param[in] now The new value to write to var + * + * @return 1 if the write completed successfully + * @return 0 if the write failed. + */ +int atomic_cas(atomic_int_t *var, int old, int now); + +/** + * @brief Increment a counter variable by one atomically and return the old value. + * + * @param[inout] var Pointer to a counter variable. + * + * @return The value of *val* before the increment. + */ +static inline int atomic_inc(atomic_int_t *var) +{ + int old; + + do { + old = var->value; + } while (!atomic_cas(var, old, old + 1)); + + return old; +} + +/** + * @brief Decrement a counter variable by one atomically and return the old value. + * + * @param[inout] var Pointer to a counter variable. + * + * @return The value of *val* before the decrement. + */ +static inline int atomic_dec(atomic_int_t *var) +{ + int old; + + do { + old = var->value; + } while (!atomic_cas(var, old, old - 1)); + + return old; +} + +/** + * @brief Set an atomic variable to 1. + * + * @param[inout] var Pointer to an atomic variable to update + * + * @return 1 if the old value was 0 and the variable was successfully updated + * @return 0 if the variable was already set + */ +static inline int atomic_set_to_one(atomic_int_t *var) +{ + do { + if (var->value != 0) { + return 0; + } + } while (!atomic_cas(var, 0, 1)); + + return 1; +} + +/** + * @brief Set an atomic variable to 0. + * + * @param[inout] var Pointer to an atomic variable to update + * + * @return 1 if the old value was not 0 and the variable was successfully updated + * @return 0 if the variable was already cleared + */ +static inline int atomic_set_to_zero(atomic_int_t *var) +{ + int old; + + do { + old = var->value; + + if (old == 0) { + return 0; + } + } while (!atomic_cas(var, old, 0)); + + return 1; +} + +/** + * @brief Get the value of an atomic int + * + * @param[in] var Atomic variable + * + * @return the value of the atomic integer + * + * @note This can be used for non-thread-safe assignment of the atomic integer + */ +#define ATOMIC_VALUE(var) ((var).value) + #ifdef __cplusplus } #endif -#endif /* _ATOMIC_H */ +#endif /* ATOMIC_H_ */ /** @} */ diff --git a/cpu/atmega_common/atomic_arch.c b/cpu/atmega_common/atomic_arch.c deleted file mode 100644 index e945cb337f..0000000000 --- a/cpu/atmega_common/atomic_arch.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - */ - -/** - * @ingroup cpu_atmega_common - * @{ - * - * @file atomic_arch.c - * @brief Implementation of the kernels atomic interface - * - * @author Stefan Pfeiffer - * @author Hauke Petersen - * @author Hinnerk van Bruinehsen - * - * @} - */ - -#include "arch/atomic_arch.h" -#include "irq.h" - -unsigned int atomic_arch_set_return(unsigned int *to_set, unsigned int value) -{ - disableIRQ(); - unsigned int old = *to_set; - *to_set = value; - enableIRQ(); - return old; -} diff --git a/cpu/cortex-m0_common/atomic_arch.c b/cpu/cortex-m0_common/atomic_arch.c deleted file mode 100644 index bbb1e57f87..0000000000 --- a/cpu/cortex-m0_common/atomic_arch.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License v2.1. See the file LICENSE in the top level directory for more - * details. - */ - -/** - * @ingroup cpu_cortexm0_common - * @{ - * - * @file - * @brief Implementation of the kernels atomic interface - * - * @author Stefan Pfeiffer - * @author Hauke Petersen - * - * @} - */ - -#include "arch/atomic_arch.h" -#include "irq.h" - - -unsigned int atomic_arch_set_return(unsigned int *to_set, unsigned int value) -{ - disableIRQ(); - unsigned int old = *to_set; - *to_set = value; - enableIRQ(); - return old; -} diff --git a/cpu/cortex-m3_common/include/cpu.h b/cpu/cortex-m3_common/include/cpu.h index 16b8728ba5..6ace208af4 100644 --- a/cpu/cortex-m3_common/include/cpu.h +++ b/cpu/cortex-m3_common/include/cpu.h @@ -20,11 +20,17 @@ * * @author Stefan Pfeiffer * @author Hauke Petersen + * @author Joakim Gebart */ #ifndef __CORTEXM_COMMON_H #define __CORTEXM_COMMON_H +/** + * @brief Cortex-M3 has architecture specific atomic operations in atomic_arch.c. + */ +#define ARCH_HAS_ATOMIC_INT 1 + #include "cpu-conf.h" /** diff --git a/cpu/cortex-m4_common/include/cpu.h b/cpu/cortex-m4_common/include/cpu.h index 031e900c3c..6ec2195083 100644 --- a/cpu/cortex-m4_common/include/cpu.h +++ b/cpu/cortex-m4_common/include/cpu.h @@ -20,11 +20,17 @@ * * @author Stefan Pfeiffer * @author Hauke Petersen + * @author Joakim Gebart */ #ifndef __CORTEXM_COMMON_H #define __CORTEXM_COMMON_H +/** + * @brief Cortex-M4 has architecture specific atomic operations in atomic_arch.c. + */ +#define ARCH_HAS_ATOMIC_INT 1 + #include "cpu-conf.h" /** diff --git a/cpu/msp430-common/atomic.c b/cpu/msp430-common/atomic.c deleted file mode 100644 index 08469c9eb3..0000000000 --- a/cpu/msp430-common/atomic.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - */ - -/** - * @ingroup cpu - * @{ - * - * @file atomic.c - * @brief atomic set and return function - * - * @author Kaspar Schleiser - * @author Oliver Hahm - * - * @} - */ - -#include "atomic.h" -#include "cpu.h" - -unsigned int atomic_set_return(unsigned int *val, unsigned int set) -{ - dINT(); - unsigned int old_val = *val; - *val = set; - eINT(); - return old_val; -} diff --git a/cpu/x86/include/cpu.h b/cpu/x86/include/cpu.h index b82b14a8fc..080bedec27 100644 --- a/cpu/x86/include/cpu.h +++ b/cpu/x86/include/cpu.h @@ -43,6 +43,11 @@ extern "C" { #endif +/** + * @brief x86 has architecture specific atomic operations in x86_atomic.c. + */ +#define ARCH_HAS_ATOMIC_INT 1 + static inline void __attribute__((always_inline)) dINT(void) { asm volatile ("cli"); From 25c2bdba848a6ee6496f97d165dbf16ead73f11a Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Tue, 28 Apr 2015 23:13:07 +0200 Subject: [PATCH 3/8] atomic: Add unit tests for atomic counters --- .../unittests/tests-core/tests-core-atomic.c | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/tests/unittests/tests-core/tests-core-atomic.c b/tests/unittests/tests-core/tests-core-atomic.c index 912a6681a9..ae126c2619 100644 --- a/tests/unittests/tests-core/tests-core-atomic.c +++ b/tests/unittests/tests-core/tests-core-atomic.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Martine Lenders + * Copyright (C) 2015 Eistec AB * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -63,6 +64,196 @@ static void test_atomic_set_return_null_random(void) TEST_ASSERT_EQUAL_INT(r, res); } +/* Test atomic_set_to_one on a variable set to 0 */ +static void test_atomic_set_to_one_zero(void) +{ + atomic_int_t res = ATOMIC_INIT(0); + + TEST_ASSERT_EQUAL_INT(1, atomic_set_to_one(&res)); + TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res)); +} + +/* Test atomic_set_to_one on a variable set to 1 */ +static void test_atomic_set_to_one_one(void) +{ + atomic_int_t res = ATOMIC_INIT(1); + + TEST_ASSERT_EQUAL_INT(0, atomic_set_to_one(&res)); + TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res)); +} + +/* Test atomic_set_to_one twice */ +static void test_atomic_set_to_one_twice(void) +{ + atomic_int_t res = ATOMIC_INIT(0); + + TEST_ASSERT_EQUAL_INT(1, atomic_set_to_one(&res)); + TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(0, atomic_set_to_one(&res)); + TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res)); +} + +/* Test atomic_set_to_zero on a variable set to 0 */ +static void test_atomic_set_to_zero_zero(void) +{ + atomic_int_t res = ATOMIC_INIT(0); + + TEST_ASSERT_EQUAL_INT(0, atomic_set_to_zero(&res)); + TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res)); +} + +/* Test atomic_set_to_zero on a variable set to 1 */ +static void test_atomic_set_to_zero_one(void) +{ + atomic_int_t res = ATOMIC_INIT(1); + + TEST_ASSERT_EQUAL_INT(1, atomic_set_to_zero(&res)); + TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res)); +} + +/* Test atomic_set_to_zero twice */ +static void test_atomic_set_to_zero_twice(void) +{ + atomic_int_t res = ATOMIC_INIT(1); + + TEST_ASSERT_EQUAL_INT(1, atomic_set_to_zero(&res)); + TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(0, atomic_set_to_zero(&res)); + TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res)); +} + +/* Test atomic_inc */ +static void test_atomic_inc_positive(void) +{ + atomic_int_t res = ATOMIC_INIT(0); + + TEST_ASSERT_EQUAL_INT(0, atomic_inc(&res)); + TEST_ASSERT_EQUAL_INT(1, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(1, atomic_inc(&res)); + TEST_ASSERT_EQUAL_INT(2, ATOMIC_VALUE(res)); + ATOMIC_VALUE(res) = 0; + for (int i = 0; i < 512; ++i) { + TEST_ASSERT_EQUAL_INT(i, atomic_inc(&res)); + TEST_ASSERT_EQUAL_INT(i + 1, ATOMIC_VALUE(res)); + } +} + +static void test_atomic_inc_negative(void) +{ + atomic_int_t res = ATOMIC_INIT(-99); + + for (int i = -99; i < 123; ++i) { + TEST_ASSERT_EQUAL_INT(i, atomic_inc(&res)); + TEST_ASSERT_EQUAL_INT(i + 1, ATOMIC_VALUE(res)); + } +} + +static void test_atomic_inc_rollover(void) +{ + atomic_int_t res = ATOMIC_INIT(INT_MAX - 30); + + for (int i = 0; i < 30; ++i) { + TEST_ASSERT_EQUAL_INT(INT_MAX - 30 + i, atomic_inc(&res)); + TEST_ASSERT_EQUAL_INT(INT_MAX - 30 + i + 1, ATOMIC_VALUE(res)); + } + TEST_ASSERT_EQUAL_INT(INT_MAX, atomic_inc(&res)); + TEST_ASSERT_EQUAL_INT(INT_MIN, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(INT_MIN, atomic_inc(&res)); + TEST_ASSERT_EQUAL_INT(INT_MIN + 1, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(INT_MIN + 1, atomic_inc(&res)); + TEST_ASSERT_EQUAL_INT(INT_MIN + 2, ATOMIC_VALUE(res)); +} + +/* Test atomic_dec */ +static void test_atomic_dec_negative(void) +{ + atomic_int_t res = ATOMIC_INIT(0); + + TEST_ASSERT_EQUAL_INT(0, atomic_dec(&res)); + TEST_ASSERT_EQUAL_INT(-1, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(-1, atomic_dec(&res)); + TEST_ASSERT_EQUAL_INT(-2, ATOMIC_VALUE(res)); + ATOMIC_VALUE(res) = 0; + for (int i = 0; i < 512; ++i) { + TEST_ASSERT_EQUAL_INT(-i, atomic_dec(&res)); + TEST_ASSERT_EQUAL_INT(-i - 1, ATOMIC_VALUE(res)); + } +} + +static void test_atomic_dec_positive(void) +{ + atomic_int_t res = ATOMIC_INIT(99); + + for (int i = 99; i < -123; --i) { + TEST_ASSERT_EQUAL_INT(i, atomic_dec(&res)); + TEST_ASSERT_EQUAL_INT(i - 1, ATOMIC_VALUE(res)); + } +} + +static void test_atomic_dec_rollover(void) +{ + atomic_int_t res = ATOMIC_INIT(INT_MIN + 30); + + for (int i = 0; i < 30; ++i) { + TEST_ASSERT_EQUAL_INT(INT_MIN + 30 - i, atomic_dec(&res)); + TEST_ASSERT_EQUAL_INT(INT_MIN + 30 - i - 1, ATOMIC_VALUE(res)); + } + TEST_ASSERT_EQUAL_INT(INT_MIN, atomic_dec(&res)); + TEST_ASSERT_EQUAL_INT(INT_MAX, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(INT_MAX, atomic_dec(&res)); + TEST_ASSERT_EQUAL_INT(INT_MAX - 1, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(INT_MAX - 1, atomic_dec(&res)); + TEST_ASSERT_EQUAL_INT(INT_MAX - 2, ATOMIC_VALUE(res)); +} + +/* Test atomic_cas with a correct old value */ +static void test_atomic_cas_same(void) +{ + atomic_int_t res = ATOMIC_INIT(0); + + TEST_ASSERT_EQUAL_INT(1, atomic_cas(&res, 0, 1234567)); + TEST_ASSERT_EQUAL_INT(1234567, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(1, atomic_cas(&res, 1234567, -987654321)); + TEST_ASSERT_EQUAL_INT(-987654321, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(1, atomic_cas(&res, -987654321, -987654321)); + TEST_ASSERT_EQUAL_INT(-987654321, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(1, atomic_cas(&res, -987654321, 0)); + TEST_ASSERT_EQUAL_INT(0, ATOMIC_VALUE(res)); +} + +/* Test atomic_cas with a non-matching old value */ +static void test_atomic_cas_diff(void) +{ + atomic_int_t res = ATOMIC_INIT(32767); + + TEST_ASSERT_EQUAL_INT(0, atomic_cas(&res, 65535, 12345)); + TEST_ASSERT_EQUAL_INT(32767, ATOMIC_VALUE(res)); + ATOMIC_VALUE(res) = -12345687; + TEST_ASSERT_EQUAL_INT(0, atomic_cas(&res, 12345687, 123456789)); + TEST_ASSERT_EQUAL_INT(-12345687, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(0, atomic_cas(&res, 12345687, 12345687)); + TEST_ASSERT_EQUAL_INT(-12345687, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(0, atomic_cas(&res, 12345687, -12345687)); + TEST_ASSERT_EQUAL_INT(-12345687, ATOMIC_VALUE(res)); +} + +/* Test ATOMIC_VALUE */ +static void test_atomic_value(void) +{ + atomic_int_t res = ATOMIC_INIT(12345); + atomic_int_t *ptr = &res; + + TEST_ASSERT_EQUAL_INT(12345, ATOMIC_VALUE(res)); + ATOMIC_VALUE(res) = 54332; + TEST_ASSERT_EQUAL_INT(54332, ATOMIC_VALUE(res)); + TEST_ASSERT_EQUAL_INT(54332, res.value); + res.value = 1232342131; + TEST_ASSERT_EQUAL_INT(ATOMIC_VALUE(res), res.value); + TEST_ASSERT_EQUAL_INT(ATOMIC_VALUE(*ptr), res.value); +} + +/* ATOMIC_INIT is implicitly tested by the other tests */ + Test *tests_core_atomic_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -72,6 +263,21 @@ Test *tests_core_atomic_tests(void) new_TestFixture(test_atomic_set_return_limit_null), new_TestFixture(test_atomic_set_return_null_limit), new_TestFixture(test_atomic_set_return_null_random), + new_TestFixture(test_atomic_set_to_one_one), + new_TestFixture(test_atomic_set_to_one_zero), + new_TestFixture(test_atomic_set_to_one_twice), + new_TestFixture(test_atomic_set_to_zero_one), + new_TestFixture(test_atomic_set_to_zero_zero), + new_TestFixture(test_atomic_set_to_zero_twice), + new_TestFixture(test_atomic_inc_positive), + new_TestFixture(test_atomic_inc_negative), + new_TestFixture(test_atomic_inc_rollover), + new_TestFixture(test_atomic_dec_positive), + new_TestFixture(test_atomic_dec_negative), + new_TestFixture(test_atomic_dec_rollover), + new_TestFixture(test_atomic_cas_same), + new_TestFixture(test_atomic_cas_diff), + new_TestFixture(test_atomic_value), }; EMB_UNIT_TESTCALLER(core_atomic_tests, NULL, NULL, From 46b9358e848aea388fb4ab6265deba11ea8e8823 Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Thu, 7 May 2015 15:49:52 +0200 Subject: [PATCH 4/8] native: Remove generic atomic_set_return implementation The removed implementation is the same as the generic implementation in core/atomic.c --- cpu/native/atomic_cpu.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 cpu/native/atomic_cpu.c diff --git a/cpu/native/atomic_cpu.c b/cpu/native/atomic_cpu.c deleted file mode 100644 index 6b7afb8f6c..0000000000 --- a/cpu/native/atomic_cpu.c +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Native CPU atomic.h implementation - * - * Copyright (C) 2013 Ludwig Ortmann - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - * - * @ingroup arch - * @{ - * @file - * @author Ludwig Ortmann - * @} - */ - -#include "atomic.h" -#include "irq.h" -#include "debug.h" - -unsigned int atomic_set_return(unsigned int *val, unsigned int set) -{ - unsigned int old_val; - unsigned int old_state; - - DEBUG("atomic_set_return\n"); - - old_state = disableIRQ(); - - old_val = *val; - *val = set; - - restoreIRQ(old_state); - - return old_val; -} From b7db351400b09bdfcfca1809197e79514fcff55a Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Tue, 28 Apr 2015 23:08:31 +0200 Subject: [PATCH 5/8] cortex-m3: Add atomic_cas implementation --- cpu/cortex-m3_common/atomic_arch.c | 30 ++++++++++++++++++++++-------- cpu/cortex-m3_common/include/cpu.h | 8 ++++---- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/cpu/cortex-m3_common/atomic_arch.c b/cpu/cortex-m3_common/atomic_arch.c index 9b51df0e58..1ee7f4fc50 100644 --- a/cpu/cortex-m3_common/atomic_arch.c +++ b/cpu/cortex-m3_common/atomic_arch.c @@ -10,23 +10,37 @@ * @ingroup cpu_cortexm3_common * @{ * - * @file atomic_arch.c + * @file * @brief Implementation of the kernels atomic interface * * @author Stefan Pfeiffer * @author Hauke Petersen + * @author Joakim Gebart * * @} */ -#include "arch/atomic_arch.h" +#include +#include "atomic.h" #include "irq.h" +#include "cpu.h" -unsigned int atomic_arch_set_return(unsigned int *to_set, unsigned int value) +int atomic_cas(atomic_int_t *var, int old, int now) { - disableIRQ(); - unsigned int old = *to_set; - *to_set = value; - enableIRQ(); - return old; + int tmp; + int status; + + /* Load exclusive */ + tmp = __LDREXW((volatile uint32_t *)(&ATOMIC_VALUE(*var))); + + if (tmp != old) { + /* Clear memory exclusivity */ + __CLREX(); + return 0; + } + + /* Try to write the new value */ + status = __STREXW(now, (volatile uint32_t *)(&ATOMIC_VALUE(*var))); + + return (status == 0); } diff --git a/cpu/cortex-m3_common/include/cpu.h b/cpu/cortex-m3_common/include/cpu.h index 6ace208af4..b72a1d768c 100644 --- a/cpu/cortex-m3_common/include/cpu.h +++ b/cpu/cortex-m3_common/include/cpu.h @@ -23,13 +23,13 @@ * @author Joakim Gebart */ -#ifndef __CORTEXM_COMMON_H -#define __CORTEXM_COMMON_H +#ifndef CORTEXM_COMMON_H_ +#define CORTEXM_COMMON_H_ /** * @brief Cortex-M3 has architecture specific atomic operations in atomic_arch.c. */ -#define ARCH_HAS_ATOMIC_INT 1 +#define ARCH_HAS_ATOMIC_COMPARE_AND_SWAP 1 #include "cpu-conf.h" @@ -55,5 +55,5 @@ void cpu_init(void); } #endif -#endif /* __CORTEXM_COMMON_H */ +#endif /* CORTEXM_COMMON_H_ */ /** @} */ From afc1dd3a6df14c8961cfec0d6b8752dd5fffa001 Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Tue, 28 Apr 2015 23:08:47 +0200 Subject: [PATCH 6/8] cortex-m4: Add atomic_cas implementation --- cpu/cortex-m4_common/atomic_arch.c | 28 +++++++++++++++++++++------- cpu/cortex-m4_common/include/cpu.h | 8 ++++---- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/cpu/cortex-m4_common/atomic_arch.c b/cpu/cortex-m4_common/atomic_arch.c index 662caa92b2..6827189e3b 100644 --- a/cpu/cortex-m4_common/atomic_arch.c +++ b/cpu/cortex-m4_common/atomic_arch.c @@ -15,18 +15,32 @@ * * @author Stefan Pfeiffer * @author Hauke Petersen + * @author Joakim Gebart * * @} */ -#include "arch/atomic_arch.h" +#include +#include "atomic.h" #include "irq.h" +#include "cpu.h" -unsigned int atomic_arch_set_return(unsigned int *to_set, unsigned int value) +int atomic_cas(atomic_int_t *var, int old, int now) { - disableIRQ(); - unsigned int old = *to_set; - *to_set = value; - enableIRQ(); - return old; + int tmp; + int status; + + /* Load exclusive */ + tmp = __LDREXW((volatile uint32_t *)(&ATOMIC_VALUE(*var))); + + if (tmp != old) { + /* Clear memory exclusivity */ + __CLREX(); + return 0; + } + + /* Try to write the new value */ + status = __STREXW(now, (volatile uint32_t *)(&ATOMIC_VALUE(*var))); + + return (status == 0); } diff --git a/cpu/cortex-m4_common/include/cpu.h b/cpu/cortex-m4_common/include/cpu.h index 6ec2195083..6fc2ced23f 100644 --- a/cpu/cortex-m4_common/include/cpu.h +++ b/cpu/cortex-m4_common/include/cpu.h @@ -23,13 +23,13 @@ * @author Joakim Gebart */ -#ifndef __CORTEXM_COMMON_H -#define __CORTEXM_COMMON_H +#ifndef CORTEXM_COMMON_H_ +#define CORTEXM_COMMON_H_ /** * @brief Cortex-M4 has architecture specific atomic operations in atomic_arch.c. */ -#define ARCH_HAS_ATOMIC_INT 1 +#define ARCH_HAS_ATOMIC_COMPARE_AND_SWAP 1 #include "cpu-conf.h" @@ -55,5 +55,5 @@ void cpu_init(void); } #endif -#endif /* __CORTEXM_COMMON_H */ +#endif /* CORTEXM_COMMON_H_ */ /** @} */ From 96ca6a6beff987c163bf7fcaf617e4afa7b5a577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Wed, 11 Mar 2015 06:35:17 +0100 Subject: [PATCH 7/8] x86: Add atomic_cas implementation --- cpu/x86/include/cpu.h | 7 ++++--- cpu/x86/x86_atomic.c | 14 +++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cpu/x86/include/cpu.h b/cpu/x86/include/cpu.h index 080bedec27..6b628e8295 100644 --- a/cpu/x86/include/cpu.h +++ b/cpu/x86/include/cpu.h @@ -26,8 +26,8 @@ * @author René Kijewski */ -#ifndef CPU__X86__CPU__H__ -#define CPU__X86__CPU__H__ +#ifndef CPU_X86_CPU_H_ +#define CPU_X86_CPU_H_ #include "attributes.h" #include "irq.h" @@ -46,7 +46,8 @@ extern "C" { /** * @brief x86 has architecture specific atomic operations in x86_atomic.c. */ -#define ARCH_HAS_ATOMIC_INT 1 +#define ARCH_HAS_ATOMIC_SET_RETURN 1 +#define ARCH_HAS_ATOMIC_COMPARE_AND_SWAP 1 static inline void __attribute__((always_inline)) dINT(void) { diff --git a/cpu/x86/x86_atomic.c b/cpu/x86/x86_atomic.c index 988340410f..d06366a0a0 100644 --- a/cpu/x86/x86_atomic.c +++ b/cpu/x86/x86_atomic.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 René Kijewski + * Copyright (C) 2014-2015 René Kijewski * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,6 +28,7 @@ * @} */ +#include #include "atomic.h" unsigned int atomic_set_return(unsigned int *val, unsigned int set) @@ -35,3 +36,14 @@ unsigned int atomic_set_return(unsigned int *val, unsigned int set) asm volatile ("lock xchg %0, %1" : "+m"(*val), "+r"(set)); return set; } + +int atomic_cas(atomic_int_t *dest, int known_value, int new_value) +{ + uint8_t successful; + asm volatile ("lock cmpxchgl %2, %0\n" + "seteb %1" + : "+m"(ATOMIC_VALUE(*dest)), "=g"(successful) + : "r"(new_value), "a"(known_value) + : "flags"); + return successful; +} From f15fc173b9f649919caed267adfbba24e53008cd Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Wed, 6 May 2015 07:58:09 +0200 Subject: [PATCH 8/8] arm7: Add ARCH_HAS_ATOMIC_SET_RETURN --- cpu/arm7_common/include/arm_cpu.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cpu/arm7_common/include/arm_cpu.h b/cpu/arm7_common/include/arm_cpu.h index cf8db69083..cf14b3bd41 100644 --- a/cpu/arm7_common/include/arm_cpu.h +++ b/cpu/arm7_common/include/arm_cpu.h @@ -6,8 +6,8 @@ * directory for more details. */ -#ifndef _ARM_CPU_H -#define _ARM_CPU_H +#ifndef ARM_CPU_H_ +#define ARM_CPU_H_ #include #include "VIC.h" @@ -17,6 +17,11 @@ extern "C" { #endif +/** + * @brief ARM has architecture specific atomic_set_return in atomic.s + */ +#define ARCH_HAS_ATOMIC_SET_RETURN 1 + #define NEW_TASK_CPSR 0x1F #define WORDSIZE 32 @@ -44,4 +49,4 @@ int uart0_puts(char *astring, int length); } #endif -#endif // _ARM_CPU_H +#endif /* ARM_CPU_H_ */