mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #14331 from maribu/atomic_utils
sys/atomic_utils: Functions for atomic access
This commit is contained in:
commit
792e031a95
76
cpu/arm7_common/include/atomic_utils_arch.h
Normal file
76
cpu/arm7_common/include/atomic_utils_arch.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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_arm7_common
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of fast atomic utility functions
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_UTILS_ARCH_H
|
||||
#define ATOMIC_UTILS_ARCH_H
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang provides no built-in atomic access to regular variables */
|
||||
#ifndef __clang__
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U8
|
||||
static inline uint8_t atomic_load_u8(const uint8_t *var)
|
||||
{
|
||||
return __atomic_load_1(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U16
|
||||
static inline uint16_t atomic_load_u16(const uint16_t *var)
|
||||
{
|
||||
return __atomic_load_2(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U32
|
||||
static inline uint32_t atomic_load_u32(const uint32_t *var)
|
||||
{
|
||||
return __atomic_load_4(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U8
|
||||
static inline void atomic_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
__atomic_store_1(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U16
|
||||
static inline void atomic_store_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
__atomic_store_2(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U32
|
||||
static inline void atomic_store_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
__atomic_store_4(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* __clang__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
#endif /* ATOMIC_UTILS_ARCH_H */
|
||||
/** @} */
|
52
cpu/atmega_common/include/atomic_utils_arch.h
Normal file
52
cpu/atmega_common/include/atomic_utils_arch.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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
|
||||
* @brief Implementation of fast atomic utility functions
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_UTILS_ARCH_H
|
||||
#define ATOMIC_UTILS_ARCH_H
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang provides no built-in atomic access to regular variables */
|
||||
#ifndef __clang__
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U8
|
||||
static inline uint8_t atomic_load_u8(const uint8_t *var)
|
||||
{
|
||||
return __atomic_load_1(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U8
|
||||
static inline void atomic_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
__atomic_store_1(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* __clang__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
#endif /* ATOMIC_UTILS_ARCH_H */
|
||||
/** @} */
|
193
cpu/cortexm_common/include/atomic_utils_arch.h
Normal file
193
cpu/cortexm_common/include/atomic_utils_arch.h
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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_cortexm_common
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of fast atomic utility functions
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_UTILS_ARCH_H
|
||||
#define ATOMIC_UTILS_ARCH_H
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#include "bit.h"
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang provides no built-in atomic access to regular variables */
|
||||
#ifndef __clang__
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U8
|
||||
static inline uint8_t atomic_load_u8(const uint8_t *var)
|
||||
{
|
||||
return __atomic_load_1(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U16
|
||||
static inline uint16_t atomic_load_u16(const uint16_t *var)
|
||||
{
|
||||
return __atomic_load_2(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U32
|
||||
static inline uint32_t atomic_load_u32(const uint32_t *var)
|
||||
{
|
||||
return __atomic_load_4(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U8
|
||||
static inline void atomic_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
__atomic_store_1(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U16
|
||||
static inline void atomic_store_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
__atomic_store_2(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U32
|
||||
static inline void atomic_store_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
__atomic_store_4(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* __clang__ */
|
||||
|
||||
#if CPU_HAS_BITBAND
|
||||
#define HAS_ATOMIC_BIT
|
||||
|
||||
typedef volatile uint32_t *atomic_bit_u8_t;
|
||||
typedef volatile uint32_t *atomic_bit_u16_t;
|
||||
typedef volatile uint32_t *atomic_bit_u32_t;
|
||||
typedef volatile uint32_t *atomic_bit_u64_t;
|
||||
|
||||
static inline void __attribute__((always_inline)) _bit_barrier_pre(void)
|
||||
{
|
||||
__asm__ volatile ("" : : : "memory");
|
||||
}
|
||||
|
||||
static inline void __attribute__((always_inline)) _bit_barrier_post(void)
|
||||
{
|
||||
__asm__ volatile ("" : : : "memory");
|
||||
}
|
||||
|
||||
static inline bool _is_addr_valid_for_bitbanding(void *_addr)
|
||||
{
|
||||
/* SRAM bit-band region goes from 0x20000000 to 0x200fffff,
|
||||
* peripheral bit-band region goes from 0x40000000 to 0x400fffff */
|
||||
uintptr_t addr = (uintptr_t)_addr;
|
||||
if ((addr < 0x20000000UL) || (addr > 0x400fffffUL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((addr >= 0x200fffffUL) && (addr < 0x40000000UL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline atomic_bit_u8_t atomic_bit_u8(uint8_t *dest, uint8_t bit)
|
||||
{
|
||||
assert(_is_addr_valid_for_bitbanding(dest));
|
||||
return bitband_addr(dest, bit);
|
||||
}
|
||||
|
||||
static inline atomic_bit_u16_t atomic_bit_u16(uint16_t *dest, uint8_t bit)
|
||||
{
|
||||
assert(_is_addr_valid_for_bitbanding(dest));
|
||||
return bitband_addr(dest, bit);
|
||||
}
|
||||
|
||||
static inline atomic_bit_u32_t atomic_bit_u32(uint32_t *dest, uint8_t bit)
|
||||
{
|
||||
assert(_is_addr_valid_for_bitbanding(dest));
|
||||
return bitband_addr(dest, bit);
|
||||
}
|
||||
|
||||
static inline atomic_bit_u64_t atomic_bit_u64(uint64_t *dest, uint8_t bit)
|
||||
{
|
||||
assert(_is_addr_valid_for_bitbanding(dest));
|
||||
return bitband_addr(dest, bit);
|
||||
}
|
||||
|
||||
static inline void atomic_set_bit_u8(atomic_bit_u8_t bit)
|
||||
{
|
||||
_bit_barrier_pre();
|
||||
*bit = 1;
|
||||
_bit_barrier_post();
|
||||
}
|
||||
|
||||
static inline void atomic_set_bit_u16(atomic_bit_u16_t bit)
|
||||
{
|
||||
_bit_barrier_pre();
|
||||
*bit = 1;
|
||||
_bit_barrier_post();
|
||||
}
|
||||
|
||||
static inline void atomic_set_bit_u32(atomic_bit_u32_t bit)
|
||||
{
|
||||
_bit_barrier_pre();
|
||||
*bit = 1;
|
||||
_bit_barrier_post();
|
||||
}
|
||||
|
||||
static inline void atomic_set_bit_u64(atomic_bit_u64_t bit)
|
||||
{
|
||||
_bit_barrier_pre();
|
||||
*bit = 1;
|
||||
_bit_barrier_post();
|
||||
}
|
||||
|
||||
static inline void atomic_clear_bit_u8(atomic_bit_u8_t bit)
|
||||
{
|
||||
_bit_barrier_pre();
|
||||
*bit = 0;
|
||||
_bit_barrier_post();
|
||||
}
|
||||
static inline void atomic_clear_bit_u16(atomic_bit_u16_t bit)
|
||||
{
|
||||
_bit_barrier_pre();
|
||||
*bit = 0;
|
||||
_bit_barrier_post();
|
||||
}
|
||||
|
||||
static inline void atomic_clear_bit_u32(atomic_bit_u32_t bit)
|
||||
{
|
||||
_bit_barrier_pre();
|
||||
*bit = 0;
|
||||
_bit_barrier_post();
|
||||
}
|
||||
|
||||
static inline void atomic_clear_bit_u64(atomic_bit_u64_t bit)
|
||||
{
|
||||
_bit_barrier_pre();
|
||||
*bit = 0;
|
||||
_bit_barrier_post();
|
||||
}
|
||||
|
||||
|
||||
#endif /* CPU_HAS_BITBAND */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
#endif /* ATOMIC_UTILS_ARCH_H */
|
||||
/** @} */
|
76
cpu/esp_common/include/atomic_utils_arch.h
Normal file
76
cpu/esp_common/include/atomic_utils_arch.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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_esp_common
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of fast atomic utility functions
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_UTILS_ARCH_H
|
||||
#define ATOMIC_UTILS_ARCH_H
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang provides no built-in atomic access to regular variables */
|
||||
#ifndef __clang__
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U8
|
||||
static inline uint8_t atomic_load_u8(const uint8_t *var)
|
||||
{
|
||||
return __atomic_load_1(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U16
|
||||
static inline uint16_t atomic_load_u16(const uint16_t *var)
|
||||
{
|
||||
return __atomic_load_2(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U32
|
||||
static inline uint32_t atomic_load_u32(const uint32_t *var)
|
||||
{
|
||||
return __atomic_load_4(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U8
|
||||
static inline void atomic_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
__atomic_store_1(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U16
|
||||
static inline void atomic_store_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
__atomic_store_2(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U32
|
||||
static inline void atomic_store_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
__atomic_store_4(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* __clang__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
#endif /* ATOMIC_UTILS_ARCH_H */
|
||||
/** @} */
|
76
cpu/fe310/include/atomic_utils_arch.h
Normal file
76
cpu/fe310/include/atomic_utils_arch.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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_fe310
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of fast atomic utility functions
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_UTILS_ARCH_H
|
||||
#define ATOMIC_UTILS_ARCH_H
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang provides no built-in atomic access to regular variables */
|
||||
#ifndef __clang__
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U8
|
||||
static inline uint8_t atomic_load_u8(const uint8_t *var)
|
||||
{
|
||||
return __atomic_load_1(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U16
|
||||
static inline uint16_t atomic_load_u16(const uint16_t *var)
|
||||
{
|
||||
return __atomic_load_2(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U32
|
||||
static inline uint32_t atomic_load_u32(const uint32_t *var)
|
||||
{
|
||||
return __atomic_load_4(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U8
|
||||
static inline void atomic_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
__atomic_store_1(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U16
|
||||
static inline void atomic_store_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
__atomic_store_2(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U32
|
||||
static inline void atomic_store_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
__atomic_store_4(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* __clang__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
#endif /* ATOMIC_UTILS_ARCH_H */
|
||||
/** @} */
|
76
cpu/mips32r2_common/include/atomic_utils_arch.h
Normal file
76
cpu/mips32r2_common/include/atomic_utils_arch.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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_mips32r2_common
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of fast atomic utility functions
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_UTILS_ARCH_H
|
||||
#define ATOMIC_UTILS_ARCH_H
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang provides no built-in atomic access to regular variables */
|
||||
#ifndef __clang__
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U8
|
||||
static inline uint8_t atomic_load_u8(const uint8_t *var)
|
||||
{
|
||||
return __atomic_load_1(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U16
|
||||
static inline uint16_t atomic_load_u16(const uint16_t *var)
|
||||
{
|
||||
return __atomic_load_2(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U32
|
||||
static inline uint32_t atomic_load_u32(const uint32_t *var)
|
||||
{
|
||||
return __atomic_load_4(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U8
|
||||
static inline void atomic_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
__atomic_store_1(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U16
|
||||
static inline void atomic_store_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
__atomic_store_2(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U32
|
||||
static inline void atomic_store_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
__atomic_store_4(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* __clang__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
#endif /* ATOMIC_UTILS_ARCH_H */
|
||||
/** @} */
|
64
cpu/msp430_common/include/atomic_utils_arch.h
Normal file
64
cpu/msp430_common/include/atomic_utils_arch.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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_msp430_common
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of fast atomic utility functions
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_UTILS_ARCH_H
|
||||
#define ATOMIC_UTILS_ARCH_H
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang provides no built-in atomic access to regular variables */
|
||||
#ifndef __clang__
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U8
|
||||
static inline uint8_t atomic_load_u8(const uint8_t *var)
|
||||
{
|
||||
return __atomic_load_1(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U16
|
||||
static inline uint16_t atomic_load_u16(const uint16_t *var)
|
||||
{
|
||||
return __atomic_load_2(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U8
|
||||
static inline void atomic_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
__atomic_store_1(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U16
|
||||
static inline void atomic_store_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
__atomic_store_2(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* __clang__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
#endif /* ATOMIC_UTILS_ARCH_H */
|
||||
/** @} */
|
76
cpu/native/include/atomic_utils_arch.h
Normal file
76
cpu/native/include/atomic_utils_arch.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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_native
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of fast atomic utility functions
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef ATOMIC_UTILS_ARCH_H
|
||||
#define ATOMIC_UTILS_ARCH_H
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* clang provides no built-in atomic access to regular variables */
|
||||
#ifndef __clang__
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U8
|
||||
static inline uint8_t atomic_load_u8(const uint8_t *var)
|
||||
{
|
||||
return __atomic_load_1(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U16
|
||||
static inline uint16_t atomic_load_u16(const uint16_t *var)
|
||||
{
|
||||
return __atomic_load_2(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_LOAD_U32
|
||||
static inline uint32_t atomic_load_u32(const uint32_t *var)
|
||||
{
|
||||
return __atomic_load_4(var, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U8
|
||||
static inline void atomic_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
__atomic_store_1(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U16
|
||||
static inline void atomic_store_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
__atomic_store_2(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#define HAS_ATOMIC_STORE_U32
|
||||
static inline void atomic_store_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
__atomic_store_4(dest, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* __clang__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
#endif /* ATOMIC_UTILS_ARCH_H */
|
||||
/** @} */
|
@ -4,6 +4,7 @@ PSEUDOMODULES += at_urc_isr_low
|
||||
PSEUDOMODULES += at_urc_isr_medium
|
||||
PSEUDOMODULES += at_urc_isr_highest
|
||||
PSEUDOMODULES += at24c%
|
||||
PSEUDOMODULES += atomic_utils
|
||||
PSEUDOMODULES += base64url
|
||||
PSEUDOMODULES += can_mbox
|
||||
PSEUDOMODULES += can_pm
|
||||
|
1137
sys/include/atomic_utils.h
Normal file
1137
sys/include/atomic_utils.h
Normal file
File diff suppressed because it is too large
Load Diff
7
tests/bench_sys_atomic_utils/Makefile
Normal file
7
tests/bench_sys_atomic_utils/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += atomic_utils
|
||||
USEMODULE += test_utils_interactive_sync
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
3
tests/bench_sys_atomic_utils/Makefile.ci
Normal file
3
tests/bench_sys_atomic_utils/Makefile.ci
Normal file
@ -0,0 +1,3 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
stm32f030f4-demo \
|
||||
#
|
18
tests/bench_sys_atomic_utils/README.md
Normal file
18
tests/bench_sys_atomic_utils/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Benchmark for `sys/atomic_utils`
|
||||
|
||||
This application will perform 100.000 repetitions (or 1.000.000 on
|
||||
Cortex-M7 and ESP32) for each atomic operation and will print the total time it
|
||||
took in a table. For comparison, the speed of C11 atomics and plain `volatile`
|
||||
accesses are also printed.
|
||||
|
||||
## Expectations
|
||||
|
||||
Lower is better!
|
||||
|
||||
Plain `volatile` accesses are not atomic, and therefore should perform faster
|
||||
than actual atomic operations. If atomic operations turn out to be faster
|
||||
(by more than rounding errors and noise), something is odd.
|
||||
|
||||
The `atomic_utils` aim to be at least as fast as C11 atomics and often faster.
|
||||
If the `atomic_utils` implementation performs slower than C11 atomics, you have
|
||||
found potential for further optimization.
|
504
tests/bench_sys_atomic_utils/main.c
Normal file
504
tests/bench_sys_atomic_utils/main.c
Normal file
@ -0,0 +1,504 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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 tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Atomic util benchmark
|
||||
*
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "atomic_utils.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
/* On fast CPUs: 1.000.000 loops */
|
||||
#if defined(CPU_CORE_CORTEX_M7) || defined(CPU_ESP32)
|
||||
#define LOOPS 1000000
|
||||
#else
|
||||
/* Else 100.000 loops */
|
||||
#define LOOPS 100000
|
||||
#endif
|
||||
|
||||
#define CONCAT(a, b) a ## b
|
||||
#define CONCAT3(a, b, c) a ## b ## c
|
||||
#define CONCAT4(a, b, c, d) a ## b ## c ## d
|
||||
|
||||
enum {
|
||||
IMPL_VOLATILE,
|
||||
IMPL_ATOMIC_UTIL,
|
||||
IMPL_C11_ATOMIC,
|
||||
IMPL_NUMOF
|
||||
};
|
||||
|
||||
#define BENCH_ATOMIC_STORE(name, type, c11type) \
|
||||
static void CONCAT(bench_atomic_store_, name)(uint32_t *result_us) \
|
||||
{ \
|
||||
uint32_t start, stop; \
|
||||
\
|
||||
{ \
|
||||
volatile type val; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
val = 42; \
|
||||
} \
|
||||
(void)val; \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_VOLATILE] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
type val; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
CONCAT(atomic_store_, name)(&val, 42); \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_ATOMIC_UTIL] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
c11type val; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
atomic_store(&val, 42); \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_C11_ATOMIC] = stop - start; \
|
||||
} \
|
||||
}
|
||||
BENCH_ATOMIC_STORE(u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_ATOMIC_STORE(u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_ATOMIC_STORE(u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_ATOMIC_STORE(u64, uint64_t, atomic_uint_least64_t)
|
||||
|
||||
#define BENCH_ATOMIC_LOAD(name, type, c11type) \
|
||||
static void CONCAT(bench_atomic_load_, name)(uint32_t *result_us) \
|
||||
{ \
|
||||
uint32_t start, stop; \
|
||||
\
|
||||
{ \
|
||||
volatile type val = 0; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
type tmp = val; \
|
||||
(void)tmp; \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_VOLATILE] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
type val = 0; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
type tmp = CONCAT(atomic_load_, name)(&val); \
|
||||
(void)tmp; \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_ATOMIC_UTIL] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
c11type val = ATOMIC_VAR_INIT(0); \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
type tmp = atomic_load(&val); \
|
||||
(void)tmp; \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_C11_ATOMIC] = stop - start; \
|
||||
} \
|
||||
}
|
||||
BENCH_ATOMIC_LOAD(u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_ATOMIC_LOAD(u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_ATOMIC_LOAD(u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_ATOMIC_LOAD(u64, uint64_t, atomic_uint_least64_t)
|
||||
|
||||
#define BENCH_ATOMIC_FETCH_OP(opname, op, name, type, c11type) \
|
||||
static void CONCAT4(bench_atomic_fetch_, opname, _, name)(uint32_t *result_us) \
|
||||
{ \
|
||||
uint32_t start, stop; \
|
||||
\
|
||||
{ \
|
||||
volatile type val = 0; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
val = val op 1; \
|
||||
} \
|
||||
(void)val; \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_VOLATILE] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
type val = 0; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
CONCAT4(atomic_fetch_, opname, _, name)(&val, 1); \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_ATOMIC_UTIL] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
c11type val = ATOMIC_VAR_INIT(0); \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
CONCAT(atomic_fetch_, opname)(&val, 1); \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_C11_ATOMIC] = stop - start; \
|
||||
} \
|
||||
}
|
||||
BENCH_ATOMIC_FETCH_OP(add, +, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_ATOMIC_FETCH_OP(add, +, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_ATOMIC_FETCH_OP(add, +, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_ATOMIC_FETCH_OP(add, +, u64, uint64_t, atomic_uint_least64_t)
|
||||
BENCH_ATOMIC_FETCH_OP(sub, -, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_ATOMIC_FETCH_OP(sub, -, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_ATOMIC_FETCH_OP(sub, -, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_ATOMIC_FETCH_OP(sub, -, u64, uint64_t, atomic_uint_least64_t)
|
||||
BENCH_ATOMIC_FETCH_OP(or, |, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_ATOMIC_FETCH_OP(or, |, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_ATOMIC_FETCH_OP(or, |, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_ATOMIC_FETCH_OP(or, |, u64, uint64_t, atomic_uint_least64_t)
|
||||
BENCH_ATOMIC_FETCH_OP(xor, ^, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_ATOMIC_FETCH_OP(xor, ^, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_ATOMIC_FETCH_OP(xor, ^, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_ATOMIC_FETCH_OP(xor, ^, u64, uint64_t, atomic_uint_least64_t)
|
||||
BENCH_ATOMIC_FETCH_OP(and, &, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_ATOMIC_FETCH_OP(and, &, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_ATOMIC_FETCH_OP(and, &, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_ATOMIC_FETCH_OP(and, &, u64, uint64_t, atomic_uint_least64_t)
|
||||
|
||||
#define BENCH_ATOMIC_SET_CLEAR_BIT(name, type, c11type, opname, set_or_clear) \
|
||||
static void CONCAT4(bench_atomic_, opname, _bit_, name)(uint32_t *result_us) \
|
||||
{ \
|
||||
uint32_t start, stop; \
|
||||
static const uint8_t _bit = 5; \
|
||||
type mask = ((type)1) << _bit; \
|
||||
\
|
||||
{ \
|
||||
volatile type val = 0; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
if (set_or_clear) { \
|
||||
val |= mask; \
|
||||
} \
|
||||
else { \
|
||||
val &= ~(mask); \
|
||||
} \
|
||||
} \
|
||||
(void)val; \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_VOLATILE] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
static type val = 0; \
|
||||
CONCAT3(atomic_bit_, name, _t) bit = \
|
||||
CONCAT(atomic_bit_, name)(&val, _bit); \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
CONCAT4(atomic_, opname, _bit_, name)(bit); \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_ATOMIC_UTIL] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
c11type val = ATOMIC_VAR_INIT(0); \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
if (set_or_clear) { \
|
||||
atomic_fetch_or(&val, mask); \
|
||||
} \
|
||||
else { \
|
||||
atomic_fetch_and(&val, ~(mask)); \
|
||||
} \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_C11_ATOMIC] = stop - start; \
|
||||
} \
|
||||
}
|
||||
BENCH_ATOMIC_SET_CLEAR_BIT(u8, uint8_t, atomic_uint_least8_t, set, 1)
|
||||
BENCH_ATOMIC_SET_CLEAR_BIT(u16, uint16_t, atomic_uint_least16_t, set, 1)
|
||||
BENCH_ATOMIC_SET_CLEAR_BIT(u32, uint32_t, atomic_uint_least32_t, set, 1)
|
||||
BENCH_ATOMIC_SET_CLEAR_BIT(u64, uint64_t, atomic_uint_least64_t, set, 1)
|
||||
BENCH_ATOMIC_SET_CLEAR_BIT(u8, uint8_t, atomic_uint_least8_t, clear, 0)
|
||||
BENCH_ATOMIC_SET_CLEAR_BIT(u16, uint16_t, atomic_uint_least16_t, clear, 0)
|
||||
BENCH_ATOMIC_SET_CLEAR_BIT(u32, uint32_t, atomic_uint_least32_t, clear, 0)
|
||||
BENCH_ATOMIC_SET_CLEAR_BIT(u64, uint64_t, atomic_uint_least64_t, clear, 0)
|
||||
|
||||
#define BENCH_SEMI_ATOMIC_FETCH_OP(opname, op, name, type, c11type) \
|
||||
static void CONCAT4(bench_semi_atomic_fetch_, opname, _, name)(uint32_t *result_us) \
|
||||
{ \
|
||||
uint32_t start, stop; \
|
||||
\
|
||||
{ \
|
||||
volatile type val = 0; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
val = val op 1; \
|
||||
} \
|
||||
(void)val; \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_VOLATILE] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
type val = 0; \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
CONCAT4(semi_atomic_fetch_, opname, _, name)(&val, 1); \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_ATOMIC_UTIL] = stop - start; \
|
||||
} \
|
||||
\
|
||||
{ \
|
||||
c11type val = ATOMIC_VAR_INIT(0); \
|
||||
start = xtimer_now_usec(); \
|
||||
for (uint32_t i = 0; i < LOOPS; i++) { \
|
||||
CONCAT(atomic_fetch_, opname)(&val, 1); \
|
||||
} \
|
||||
stop = xtimer_now_usec(); \
|
||||
result_us[IMPL_C11_ATOMIC] = stop - start; \
|
||||
} \
|
||||
}
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(add, +, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(add, +, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(add, +, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(add, +, u64, uint64_t, atomic_uint_least64_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(sub, -, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(sub, -, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(sub, -, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(sub, -, u64, uint64_t, atomic_uint_least64_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(or, |, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(or, |, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(or, |, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(or, |, u64, uint64_t, atomic_uint_least64_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(xor, ^, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(xor, ^, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(xor, ^, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(xor, ^, u64, uint64_t, atomic_uint_least64_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(and, &, u8, uint8_t, atomic_uint_least8_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(and, &, u16, uint16_t, atomic_uint_least16_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(and, &, u32, uint32_t, atomic_uint_least32_t)
|
||||
BENCH_SEMI_ATOMIC_FETCH_OP(and, &, u64, uint64_t, atomic_uint_least64_t)
|
||||
|
||||
#define LINE "+------+----------+------+------------------+" \
|
||||
"------------------+------------------+"
|
||||
#define FMT "| %4s | %8s | %4u | %13" PRIu32 " µs | %13" PRIu32 " µs | " \
|
||||
"%13" PRIu32 " µs |\n"
|
||||
int main(void)
|
||||
{
|
||||
uint32_t results[IMPL_NUMOF];
|
||||
|
||||
puts("Note: LOWER IS BETTER!\n");
|
||||
puts(LINE);
|
||||
printf("| %4s | %8s | %4s | %16s | %16s | %16s |\n",
|
||||
"mode", "op", "bits", "volatile", "atomic util", "c11 atomic");
|
||||
puts(LINE);
|
||||
|
||||
bench_atomic_store_u8(results);
|
||||
printf(FMT, "atom", "store", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_store_u16(results);
|
||||
printf(FMT, "atom", "store", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_store_u32(results);
|
||||
printf(FMT, "atom", "store", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_store_u64(results);
|
||||
printf(FMT, "atom", "store", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_atomic_load_u8(results);
|
||||
printf(FMT, "atom", "load", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_load_u16(results);
|
||||
printf(FMT, "atom", "load", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_load_u32(results);
|
||||
printf(FMT, "atom", "load", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_load_u64(results);
|
||||
printf(FMT, "atom", "load", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
/* atomic read-modify-write operations */
|
||||
bench_atomic_fetch_add_u8(results);
|
||||
printf(FMT, "atom", "add", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_add_u16(results);
|
||||
printf(FMT, "atom", "add", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_add_u32(results);
|
||||
printf(FMT, "atom", "add", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_add_u64(results);
|
||||
printf(FMT, "atom", "add", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_atomic_fetch_sub_u8(results);
|
||||
printf(FMT, "atom", "sub", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_sub_u16(results);
|
||||
printf(FMT, "atom", "sub", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_sub_u32(results);
|
||||
printf(FMT, "atom", "sub", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_sub_u64(results);
|
||||
printf(FMT, "atom", "sub", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_atomic_fetch_or_u8(results);
|
||||
printf(FMT, "atom", "or", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_or_u16(results);
|
||||
printf(FMT, "atom", "or", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_or_u32(results);
|
||||
printf(FMT, "atom", "or", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_or_u64(results);
|
||||
printf(FMT, "atom", "or", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_atomic_fetch_xor_u8(results);
|
||||
printf(FMT, "atom", "xor", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_xor_u16(results);
|
||||
printf(FMT, "atom", "xor", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_xor_u32(results);
|
||||
printf(FMT, "atom", "xor", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_xor_u64(results);
|
||||
printf(FMT, "atom", "xor", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_atomic_fetch_and_u8(results);
|
||||
printf(FMT, "atom", "and", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_and_u16(results);
|
||||
printf(FMT, "atom", "and", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_and_u32(results);
|
||||
printf(FMT, "atom", "and", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_fetch_and_u64(results);
|
||||
printf(FMT, "atom", "and", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
/* atomic bit setting and clearing */
|
||||
bench_atomic_set_bit_u8(results);
|
||||
printf(FMT, "atom", "set", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_set_bit_u16(results);
|
||||
printf(FMT, "atom", "set", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_set_bit_u32(results);
|
||||
printf(FMT, "atom", "set", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_set_bit_u64(results);
|
||||
printf(FMT, "atom", "set", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_atomic_clear_bit_u8(results);
|
||||
printf(FMT, "atom", "clear", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_clear_bit_u16(results);
|
||||
printf(FMT, "atom", "clear", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_clear_bit_u32(results);
|
||||
printf(FMT, "atom", "clear", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_atomic_clear_bit_u64(results);
|
||||
printf(FMT, "atom", "clear", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
/* semi-atomic read-modify-write operations */
|
||||
bench_semi_atomic_fetch_add_u8(results);
|
||||
printf(FMT, "semi", "add", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_add_u16(results);
|
||||
printf(FMT, "semi", "add", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_add_u32(results);
|
||||
printf(FMT, "semi", "add", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_add_u64(results);
|
||||
printf(FMT, "semi", "add", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_semi_atomic_fetch_sub_u8(results);
|
||||
printf(FMT, "semi", "sub", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_sub_u16(results);
|
||||
printf(FMT, "semi", "sub", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_sub_u32(results);
|
||||
printf(FMT, "semi", "sub", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_sub_u64(results);
|
||||
printf(FMT, "semi", "sub", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_semi_atomic_fetch_or_u8(results);
|
||||
printf(FMT, "semi", "or", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_or_u16(results);
|
||||
printf(FMT, "semi", "or", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_or_u32(results);
|
||||
printf(FMT, "semi", "or", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_or_u64(results);
|
||||
printf(FMT, "semi", "or", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_semi_atomic_fetch_xor_u8(results);
|
||||
printf(FMT, "semi", "xor", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_xor_u16(results);
|
||||
printf(FMT, "semi", "xor", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_xor_u32(results);
|
||||
printf(FMT, "semi", "xor", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_xor_u64(results);
|
||||
printf(FMT, "semi", "xor", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
|
||||
bench_semi_atomic_fetch_and_u8(results);
|
||||
printf(FMT, "semi", "and", 8, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_and_u16(results);
|
||||
printf(FMT, "semi", "and", 16, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_and_u32(results);
|
||||
printf(FMT, "semi", "and", 32, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
bench_semi_atomic_fetch_and_u64(results);
|
||||
printf(FMT, "semi", "and", 64, results[IMPL_VOLATILE],
|
||||
results[IMPL_ATOMIC_UTIL], results[IMPL_C11_ATOMIC]);
|
||||
puts(LINE);
|
||||
return 0;
|
||||
}
|
9
tests/sys_atomic_utils/Makefile
Normal file
9
tests/sys_atomic_utils/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += atomic_utils
|
||||
USEMODULE += fmt
|
||||
USEMODULE += random
|
||||
USEMODULE += shell
|
||||
USEMODULE += xtimer
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
10
tests/sys_atomic_utils/Makefile.ci
Normal file
10
tests/sys_atomic_utils/Makefile.ci
Normal file
@ -0,0 +1,10 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-leonardo \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
nucleo-f031k6 \
|
||||
nucleo-l011k4 \
|
||||
stm32f030f4-demo \
|
||||
#
|
87
tests/sys_atomic_utils/README.md
Normal file
87
tests/sys_atomic_utils/README.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Test Application for `sys/atomic_utils`
|
||||
|
||||
## Design of the Test
|
||||
|
||||
This test application will launch one worker and one tester thread. The
|
||||
worker thread will perform a specific operation (such as
|
||||
`atomic_fetch_add_u32()`) over and over again on a single target variable, and
|
||||
the tester thread occasionally interrupts to verify the target variable's value
|
||||
is valid. If the variable has corrupted, this is reported.
|
||||
|
||||
This test works only *statistically*. Absence of detected corruption does not
|
||||
guarantee, that a specific function indeed works correctly. (But a detected
|
||||
corruptions guarantees that something is indeed broken.) However, the longer
|
||||
the tests runs the higher the odds are that a malfunctioning implementation is
|
||||
indeed caught corrupting memory.
|
||||
|
||||
## Types of Corruptions Tested For
|
||||
|
||||
### Lost Update
|
||||
|
||||
In the lost update the worker thread will perform an operation that has no
|
||||
effect (addition, subtraction, binary or, and binary xor with `0` as
|
||||
second parameter, or binary and with `0xff...`). The checker thread will
|
||||
atomically increment the target value. If in the next iteration the of the
|
||||
checker thread the value has changed, a corruption is detected. This could
|
||||
happen e.g. by
|
||||
|
||||
```
|
||||
Worker Thread | Checker Thread | Value of t
|
||||
| |
|
||||
| t++ | 1
|
||||
reg1 = t | | 1
|
||||
reg1 += 0 | | 1
|
||||
| t++ | 2
|
||||
t = reg1 | | 1
|
||||
```
|
||||
|
||||
Here, the read-modify-write sequence (`reg1 = t; reg1 += 0; t = reg1;`) has
|
||||
been interrupted by the Checker Thread. The update of `t` (the atomic `t++`
|
||||
operation) is afterwards lost, when the Worker Thread writes `reg1` into
|
||||
`t`. Such a lost update proves that the read-modify-write operation was not
|
||||
atomic.
|
||||
|
||||
Note: Only the `atomic_<op>_u<width>` family of functions must pass this test.
|
||||
A failure for the other families does ***not*** indicate an issue.
|
||||
|
||||
### Store Tearing
|
||||
|
||||
In the tearing test the worker thread will first initialize the target variable,
|
||||
e.g. with zero. Then, a sequence of read-modify-write operations is performed,
|
||||
e.g. 3 times `atomic_fetch_add_u16(&target, 0x5555)`. During this sequence, only
|
||||
the target variable should contain on of the following values:
|
||||
`0x0000`, `0x5555`, `0xaaaa`, and `0xffff`.
|
||||
|
||||
After each sequence is complete, the target variable will be atomically
|
||||
re-initialized and the next sequence starts. If e.g. on AVR the write is
|
||||
interrupted after only one byte is written (AVR is an 8-bit platform and only
|
||||
can write 8 bits per store), e.g. a value of `0x55aa` or `0xaa55` might be
|
||||
stored in the target variable. If such an value is observed, an atomic store
|
||||
operation was torn apart into two parts and a memory corruption was detected.
|
||||
|
||||
Note: Both the `atomic_<op>_u<width>` and `semi_atomic_<op>_u<width>` families
|
||||
of functions must pass this test. A failure of the
|
||||
`volatile_<op>_u<width>` family of functions does ***not*** indicate an
|
||||
issue.
|
||||
|
||||
## Usage
|
||||
|
||||
The test will drop you into a shell. The welcome message and the help command
|
||||
contain all information on how to use the test. In addition, `make test` will
|
||||
run all tests that are expected to pass for one second each. This is hopefully
|
||||
long enough to detect any issues. It is certainly not possible to run the test
|
||||
longer in automated tests.
|
||||
|
||||
## Test Self Check
|
||||
|
||||
The test brings an alternative implementation of the `atomic_<op>_u<width>`
|
||||
family of functions called `volatile_<op>_u<width>`. This implementation
|
||||
incorrectly assumes that `volatile` provides atomic access. Thus, checking
|
||||
this implementation should result in failures:
|
||||
|
||||
- The lost update test is expected to (eventually) fail for every platform
|
||||
- The tearing test is expected for width bigger than the word size
|
||||
- Cortex-M7 is one exception: Due to instruction fusion two 32 bit writes
|
||||
can be issued in one CPU cycle, so that a 64 bit write can indeed be
|
||||
atomic for this platform. Thus, it could happen that no tearing failure
|
||||
is detected for the `volatile` implementation on Cortex-M7 at all.
|
1361
tests/sys_atomic_utils/main.c
Normal file
1361
tests/sys_atomic_utils/main.c
Normal file
File diff suppressed because it is too large
Load Diff
35
tests/sys_atomic_utils/tests/01-run.py
Executable file
35
tests/sys_atomic_utils/tests/01-run.py
Executable file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
#
|
||||
# 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.
|
||||
|
||||
# @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
fns = ["fetch_add", "fetch_sub", "fetch_or", "fetch_xor", "fetch_and"]
|
||||
postfixes = ["_u8", "_u16", "_u32", "_u64"]
|
||||
tests = ["tearing_test", "lost_update_test"]
|
||||
prefixes = {
|
||||
"tearing_test": ["atomic_", "semi_atomic_"],
|
||||
"lost_update_test": ["atomic_"]
|
||||
}
|
||||
timeout = "1"
|
||||
|
||||
for test in tests:
|
||||
for prefix in prefixes[test]:
|
||||
for postfix in postfixes:
|
||||
for fn in fns:
|
||||
child.sendline(test + " " + prefix + fn + postfix + " "
|
||||
+ timeout)
|
||||
child.expect("OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc))
|
192
tests/sys_atomic_utils/volatile_utils.h
Normal file
192
tests/sys_atomic_utils/volatile_utils.h
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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 tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief For comparison: "Atomic" accesses using volatile
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*
|
||||
* This header implements the `volatile_*()` family of functions of
|
||||
* @ref sys_volatile_utils with `volatile_` instead of `volatile_` as prefix.
|
||||
* These implementation rely on the `volatile` type qualifier for implementing
|
||||
* "atomic" accesses; which in many cases will not result in atomic operations.
|
||||
* So this is a known to be ***BROKEN*** implementation. Its sole purpose is
|
||||
* to verify that the tests does detect broken implementations. Do not use
|
||||
* these functions for anything else but testing ;-)
|
||||
*/
|
||||
|
||||
#ifndef VOLATILE_UTILS_H
|
||||
#define VOLATILE_UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline uint8_t volatile_load_u8(const uint8_t *var)
|
||||
{
|
||||
return *((const volatile uint8_t *)var);
|
||||
}
|
||||
static inline uint16_t volatile_load_u16(const uint16_t *var)
|
||||
{
|
||||
return *((const volatile uint16_t *)var);
|
||||
}
|
||||
static inline uint32_t volatile_load_u32(const uint32_t *var)
|
||||
{
|
||||
return *((const volatile uint32_t *)var);
|
||||
}
|
||||
static inline uint64_t volatile_load_u64(const uint64_t *var)
|
||||
{
|
||||
return *((const volatile uint64_t *)var);
|
||||
}
|
||||
|
||||
static inline void volatile_store_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
*((volatile uint8_t *)dest) = val;
|
||||
}
|
||||
static inline void volatile_store_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
*((volatile uint16_t *)dest) = val;
|
||||
}
|
||||
static inline void volatile_store_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
*((volatile uint32_t *)dest) = val;
|
||||
}
|
||||
static inline void volatile_store_u64(uint64_t *dest, uint64_t val)
|
||||
{
|
||||
*((volatile uint64_t *)dest) = val;
|
||||
}
|
||||
|
||||
static inline void volatile_fetch_add_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
*((volatile uint8_t *)dest) += val;
|
||||
}
|
||||
static inline void volatile_fetch_sub_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
*((volatile uint8_t *)dest) -= val;
|
||||
}
|
||||
static inline void volatile_fetch_or_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
*((volatile uint8_t *)dest) |= val;
|
||||
}
|
||||
static inline void volatile_fetch_xor_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
*((volatile uint8_t *)dest) ^= val;
|
||||
}
|
||||
static inline void volatile_fetch_and_u8(uint8_t *dest, uint8_t val)
|
||||
{
|
||||
*((volatile uint8_t *)dest) &= val;
|
||||
}
|
||||
|
||||
static inline void volatile_fetch_add_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
*((volatile uint16_t *)dest) += val;
|
||||
}
|
||||
static inline void volatile_fetch_sub_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
*((volatile uint16_t *)dest) -= val;
|
||||
}
|
||||
static inline void volatile_fetch_or_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
*((volatile uint16_t *)dest) |= val;
|
||||
}
|
||||
static inline void volatile_fetch_xor_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
*((volatile uint16_t *)dest) ^= val;
|
||||
}
|
||||
static inline void volatile_fetch_and_u16(uint16_t *dest, uint16_t val)
|
||||
{
|
||||
*((volatile uint16_t *)dest) &= val;
|
||||
}
|
||||
|
||||
static inline void volatile_fetch_add_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
*((volatile uint32_t *)dest) += val;
|
||||
}
|
||||
static inline void volatile_fetch_sub_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
*((volatile uint32_t *)dest) -= val;
|
||||
}
|
||||
static inline void volatile_fetch_or_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
*((volatile uint32_t *)dest) |= val;
|
||||
}
|
||||
static inline void volatile_fetch_xor_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
*((volatile uint32_t *)dest) ^= val;
|
||||
}
|
||||
static inline void volatile_fetch_and_u32(uint32_t *dest, uint32_t val)
|
||||
{
|
||||
*((volatile uint32_t *)dest) &= val;
|
||||
}
|
||||
|
||||
static inline void volatile_fetch_add_u64(uint64_t *dest, uint64_t val)
|
||||
{
|
||||
*((volatile uint64_t *)dest) += val;
|
||||
}
|
||||
static inline void volatile_fetch_sub_u64(uint64_t *dest, uint64_t val)
|
||||
{
|
||||
*((volatile uint64_t *)dest) -= val;
|
||||
}
|
||||
static inline void volatile_fetch_or_u64(uint64_t *dest, uint64_t val)
|
||||
{
|
||||
*((volatile uint64_t *)dest) |= val;
|
||||
}
|
||||
static inline void volatile_fetch_xor_u64(uint64_t *dest, uint64_t val)
|
||||
{
|
||||
*((volatile uint64_t *)dest) ^= val;
|
||||
}
|
||||
static inline void volatile_fetch_and_u64(uint64_t *dest, uint64_t val)
|
||||
{
|
||||
*((volatile uint64_t *)dest) &= val;
|
||||
}
|
||||
|
||||
static inline void volatile_set_bit_u8(uint8_t *mask, uint8_t bit)
|
||||
{
|
||||
*((volatile uint8_t *)mask) |= 1 << bit;
|
||||
}
|
||||
static inline void volatile_set_bit_u16(uint16_t *mask, uint8_t bit)
|
||||
{
|
||||
*((volatile uint16_t *)mask) |= 1 << bit;
|
||||
}
|
||||
static inline void volatile_set_bit_u32(uint32_t *mask, uint8_t bit)
|
||||
{
|
||||
*((volatile uint32_t *)mask) |= 1UL << bit;
|
||||
}
|
||||
static inline void volatile_set_bit_u64(uint64_t *mask, uint8_t bit)
|
||||
{
|
||||
*((volatile uint64_t *)mask) |= 1ULL << bit;
|
||||
}
|
||||
|
||||
static inline void volatile_clear_bit_u8(uint8_t *mask, uint8_t bit)
|
||||
{
|
||||
*((volatile uint8_t *)mask) &= ~(1 << bit);
|
||||
}
|
||||
static inline void volatile_clear_bit_u16(uint16_t *mask, uint8_t bit)
|
||||
{
|
||||
*((volatile uint16_t *)mask) &= ~(1 << bit);
|
||||
}
|
||||
static inline void volatile_clear_bit_u32(uint32_t *mask, uint8_t bit)
|
||||
{
|
||||
*((volatile uint32_t *)mask) &= ~(1UL << bit);
|
||||
}
|
||||
static inline void volatile_clear_bit_u64(uint64_t *mask, uint8_t bit)
|
||||
{
|
||||
*((volatile uint64_t *)mask) &= ~(1ULL << bit);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VOLATILE_UTILS_H */
|
||||
/** @} */
|
1
tests/unittests/tests-atomic_utils/Makefile
Normal file
1
tests/unittests/tests-atomic_utils/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
2
tests/unittests/tests-atomic_utils/Makefile.include
Normal file
2
tests/unittests/tests-atomic_utils/Makefile.include
Normal file
@ -0,0 +1,2 @@
|
||||
USEMODULE += atomic_utils
|
||||
USEMODULE += random # <-- used for input data to operate on
|
286
tests/unittests/tests-atomic_utils/tests-atomic_utils.c
Normal file
286
tests/unittests/tests-atomic_utils/tests-atomic_utils.c
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Unittests for the atomic utils module
|
||||
*
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "embUnit.h"
|
||||
#include "tests-atomic_utils.h"
|
||||
|
||||
#include "atomic_utils.h"
|
||||
#include "random.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
typedef void (*fetch_op_u8_t)(uint8_t *dest, uint8_t val);
|
||||
typedef void (*fetch_op_u16_t)(uint16_t *dest, uint16_t val);
|
||||
typedef void (*fetch_op_u32_t)(uint32_t *dest, uint32_t val);
|
||||
typedef void (*fetch_op_u64_t)(uint64_t *dest, uint64_t val);
|
||||
|
||||
static void fetch_add_u8(uint8_t *dest, uint8_t val){ *dest += val; }
|
||||
static void fetch_add_u16(uint16_t *dest, uint16_t val){ *dest += val; }
|
||||
static void fetch_add_u32(uint32_t *dest, uint32_t val){ *dest += val; }
|
||||
static void fetch_add_u64(uint64_t *dest, uint64_t val){ *dest += val; }
|
||||
|
||||
static void fetch_sub_u8(uint8_t *dest, uint8_t val){ *dest -= val; }
|
||||
static void fetch_sub_u16(uint16_t *dest, uint16_t val){ *dest -= val; }
|
||||
static void fetch_sub_u32(uint32_t *dest, uint32_t val){ *dest -= val; }
|
||||
static void fetch_sub_u64(uint64_t *dest, uint64_t val){ *dest -= val; }
|
||||
|
||||
static void fetch_or_u8(uint8_t *dest, uint8_t val){ *dest |= val; }
|
||||
static void fetch_or_u16(uint16_t *dest, uint16_t val){ *dest |= val; }
|
||||
static void fetch_or_u32(uint32_t *dest, uint32_t val){ *dest |= val; }
|
||||
static void fetch_or_u64(uint64_t *dest, uint64_t val){ *dest |= val; }
|
||||
|
||||
static void fetch_xor_u8(uint8_t *dest, uint8_t val){ *dest ^= val; }
|
||||
static void fetch_xor_u16(uint16_t *dest, uint16_t val){ *dest ^= val; }
|
||||
static void fetch_xor_u32(uint32_t *dest, uint32_t val){ *dest ^= val; }
|
||||
static void fetch_xor_u64(uint64_t *dest, uint64_t val){ *dest ^= val; }
|
||||
|
||||
static void fetch_and_u8(uint8_t *dest, uint8_t val){ *dest &= val; }
|
||||
static void fetch_and_u16(uint16_t *dest, uint16_t val){ *dest &= val; }
|
||||
static void fetch_and_u32(uint32_t *dest, uint32_t val){ *dest &= val; }
|
||||
static void fetch_and_u64(uint64_t *dest, uint64_t val){ *dest &= val; }
|
||||
|
||||
static void test_load_store(void)
|
||||
{
|
||||
uint8_t u8 = 42;
|
||||
atomic_store_u8(&u8, 13);
|
||||
TEST_ASSERT_EQUAL_INT(atomic_load_u8(&u8), 13);
|
||||
|
||||
uint16_t u16 = 42;
|
||||
atomic_store_u16(&u16, 1337);
|
||||
TEST_ASSERT_EQUAL_INT(atomic_load_u16(&u16), 1337);
|
||||
|
||||
uint32_t u32 = 42;
|
||||
atomic_store_u32(&u32, 0x13371337);
|
||||
TEST_ASSERT_EQUAL_INT(atomic_load_u32(&u32), 0x13371337);
|
||||
|
||||
uint64_t u64 = 42;
|
||||
atomic_store_u64(&u64, 0x1337133713371337);
|
||||
TEST_ASSERT_EQUAL_INT(atomic_load_u64(&u64), 0x1337133713371337);
|
||||
}
|
||||
|
||||
static void test_fetch_op_u8(fetch_op_u8_t atomic_op, fetch_op_u8_t op)
|
||||
{
|
||||
uint8_t state1 = 0, state2 = 0;
|
||||
uint8_t i = 0;
|
||||
do {
|
||||
atomic_op(&state1, i);
|
||||
op(&state2, i);
|
||||
TEST_ASSERT_EQUAL_INT(state1, state2);
|
||||
i++;
|
||||
} while (i);
|
||||
}
|
||||
|
||||
static void test_fetch_ops_u8(void)
|
||||
{
|
||||
test_fetch_op_u8(atomic_fetch_add_u8, fetch_add_u8);
|
||||
test_fetch_op_u8(atomic_fetch_sub_u8, fetch_sub_u8);
|
||||
test_fetch_op_u8(atomic_fetch_or_u8, fetch_or_u8);
|
||||
test_fetch_op_u8(atomic_fetch_xor_u8, fetch_xor_u8);
|
||||
test_fetch_op_u8(atomic_fetch_and_u8, fetch_and_u8);
|
||||
test_fetch_op_u8(semi_atomic_fetch_add_u8, fetch_add_u8);
|
||||
test_fetch_op_u8(semi_atomic_fetch_sub_u8, fetch_sub_u8);
|
||||
test_fetch_op_u8(semi_atomic_fetch_or_u8, fetch_or_u8);
|
||||
test_fetch_op_u8(semi_atomic_fetch_xor_u8, fetch_xor_u8);
|
||||
test_fetch_op_u8(semi_atomic_fetch_and_u8, fetch_and_u8);
|
||||
}
|
||||
|
||||
static void test_fetch_op_u16(fetch_op_u16_t atomic_op, fetch_op_u16_t op)
|
||||
{
|
||||
uint16_t state1 = 0, state2 = 0;
|
||||
uint8_t i = 0;
|
||||
do {
|
||||
uint16_t num = random_uint32();
|
||||
atomic_op(&state1, num);
|
||||
op(&state2, num);
|
||||
TEST_ASSERT_EQUAL_INT(state1, state2);
|
||||
i++;
|
||||
} while (i);
|
||||
}
|
||||
|
||||
static void test_fetch_ops_u16(void)
|
||||
{
|
||||
test_fetch_op_u16(atomic_fetch_add_u16, fetch_add_u16);
|
||||
test_fetch_op_u16(atomic_fetch_sub_u16, fetch_sub_u16);
|
||||
test_fetch_op_u16(atomic_fetch_or_u16, fetch_or_u16);
|
||||
test_fetch_op_u16(atomic_fetch_xor_u16, fetch_xor_u16);
|
||||
test_fetch_op_u16(atomic_fetch_and_u16, fetch_and_u16);
|
||||
test_fetch_op_u16(semi_atomic_fetch_add_u16, fetch_add_u16);
|
||||
test_fetch_op_u16(semi_atomic_fetch_sub_u16, fetch_sub_u16);
|
||||
test_fetch_op_u16(semi_atomic_fetch_or_u16, fetch_or_u16);
|
||||
test_fetch_op_u16(semi_atomic_fetch_xor_u16, fetch_xor_u16);
|
||||
test_fetch_op_u16(semi_atomic_fetch_and_u16, fetch_and_u16);
|
||||
}
|
||||
|
||||
static void test_fetch_op_u32(fetch_op_u32_t atomic_op, fetch_op_u32_t op)
|
||||
{
|
||||
uint32_t state1 = 0, state2 = 0;
|
||||
uint8_t i = 0;
|
||||
do {
|
||||
uint32_t num = random_uint32();
|
||||
atomic_op(&state1, num);
|
||||
op(&state2, num);
|
||||
TEST_ASSERT_EQUAL_INT(state1, state2);
|
||||
i++;
|
||||
} while (i);
|
||||
}
|
||||
|
||||
static void test_fetch_ops_u32(void)
|
||||
{
|
||||
test_fetch_op_u32(atomic_fetch_add_u32, fetch_add_u32);
|
||||
test_fetch_op_u32(atomic_fetch_sub_u32, fetch_sub_u32);
|
||||
test_fetch_op_u32(atomic_fetch_or_u32, fetch_or_u32);
|
||||
test_fetch_op_u32(atomic_fetch_xor_u32, fetch_xor_u32);
|
||||
test_fetch_op_u32(atomic_fetch_and_u32, fetch_and_u32);
|
||||
test_fetch_op_u32(semi_atomic_fetch_add_u32, fetch_add_u32);
|
||||
test_fetch_op_u32(semi_atomic_fetch_sub_u32, fetch_sub_u32);
|
||||
test_fetch_op_u32(semi_atomic_fetch_or_u32, fetch_or_u32);
|
||||
test_fetch_op_u32(semi_atomic_fetch_xor_u32, fetch_xor_u32);
|
||||
test_fetch_op_u32(semi_atomic_fetch_and_u32, fetch_and_u32);
|
||||
}
|
||||
|
||||
static void test_fetch_op_u64(fetch_op_u64_t atomic_op, fetch_op_u64_t op)
|
||||
{
|
||||
uint64_t state1 = 0, state2 = 0;
|
||||
uint8_t i = 0;
|
||||
do {
|
||||
uint64_t num;
|
||||
random_bytes((void *)&num, sizeof(num));
|
||||
atomic_op(&state1, num);
|
||||
op(&state2, num);
|
||||
TEST_ASSERT_EQUAL_INT(state1, state2);
|
||||
i++;
|
||||
} while (i);
|
||||
}
|
||||
|
||||
static void test_fetch_ops_u64(void)
|
||||
{
|
||||
test_fetch_op_u64(atomic_fetch_add_u64, fetch_add_u64);
|
||||
test_fetch_op_u64(atomic_fetch_sub_u64, fetch_sub_u64);
|
||||
test_fetch_op_u64(atomic_fetch_or_u64, fetch_or_u64);
|
||||
test_fetch_op_u64(atomic_fetch_xor_u64, fetch_xor_u64);
|
||||
test_fetch_op_u64(atomic_fetch_and_u64, fetch_and_u64);
|
||||
test_fetch_op_u64(semi_atomic_fetch_add_u64, fetch_add_u64);
|
||||
test_fetch_op_u64(semi_atomic_fetch_sub_u64, fetch_sub_u64);
|
||||
test_fetch_op_u64(semi_atomic_fetch_or_u64, fetch_or_u64);
|
||||
test_fetch_op_u64(semi_atomic_fetch_xor_u64, fetch_xor_u64);
|
||||
test_fetch_op_u64(semi_atomic_fetch_and_u64, fetch_and_u64);
|
||||
}
|
||||
|
||||
static void test_atomic_set_bit(void)
|
||||
{
|
||||
{
|
||||
uint8_t val1 = 0, val2 = 0;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
atomic_set_bit_u8(atomic_bit_u8(&val1, i));
|
||||
val2 |= 1ULL << i;
|
||||
TEST_ASSERT_EQUAL_INT(val2, val1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint16_t val1 = 0, val2 = 0;
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
atomic_set_bit_u16(atomic_bit_u16(&val1, i));
|
||||
val2 |= 1ULL << i;
|
||||
TEST_ASSERT_EQUAL_INT(val2, val1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t val1 = 0, val2 = 0;
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
atomic_set_bit_u32(atomic_bit_u32(&val1, i));
|
||||
val2 |= 1ULL << i;
|
||||
TEST_ASSERT_EQUAL_INT(val2, val1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint64_t val1 = 0, val2 = 0;
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
atomic_set_bit_u64(atomic_bit_u64(&val1, i));
|
||||
val2 |= 1ULL << i;
|
||||
TEST_ASSERT(val2 == val1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_atomic_clear_bit(void)
|
||||
{
|
||||
{
|
||||
uint8_t val1 = 0xff, val2 = 0xff;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
atomic_clear_bit_u8(atomic_bit_u8(&val1, i));
|
||||
val2 &= ~(1ULL << i);
|
||||
TEST_ASSERT_EQUAL_INT(val2, val1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint16_t val1 = 0xffff, val2 = 0xffff;
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
atomic_clear_bit_u16(atomic_bit_u16(&val1, i));
|
||||
val2 &= ~(1ULL << i);
|
||||
TEST_ASSERT_EQUAL_INT(val2, val1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t val1 = 0xffffffff, val2 = 0xffffffff;
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
atomic_clear_bit_u32(atomic_bit_u32(&val1, i));
|
||||
val2 &= ~(1ULL << i);
|
||||
TEST_ASSERT_EQUAL_INT(val2, val1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint64_t val1 = 0xffffffffffffffff, val2 = 0xffffffffffffffff;
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
atomic_clear_bit_u64(atomic_bit_u64(&val1, i));
|
||||
val2 &= ~(1ULL << i);
|
||||
TEST_ASSERT(val2 == val1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Test *tests_atomic_utils_tests(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(test_load_store),
|
||||
new_TestFixture(test_fetch_ops_u8),
|
||||
new_TestFixture(test_fetch_ops_u16),
|
||||
new_TestFixture(test_fetch_ops_u32),
|
||||
new_TestFixture(test_fetch_ops_u64),
|
||||
new_TestFixture(test_atomic_set_bit),
|
||||
new_TestFixture(test_atomic_clear_bit),
|
||||
};
|
||||
|
||||
EMB_UNIT_TESTCALLER(atomic_utils_tests, NULL, NULL, fixtures);
|
||||
|
||||
return (Test *)&atomic_utils_tests;
|
||||
}
|
||||
|
||||
void tests_atomic_utils(void)
|
||||
{
|
||||
TESTS_RUN(tests_atomic_utils_tests());
|
||||
}
|
38
tests/unittests/tests-atomic_utils/tests-atomic_utils.h
Normal file
38
tests/unittests/tests-atomic_utils/tests-atomic_utils.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup unittests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Unittests for the atomic util module
|
||||
*
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*/
|
||||
|
||||
#ifndef TESTS_ATOMIC_UTILS_H
|
||||
#define TESTS_ATOMIC_UTILS_H
|
||||
|
||||
#include "embUnit.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The entry point of this test suite.
|
||||
*/
|
||||
void tests_atomic_util(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* TESTS_ATOMIC_UTILS_H */
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user