diff --git a/Makefile.am b/Makefile.am index 8057f663..efdf009d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -82,6 +82,7 @@ nobase_include_HEADERS = \ metal/drivers/ucb_htif0.h \ metal/machine/inline.h \ metal/machine/platform.h \ + metal/atomic.h \ metal/button.h \ metal/cache.h \ metal/clock.h \ @@ -188,6 +189,7 @@ libriscv__mmachine__@MACHINE_NAME@_a_SOURCES = \ src/drivers/sifive_uart0.c \ src/drivers/sifive_wdog0.c \ src/drivers/ucb_htif0.c \ + src/atomic.c \ src/button.c \ src/cache.c \ src/clock.c \ diff --git a/Makefile.in b/Makefile.in index 654c3410..71645244 100644 --- a/Makefile.in +++ b/Makefile.in @@ -226,6 +226,7 @@ am_libriscv__mmachine__@MACHINE_NAME@_a_OBJECTS = src/drivers/libriscv__mmachine src/drivers/libriscv__mmachine__@MACHINE_NAME@_a-sifive_uart0.$(OBJEXT) \ src/drivers/libriscv__mmachine__@MACHINE_NAME@_a-sifive_wdog0.$(OBJEXT) \ src/drivers/libriscv__mmachine__@MACHINE_NAME@_a-ucb_htif0.$(OBJEXT) \ + src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.$(OBJEXT) \ src/libriscv__mmachine__@MACHINE_NAME@_a-button.$(OBJEXT) \ src/libriscv__mmachine__@MACHINE_NAME@_a-cache.$(OBJEXT) \ src/libriscv__mmachine__@MACHINE_NAME@_a-clock.$(OBJEXT) \ @@ -509,6 +510,7 @@ nobase_include_HEADERS = \ metal/drivers/ucb_htif0.h \ metal/machine/inline.h \ metal/machine/platform.h \ + metal/atomic.h \ metal/button.h \ metal/cache.h \ metal/clock.h \ @@ -582,6 +584,7 @@ libriscv__mmachine__@MACHINE_NAME@_a_SOURCES = \ src/drivers/sifive_uart0.c \ src/drivers/sifive_wdog0.c \ src/drivers/ucb_htif0.c \ + src/atomic.c \ src/button.c \ src/cache.c \ src/clock.c \ @@ -892,6 +895,8 @@ src/$(am__dirstamp): src/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) src/$(DEPDIR) @: > src/$(DEPDIR)/$(am__dirstamp) +src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.$(OBJEXT): \ + src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) src/libriscv__mmachine__@MACHINE_NAME@_a-button.$(OBJEXT): \ src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) src/libriscv__mmachine__@MACHINE_NAME@_a-cache.$(OBJEXT): \ @@ -1018,6 +1023,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@gloss/$(DEPDIR)/sys_utime.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gloss/$(DEPDIR)/sys_wait.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gloss/$(DEPDIR)/sys_write.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-atomic.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-button.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-clock.Po@am__quote@ @@ -1526,6 +1532,20 @@ src/drivers/libriscv__mmachine__@MACHINE_NAME@_a-ucb_htif0.obj: src/drivers/ucb_ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libriscv__mmachine__@MACHINE_NAME@_a_CFLAGS) $(CFLAGS) -c -o src/drivers/libriscv__mmachine__@MACHINE_NAME@_a-ucb_htif0.obj `if test -f 'src/drivers/ucb_htif0.c'; then $(CYGPATH_W) 'src/drivers/ucb_htif0.c'; else $(CYGPATH_W) '$(srcdir)/src/drivers/ucb_htif0.c'; fi` +src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.o: src/atomic.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libriscv__mmachine__@MACHINE_NAME@_a_CFLAGS) $(CFLAGS) -MT src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.o -MD -MP -MF src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-atomic.Tpo -c -o src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.o `test -f 'src/atomic.c' || echo '$(srcdir)/'`src/atomic.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-atomic.Tpo src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-atomic.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/atomic.c' object='src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libriscv__mmachine__@MACHINE_NAME@_a_CFLAGS) $(CFLAGS) -c -o src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.o `test -f 'src/atomic.c' || echo '$(srcdir)/'`src/atomic.c + +src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.obj: src/atomic.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libriscv__mmachine__@MACHINE_NAME@_a_CFLAGS) $(CFLAGS) -MT src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.obj -MD -MP -MF src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-atomic.Tpo -c -o src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.obj `if test -f 'src/atomic.c'; then $(CYGPATH_W) 'src/atomic.c'; else $(CYGPATH_W) '$(srcdir)/src/atomic.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-atomic.Tpo src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-atomic.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/atomic.c' object='src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libriscv__mmachine__@MACHINE_NAME@_a_CFLAGS) $(CFLAGS) -c -o src/libriscv__mmachine__@MACHINE_NAME@_a-atomic.obj `if test -f 'src/atomic.c'; then $(CYGPATH_W) 'src/atomic.c'; else $(CYGPATH_W) '$(srcdir)/src/atomic.c'; fi` + src/libriscv__mmachine__@MACHINE_NAME@_a-button.o: src/button.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libriscv__mmachine__@MACHINE_NAME@_a_CFLAGS) $(CFLAGS) -MT src/libriscv__mmachine__@MACHINE_NAME@_a-button.o -MD -MP -MF src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-button.Tpo -c -o src/libriscv__mmachine__@MACHINE_NAME@_a-button.o `test -f 'src/button.c' || echo '$(srcdir)/'`src/button.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-button.Tpo src/$(DEPDIR)/libriscv__mmachine__@MACHINE_NAME@_a-button.Po diff --git a/metal/atomic.h b/metal/atomic.h new file mode 100644 index 00000000..32a33abd --- /dev/null +++ b/metal/atomic.h @@ -0,0 +1,259 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#ifndef METAL__ATOMIC_H +#define METAL__ATOMIC_H + +#include + +#include + +typedef volatile int32_t metal_atomic_t; + +#define METAL_ATOMIC_DECLARE(name) \ + __attribute((section(".data.atomics"))) metal_atomic_t name + +#define _METAL_STORE_AMO_ACCESS_FAULT 7 + +/* This macro stores the memory address in mtval like a normal store/amo access + * fault, triggers a trap, and then if execution returns, returns 0 as an + * arbitrary choice */ +#define _METAL_TRAP_AMO_ACCESS(addr) \ + __asm__("csrw mtval, %[atomic]" ::[atomic] "r"(a)); \ + _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT); \ + return 0; + +/*! + * @brief Check if the platform supports atomic operations + * + * @return 1 if atomic operations are supported, 0 if not + */ +__inline__ int32_t metal_atomic_available(void) { +#ifdef __riscv_atomic + return 1; +#else + return 0; +#endif +} + +/*! + * @brief Atomically increment a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to increment + * @param increment the amount to increment the value + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_add(metal_atomic_t *a, int32_t increment) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoadd.w %[old], %[increment], (%[atomic])" + : [old] "=r"(old) + : [increment] "r"(increment), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically bitwise-AND a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to bitwise-AND + * @param mask the bitmask to AND + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_and(metal_atomic_t *a, int32_t mask) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoand.w %[old], %[mask], (%[atomic])" + : [old] "=r"(old) + : [mask] "r"(mask), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically bitwise-OR a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to bitwise-OR + * @param mask the bitmask to OR + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_or(metal_atomic_t *a, int32_t mask) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoor.w %[old], %[mask], (%[atomic])" + : [old] "=r"(old) + : [mask] "r"(mask), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically swap a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param new_value the value to store in the metal_atomic_t + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_swap(metal_atomic_t *a, int32_t new_value) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoswap.w %[old], %[newval], (%[atomic])" + : [old] "=r"(old) + : [newval] "r"(new_value), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically bitwise-XOR a metal_atomic_t and return its old value + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to bitwise-XOR + * @param mask the bitmask to XOR + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_xor(metal_atomic_t *a, int32_t mask) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amoxor.w %[old], %[mask], (%[atomic])" + : [old] "=r"(old) + : [mask] "r"(mask), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically set the value of a memory location to the greater of + * its current value or a value to compare it with. + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param compare the value to compare with the value in memory + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_max(metal_atomic_t *a, int32_t compare) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amomax.w %[old], %[compare], (%[atomic])" + : [old] "=r"(old) + : [compare] "r"(compare), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically set the value of a memory location to the (unsigned) + * greater of its current value or a value to compare it with. + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param compare the value to compare with the value in memory + * + * @return The previous value of the metal_atomic_t + */ +__inline__ uint32_t metal_atomic_max_u(metal_atomic_t *a, uint32_t compare) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amomaxu.w %[old], %[compare], (%[atomic])" + : [old] "=r"(old) + : [compare] "r"(compare), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically set the value of a memory location to the lesser of + * its current value or a value to compare it with. + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param compare the value to compare with the value in memory + * + * @return The previous value of the metal_atomic_t + */ +__inline__ int32_t metal_atomic_min(metal_atomic_t *a, int32_t compare) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amomin.w %[old], %[compare], (%[atomic])" + : [old] "=r"(old) + : [compare] "r"(compare), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +/*! + * @brief Atomically set the value of a memory location to the (unsigned) lesser + * of its current value or a value to compare it with. + * + * If atomics are not supported on the platform, this function will trap with + * a Store/AMO access fault. + * + * @param a The pointer to the value to swap + * @param compare the value to compare with the value in memory + * + * @return The previous value of the metal_atomic_t + */ +__inline__ uint32_t metal_atomic_min_u(metal_atomic_t *a, uint32_t compare) { +#ifdef __riscv_atomic + int32_t old; + __asm__ volatile("amominu.w %[old], %[compare], (%[atomic])" + : [old] "=r"(old) + : [compare] "r"(compare), [atomic] "r"(a) + : "memory"); + return old; +#else + _METAL_TRAP_AMO_ACCESS(a); +#endif +} + +#endif /* METAL__ATOMIC_H */ diff --git a/src/atomic.c b/src/atomic.c new file mode 100644 index 00000000..326568e3 --- /dev/null +++ b/src/atomic.c @@ -0,0 +1,19 @@ +/* Copyright 2019 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +extern __inline__ int32_t metal_atomic_available(void); +extern __inline__ int32_t metal_atomic_add(metal_atomic_t *a, + int32_t increment); +extern __inline__ int32_t metal_atomic_and(metal_atomic_t *a, int32_t mask); +extern __inline__ int32_t metal_atomic_or(metal_atomic_t *a, int32_t mask); +extern __inline__ int32_t metal_atomic_swap(metal_atomic_t *a, + int32_t new_value); +extern __inline__ int32_t metal_atomic_xor(metal_atomic_t *a, int32_t mask); +extern __inline__ int32_t metal_atomic_max(metal_atomic_t *a, int32_t compare); +extern __inline__ uint32_t metal_atomic_max_u(metal_atomic_t *a, + uint32_t compare); +extern __inline__ int32_t metal_atomic_min(metal_atomic_t *a, int32_t compare); +extern __inline__ uint32_t metal_atomic_min_u(metal_atomic_t *a, + uint32_t compare);