]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/bsp/install/include/metal/lock.h
d863aa96ec41cd943702d36125ca44a93f5da250
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_FreedomStudio / bsp / install / include / 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/memory.h>
8 #include <metal/compiler.h>
9
10 /*!
11  * @file lock.h
12  * @brief An API for creating and using a software lock/mutex
13  */
14
15 /* TODO: How can we make the exception code platform-independant? */
16 #define _METAL_STORE_AMO_ACCESS_FAULT 7
17
18 /*!
19  * @def METAL_LOCK_DECLARE
20  * @brief Declare a lock
21  *
22  * Locks must be declared with METAL_LOCK_DECLARE to ensure that the lock
23  * is linked into a memory region which supports atomic memory operations.
24  */
25 #define METAL_LOCK_DECLARE(name) \
26                 __attribute__((section(".data.locks"))) \
27                 struct metal_lock name
28
29 /*!
30  * @brief A handle for a lock
31  */
32 struct metal_lock {
33         int _state;
34 };
35
36 /*!
37  * @brief Initialize a lock
38  * @param lock The handle for a lock
39  * @return 0 if the lock is successfully initialized. A non-zero code indicates failure.
40  *
41  * If the lock cannot be initialized, attempts to take or give the lock
42  * will result in a Store/AMO access fault.
43  */
44 inline int metal_lock_init(struct metal_lock *lock) {
45 #ifdef __riscv_atomic
46     /* Get a handle for the memory which holds the lock state */
47     struct metal_memory *lock_mem = metal_get_memory_from_address((uintptr_t) &(lock->_state));
48     if(!lock_mem) {
49         return 1;
50     }
51
52     /* If the memory doesn't support atomics, report an error */
53     if(!metal_memory_supports_atomics(lock_mem)) {
54         return 2;
55     }
56
57     lock->_state = 0;
58
59     return 0;
60 #else
61     return 3;
62 #endif
63 }
64
65 /*!
66  * @brief Take a lock
67  * @param lock The handle for a lock
68  * @return 0 if the lock is successfully taken
69  *
70  * If the lock initialization failed, attempts to take a lock will result in
71  * a Store/AMO access fault.
72  */
73 inline int metal_lock_take(struct metal_lock *lock) {
74 #ifdef __riscv_atomic
75     int old = 1;
76     int new = 1;
77
78     while(old != 0) {
79         __asm__ volatile("amoswap.w.aq %[old], %[new], (%[state])"
80                          : [old] "=r" (old)
81                          : [new] "r" (new), [state] "r" (&(lock->_state))
82                          : "memory");
83     }
84
85     return 0;
86 #else
87     /* Store the memory address in mtval like a normal store/amo access fault */
88     __asm__ ("csrw mtval, %[state]"
89              :: [state] "r" (&(lock->_state)));
90
91     /* Trigger a Store/AMO access fault */
92     _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
93
94     /* If execution returns, indicate failure */
95     return 1;
96 #endif
97 }
98
99 /*!
100  * @brief Give back a held lock
101  * @param lock The handle for a lock
102  * @return 0 if the lock is successfully given
103  *
104  * If the lock initialization failed, attempts to give a lock will result in
105  * a Store/AMO access fault.
106  */
107 inline int metal_lock_give(struct metal_lock *lock) {
108 #ifdef __riscv_atomic
109     __asm__ volatile("amoswap.w.rl x0, x0, (%[state])"
110                      :: [state] "r" (&(lock->_state))
111                      : "memory");
112
113     return 0;
114 #else
115     /* Store the memory address in mtval like a normal store/amo access fault */
116     __asm__ ("csrw mtval, %[state]"
117              :: [state] "r" (&(lock->_state)));
118
119     /* Trigger a Store/AMO access fault */
120     _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
121
122     /* If execution returns, indicate failure */
123     return 1;
124 #endif
125 }
126
127 #endif /* METAL__LOCK_H */