]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/metal/lock.h
Update RISCC-V-RV32-SiFive_HiFive1_FreedomStudio project to latest tools and metal...
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_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/machine.h>
8 #include <metal/memory.h>
9 #include <metal/compiler.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"))) \
31                 struct metal_lock name
32
33 /*!
34  * @brief A handle for a lock
35  */
36 struct metal_lock {
37         int _state;
38 };
39
40 /*!
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.
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 = metal_get_memory_from_address((uintptr_t) &(lock->_state));
52     if(!lock_mem) {
53         return 1;
54     }
55
56     /* If the memory doesn't support atomics, report an error */
57     if(!metal_memory_supports_atomics(lock_mem)) {
58         return 2;
59     }
60
61     lock->_state = 0;
62
63     return 0;
64 #else
65     return 3;
66 #endif
67 }
68
69 /*!
70  * @brief Take a lock
71  * @param lock The handle for a lock
72  * @return 0 if the lock is successfully taken
73  *
74  * If the lock initialization failed, attempts to take a lock will result in
75  * a Store/AMO access fault.
76  */
77 __inline__ int metal_lock_take(struct metal_lock *lock) {
78 #ifdef __riscv_atomic
79     int old = 1;
80     int new = 1;
81
82     int backoff = 1;
83     const int max_backoff = METAL_LOCK_BACKOFF_CYCLES * METAL_MAX_CORES;
84
85     while(1) {
86         __asm__ volatile("amoswap.w.aq %[old], %[new], (%[state])"
87                          : [old] "=r" (old)
88                          : [new] "r" (new), [state] "r" (&(lock->_state))
89                          : "memory");
90
91         if (old == 0) {
92             break;
93         }
94
95         for (int i = 0; i < backoff; i++) {
96             __asm__ volatile("");
97         }
98
99         if (backoff < max_backoff) {
100             backoff *= METAL_LOCK_BACKOFF_EXPONENT;
101         }
102     }
103
104     return 0;
105 #else
106     /* Store the memory address in mtval like a normal store/amo access fault */
107     __asm__ ("csrw mtval, %[state]"
108              :: [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("amoswap.w.rl x0, x0, (%[state])"
129                      :: [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]"
136              :: [state] "r" (&(lock->_state)));
137
138     /* Trigger a Store/AMO access fault */
139     _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
140
141     /* If execution returns, indicate failure */
142     return 1;
143 #endif
144 }
145
146 #endif /* METAL__LOCK_H */