1 /* Copyright 2019 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
7 #include <metal/machine.h>
8 #include <metal/memory.h>
9 #include <metal/compiler.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"))) \
31 struct metal_lock name
34 * @brief A handle for a lock
41 * @brief Initialize a lock
42 * @param lock The handle for a lock
43 * @return 0 if the lock is successfully initialized. A non-zero code indicates failure.
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 = metal_get_memory_from_address((uintptr_t) &(lock->_state));
56 /* If the memory doesn't support atomics, report an error */
57 if(!metal_memory_supports_atomics(lock_mem)) {
71 * @param lock The handle for a lock
72 * @return 0 if the lock is successfully taken
74 * If the lock initialization failed, attempts to take a lock will result in
75 * a Store/AMO access fault.
77 __inline__ int metal_lock_take(struct metal_lock *lock) {
83 const int max_backoff = METAL_LOCK_BACKOFF_CYCLES * METAL_MAX_CORES;
86 __asm__ volatile("amoswap.w.aq %[old], %[new], (%[state])"
88 : [new] "r" (new), [state] "r" (&(lock->_state))
95 for (int i = 0; i < backoff; i++) {
99 if (backoff < max_backoff) {
100 backoff *= METAL_LOCK_BACKOFF_EXPONENT;
106 /* Store the memory address in mtval like a normal store/amo access fault */
107 __asm__ ("csrw mtval, %[state]"
108 :: [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
128 __asm__ volatile("amoswap.w.rl x0, x0, (%[state])"
129 :: [state] "r" (&(lock->_state))
134 /* Store the memory address in mtval like a normal store/amo access fault */
135 __asm__ ("csrw mtval, %[state]"
136 :: [state] "r" (&(lock->_state)));
138 /* Trigger a Store/AMO access fault */
139 _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
141 /* If execution returns, indicate failure */
146 #endif /* METAL__LOCK_H */