1 /* Copyright 2019 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
7 #include <metal/compiler.h>
8 #include <metal/machine.h>
9 #include <metal/memory.h>
13 * @brief An API for creating and using a software lock/mutex
16 /* TODO: How can we make the exception code platform-independant? */
17 #define _METAL_STORE_AMO_ACCESS_FAULT 7
19 #define METAL_LOCK_BACKOFF_CYCLES 32
20 #define METAL_LOCK_BACKOFF_EXPONENT 2
23 * @def METAL_LOCK_DECLARE
24 * @brief Declare a lock
26 * Locks must be declared with METAL_LOCK_DECLARE to ensure that the lock
27 * is linked into a memory region which supports atomic memory operations.
29 #define METAL_LOCK_DECLARE(name) \
30 __attribute__((section(".data.locks"))) struct metal_lock name
33 * @brief A handle for a lock
40 * @brief Initialize a lock
41 * @param lock The handle for a lock
42 * @return 0 if the lock is successfully initialized. A non-zero code indicates
45 * If the lock cannot be initialized, attempts to take or give the lock
46 * will result in a Store/AMO access fault.
48 __inline__ int metal_lock_init(struct metal_lock *lock) {
50 /* Get a handle for the memory which holds the lock state */
51 struct metal_memory *lock_mem =
52 metal_get_memory_from_address((uintptr_t) & (lock->_state));
57 /* If the memory doesn't support atomics, report an error */
58 if (!metal_memory_supports_atomics(lock_mem)) {
72 * @param lock The handle for a lock
73 * @return 0 if the lock is successfully taken
75 * If the lock initialization failed, attempts to take a lock will result in
76 * a Store/AMO access fault.
78 __inline__ int metal_lock_take(struct metal_lock *lock) {
84 const int max_backoff = METAL_LOCK_BACKOFF_CYCLES * METAL_MAX_CORES;
87 __asm__ volatile("amoswap.w.aq %[old], %[new], (%[state])"
89 : [new] "r"(new), [state] "r"(&(lock->_state))
96 for (int i = 0; i < backoff; i++) {
100 if (backoff < max_backoff) {
101 backoff *= METAL_LOCK_BACKOFF_EXPONENT;
107 /* Store the memory address in mtval like a normal store/amo access fault */
108 __asm__("csrw mtval, %[state]" ::[state] "r"(&(lock->_state)));
110 /* Trigger a Store/AMO access fault */
111 _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
113 /* If execution returns, indicate failure */
119 * @brief Give back a held lock
120 * @param lock The handle for a lock
121 * @return 0 if the lock is successfully given
123 * If the lock initialization failed, attempts to give a lock will result in
124 * a Store/AMO access fault.
126 __inline__ int metal_lock_give(struct metal_lock *lock) {
127 #ifdef __riscv_atomic
129 "amoswap.w.rl x0, x0, (%[state])" ::[state] "r"(&(lock->_state))
134 /* Store the memory address in mtval like a normal store/amo access fault */
135 __asm__("csrw mtval, %[state]" ::[state] "r"(&(lock->_state)));
137 /* Trigger a Store/AMO access fault */
138 _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
140 /* If execution returns, indicate failure */
145 #endif /* METAL__LOCK_H */