include: riscv: add RISC-V arch atomic instructions
Add RISC-V architecture specific atomic instructions, not present in atomic_builtin.h Signed-off-by: Aleksandar Cecaric <aleksandar.cecaric@nextsilicon.com>
This commit is contained in:
parent
03d1eabc32
commit
0f1d93cb4f
5 changed files with 236 additions and 0 deletions
149
include/zephyr/arch/riscv/atomic.h
Normal file
149
include/zephyr/arch/riscv/atomic.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* Copyright (c) 2024 NextSilicon
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_ARCH_RISCV_ATOMIC_H_
|
||||
#define ZEPHYR_INCLUDE_ARCH_RISCV_ATOMIC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The standard RISC-V atomic-instruction extension, "A", specifies
|
||||
* the number of instructions that atomically read-modify-write memory,
|
||||
* which RISC-V harts should support in order to synchronise harts
|
||||
* running in the same memory space. This is the subset of RISC-V
|
||||
* atomic-instructions not present in atomic_builtin.h file.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
static ALWAYS_INLINE atomic_val_t atomic_swap(const atomic_t *target, atomic_val_t newval)
|
||||
{
|
||||
atomic_val_t ret;
|
||||
|
||||
__asm__ volatile("amoswap.d.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(newval), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE atomic_val_t atomic_max(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
atomic_val_t ret;
|
||||
|
||||
__asm__ volatile("amomax.d.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(value), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE atomic_val_t atomic_min(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
atomic_val_t ret;
|
||||
|
||||
__asm__ volatile("amomin.d.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(value), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE atomic_val_t atomic_maxu(unsigned long *target, unsigned long value)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
__asm__ volatile("amomaxu.d.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(value), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE atomic_val_t atomic_minu(unsigned long *target, unsigned long value)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
__asm__ volatile("amominu.d.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(value), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static ALWAYS_INLINE atomic_val_t atomic_swap(const atomic_t *target, atomic_val_t newval)
|
||||
{
|
||||
atomic_val_t ret;
|
||||
|
||||
__asm__ volatile("amoswap.w.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(newval), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE atomic_val_t atomic_max(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
atomic_val_t ret;
|
||||
|
||||
__asm__ volatile("amomax.w.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(value), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE atomic_val_t atomic_min(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
atomic_val_t ret;
|
||||
|
||||
__asm__ volatile("amomin.w.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(value), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE unsigned long atomic_maxu(unsigned long *target, unsigned long value)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
__asm__ volatile("amomaxu.w.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(value), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE unsigned long atomic_minu(unsigned long *target, unsigned long value)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
__asm__ volatile("amominu.w.aq %0, %1, %2"
|
||||
: "=r"(ret)
|
||||
: "r"(value), "A"(*target)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_64BIT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_ARCH_RISCV_ATOMIC_H_ */
|
8
tests/arch/riscv/atomic/CMakeLists.txt
Normal file
8
tests/arch/riscv/atomic/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(riscv_atomic)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources})
|
2
tests/arch/riscv/atomic/prj.conf
Normal file
2
tests/arch/riscv/atomic/prj.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
CONFIG_ZTEST=y
|
||||
CONFIG_MP_MAX_NUM_CPUS=1
|
73
tests/arch/riscv/atomic/src/main.c
Normal file
73
tests/arch/riscv/atomic/src/main.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2024 NextSilicon LTD
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
#include <zephyr/sys/atomic.h>
|
||||
#include <zephyr/arch/riscv/atomic.h>
|
||||
|
||||
/**
|
||||
* @brief Verify RISC-V specific atomic functionalities
|
||||
* @details
|
||||
* Test Objective:
|
||||
* - Test if the RISC-V atomic instructions API is correct.
|
||||
*
|
||||
* Test Procedure:
|
||||
* - Call the API interface of the following atomic operations in turn,
|
||||
* decision is based on function return value and target operands.
|
||||
* - atomic_swap()
|
||||
* - atomic_max()
|
||||
* - atomic_min()
|
||||
*
|
||||
* Expected Test Result:
|
||||
* - The function return value and target operands are correct.
|
||||
*
|
||||
* Pass/Fail Criteria:
|
||||
* - Successful if all check points in the test procedure have passed, failure otherwise.
|
||||
*/
|
||||
|
||||
#define ATOMIC_WORD(val_if_64, val_if_32) \
|
||||
((atomic_t)((sizeof(void *) == sizeof(uint64_t)) ? (val_if_64) : (val_if_32)))
|
||||
|
||||
ZTEST_USER(riscv_atomic, test_atomic)
|
||||
{
|
||||
atomic_t target;
|
||||
atomic_val_t value;
|
||||
unsigned long utarget, uvalue;
|
||||
|
||||
zassert_equal(sizeof(atomic_t), ATOMIC_WORD(sizeof(uint64_t), sizeof(uint32_t)),
|
||||
"sizeof(atomic_t)");
|
||||
|
||||
/* atomic_swap */
|
||||
target = 21;
|
||||
value = 7;
|
||||
zassert_true((atomic_swap(&target, value) == 21), "atomic_swap");
|
||||
zassert_true((target == 7), "atomic_swap");
|
||||
|
||||
/* atomic_max */
|
||||
target = 5;
|
||||
value = -8;
|
||||
zassert_true((atomic_max(&target, value) == 5), "atomic_max");
|
||||
zassert_true((target == 5), "atomic_max");
|
||||
|
||||
/* atomic_min */
|
||||
target = 5;
|
||||
value = -8;
|
||||
zassert_true((atomic_min(&target, value) == 5), "atomic_min");
|
||||
zassert_true((target == -8), "atomic_min");
|
||||
|
||||
/* atomic_max unsigned */
|
||||
utarget = 5;
|
||||
uvalue = ATOMIC_WORD(0xffffffff00000000, 0xffff0000);
|
||||
zassert_true((atomic_maxu(&utarget, uvalue) == 5), "atomic_maxu");
|
||||
zassert_true((utarget == ATOMIC_WORD(0xffffffff00000000, 0xffff0000)), "atomic_maxu");
|
||||
|
||||
/* atomic_min unsigned */
|
||||
utarget = 5;
|
||||
uvalue = ATOMIC_WORD(0xffffffff00000000, 0xffff0000);
|
||||
zassert_true((atomic_minu(&utarget, uvalue) == 5), "atomic_minu");
|
||||
zassert_true((utarget == 5), "atomic_minu");
|
||||
}
|
||||
|
||||
ZTEST_SUITE(riscv_atomic, NULL, NULL, NULL, NULL, NULL);
|
4
tests/arch/riscv/atomic/testcase.yaml
Normal file
4
tests/arch/riscv/atomic/testcase.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
tests:
|
||||
arch.riscv.atomic:
|
||||
arch_allow: riscv
|
||||
filter: CONFIG_RISCV_ISA_EXT_A and not CONFIG_SOC_OPENISA_RV32M1
|
Loading…
Add table
Add a link
Reference in a new issue