]> begriffs open source - cmsis-freertos/blob - Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/metal/lock.h
Update README.md - branch main is now the base branch
[cmsis-freertos] / Demo / RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio / freedom-metal / metal / lock.h
1 /* Copyright 2019 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
3
4 #ifndef METAL__LOCK_H
5 #define METAL__LOCK_H
6
7 #include <metal/compiler.h>
8 #include <metal/machine.h>
9 #include <metal/memory.h>
10
11 /*!
12  * @file lock.h
13  * @brief An API for creating and using a software lock/mutex
14  */
15
16 /* TODO: How can we make the exception code platform-independant? */
17 #define _METAL_STORE_AMO_ACCESS_FAULT 7
18
19 #define METAL_LOCK_BACKOFF_CYCLES 32
20 #define METAL_LOCK_BACKOFF_EXPONENT 2
21
22 /*!
23  * @def METAL_LOCK_DECLARE
24  * @brief Declare a lock
25  *
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.
28  */
29 #define METAL_LOCK_DECLARE(name)                                               \
30     __attribute__((section(".data.locks"))) struct metal_lock name
31
32 /*!
33  * @brief A handle for a lock
34  */
35 struct metal_lock {
36     int _state;
37 };
38
39 /*!
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
43  * failure.
44  *
45  * If the lock cannot be initialized, attempts to take or give the lock
46  * will result in a Store/AMO access fault.
47  */
48 __inline__ int metal_lock_init(struct metal_lock *lock) {
49 #ifdef __riscv_atomic
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));
53     if (!lock_mem) {
54         return 1;
55     }
56
57     /* If the memory doesn't support atomics, report an error */
58     if (!metal_memory_supports_atomics(lock_mem)) {
59         return 2;
60     }
61
62     lock->_state = 0;
63
64     return 0;
65 #else
66     return 3;
67 #endif
68 }
69
70 /*!
71  * @brief Take a lock
72  * @param lock The handle for a lock
73  * @return 0 if the lock is successfully taken
74  *
75  * If the lock initialization failed, attempts to take a lock will result in
76  * a Store/AMO access fault.
77  */
78 __inline__ int metal_lock_take(struct metal_lock *lock) {
79 #ifdef __riscv_atomic
80     int old = 1;
81     int new = 1;
82
83     int backoff = 1;
84     const int max_backoff = METAL_LOCK_BACKOFF_CYCLES * METAL_MAX_CORES;
85
86     while (1) {
87         __asm__ volatile("amoswap.w.aq %[old], %[new], (%[state])"
88                          : [old] "=r"(old)
89                          : [new] "r"(new), [state] "r"(&(lock->_state))
90                          : "memory");
91
92         if (old == 0) {
93             break;
94         }
95
96         for (int i = 0; i < backoff; i++) {
97             __asm__ volatile("");
98         }
99
100         if (backoff < max_backoff) {
101             backoff *= METAL_LOCK_BACKOFF_EXPONENT;
102         }
103     }
104
105     return 0;
106 #else
107     /* Store the memory address in mtval like a normal store/amo access fault */
108     __asm__("csrw mtval, %[state]" ::[state] "r"(&(lock->_state)));
109
110     /* Trigger a Store/AMO access fault */
111     _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
112
113     /* If execution returns, indicate failure */
114     return 1;
115 #endif
116 }
117
118 /*!
119  * @brief Give back a held lock
120  * @param lock The handle for a lock
121  * @return 0 if the lock is successfully given
122  *
123  * If the lock initialization failed, attempts to give a lock will result in
124  * a Store/AMO access fault.
125  */
126 __inline__ int metal_lock_give(struct metal_lock *lock) {
127 #ifdef __riscv_atomic
128     __asm__ volatile(
129         "amoswap.w.rl x0, x0, (%[state])" ::[state] "r"(&(lock->_state))
130         : "memory");
131
132     return 0;
133 #else
134     /* Store the memory address in mtval like a normal store/amo access fault */
135     __asm__("csrw mtval, %[state]" ::[state] "r"(&(lock->_state)));
136
137     /* Trigger a Store/AMO access fault */
138     _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
139
140     /* If execution returns, indicate failure */
141     return 1;
142 #endif
143 }
144
145 #endif /* METAL__LOCK_H */