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