• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2019 Nuclei Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Licensed under the Apache License, Version 2.0 (the License); you may
7  * not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 #ifndef __CORE_FEATURE_FPU_H__
19 #define __CORE_FEATURE_FPU_H__
20 /*!
21  * @file     core_feature_fpu.h
22  * @brief    FPU feature API header file for Nuclei N/NX Core
23  */
24 /*
25  * FPU Feature Configuration Macro:
26  * 1. __FPU_PRESENT:  Define whether Floating Point Unit(FPU) is present or not
27  *   * 0: Not present
28  *   * 1: Single precision FPU present, __RISCV_FLEN == 32
29  *   * 2: Double precision FPU present, __RISCV_FLEN == 64
30  */
31 #ifdef __cplusplus
32  extern "C" {
33 #endif
34 
35 /* ===== FPU Operations ===== */
36 /**
37  * \defgroup NMSIS_Core_FPU_Functions   FPU Functions
38  * \ingroup  NMSIS_Core
39  * \brief    Functions that related to the RISC-V FPU (F and D extension).
40  * \details
41  *
42  * Nuclei provided floating point unit by RISC-V F and D extension.
43  * * `F extension` adds single-precision floating-point computational
44  * instructions compliant with the IEEE 754-2008 arithmetic standard, __RISCV_FLEN = 32.
45  *   The F extension adds 32 floating-point registers, f0-f31, each 32 bits wide,
46  *   and a floating-point control and status register fcsr, which contains the
47  *   operating mode and exception status of the floating-point unit.
48  * * `D extension` adds double-precision floating-point computational instructions
49  * compliant with the IEEE 754-2008 arithmetic standard.
50  *   The D extension widens the 32 floating-point registers, f0-f31, to 64 bits, __RISCV_FLEN = 64
51  *   @{
52  */
53 #if defined(__FPU_PRESENT) && (__FPU_PRESENT > 0)
54 
55 #if __FPU_PRESENT == 1
56   /** \brief Refer to the width of the floating point register in bits(either 32 or 64) */
57   #define __RISCV_FLEN          32
58 #elif __FPU_PRESENT == 2
59   #define __RISCV_FLEN          64
60 #else
61   #define __RISCV_FLEN          __riscv_flen
62 #endif /* __FPU_PRESENT == 1 */
63 
64 /** \brief Get FCSR CSR Register */
65 #define __get_FCSR()            __RV_CSR_READ(CSR_FCSR)
66 /** \brief Set FCSR CSR Register with val */
67 #define __set_FCSR(val)         __RV_CSR_WRITE(CSR_FCSR, (val))
68 /** \brief Get FRM CSR Register */
69 #define __get_FRM()             __RV_CSR_READ(CSR_FRM)
70 /** \brief Set FRM CSR Register with val */
71 #define __set_FRM(val)          __RV_CSR_WRITE(CSR_FRM, (val))
72 /** \brief Get FFLAGS CSR Register */
73 #define __get_FFLAGS()          __RV_CSR_READ(CSR_FFLAGS)
74 /** \brief Set FFLAGS CSR Register with val */
75 #define __set_FFLAGS(val)       __RV_CSR_WRITE(CSR_FFLAGS, (val))
76 
77 /** \brief Enable FPU Unit */
78 #define __enable_FPU()          __RV_CSR_SET(CSR_MSTATUS, MSTATUS_FS)
79 /**
80  * \brief Disable FPU Unit
81  * \details
82  * * We can save power by disable FPU Unit.
83  * * When FPU Unit is disabled, any access to FPU related CSR registers
84  * and FPU instructions will cause illegal Instuction Exception.
85  * */
86 #define __disable_FPU()         __RV_CSR_CLEAR(CSR_MSTATUS, MSTATUS_FS)
87 
88 
89 /**
90  * \brief   Load a single-precision value from memory into float point register freg using flw instruction
91  * \details The FLW instruction loads a single-precision floating point value from memory
92  * address (addr + ofs) into floating point register freg(f0-f31)
93  * \param [in]    freg   The floating point register, eg. FREG(0), f0
94  * \param [in]    addr   The memory base address, 4 byte aligned required
95  * \param [in]    ofs    a 12-bit immediate signed byte offset value, should be an const value
96  * \remarks
97  * * FLW and FSW operations need to make sure the address is 4 bytes aligned,
98  *   otherwise it will cause exception code 4(Load address misaligned) or 6 (Store/AMO address misaligned)
99  * * FLW and FSW do not modify the bits being transferred; in particular, the payloads of non-canonical
100  * NaNs are preserved
101  *
102  */
103 #define __RV_FLW(freg, addr, ofs)                              \
104     ({                                                         \
105         register rv_csr_t __addr = (rv_csr_t)(addr);           \
106         __ASM volatile("flw " STRINGIFY(freg) ", %0(%1)  "     \
107                      : : "I"(ofs), "r"(__addr)                 \
108                      : "memory");                              \
109     })
110 
111 /**
112  * \brief   Store a single-precision value from float point freg into memory using fsw instruction
113  * \details The FSW instruction stores a single-precision value from floating point register to memory
114  * \param [in]    freg   The floating point register(f0-f31), eg. FREG(0), f0
115  * \param [in]    addr   The memory base address, 4 byte aligned required
116  * \param [in]    ofs    a 12-bit immediate signed byte offset value, should be an const value
117  * \remarks
118  * * FLW and FSW operations need to make sure the address is 4 bytes aligned,
119  *   otherwise it will cause exception code 4(Load address misaligned) or 6 (Store/AMO address misaligned)
120  * * FLW and FSW do not modify the bits being transferred; in particular, the payloads of non-canonical
121  * NaNs are preserved
122  *
123  */
124 #define __RV_FSW(freg, addr, ofs)                              \
125     ({                                                         \
126         register rv_csr_t __addr = (rv_csr_t)(addr);           \
127         __ASM volatile("fsw " STRINGIFY(freg) ", %0(%1)  "     \
128                      : : "I"(ofs), "r"(__addr)                 \
129                      : "memory");                              \
130     })
131 
132 /**
133  * \brief   Load a double-precision value from memory into float point register freg using fld instruction
134  * \details The FLD instruction loads a double-precision floating point value from memory
135  * address (addr + ofs) into floating point register freg(f0-f31)
136  * \param [in]    freg   The floating point register, eg. FREG(0), f0
137  * \param [in]    addr   The memory base address, 8 byte aligned required
138  * \param [in]    ofs    a 12-bit immediate signed byte offset value, should be an const value
139  * \attention
140  * * Function only available for double precision floating point unit, FLEN = 64
141  * \remarks
142  * * FLD and FSD operations need to make sure the address is 8 bytes aligned,
143  *   otherwise it will cause exception code 4(Load address misaligned) or 6 (Store/AMO address misaligned)
144  * * FLD and FSD do not modify the bits being transferred; in particular, the payloads of non-canonical
145  * NaNs are preserved.
146  */
147 #define __RV_FLD(freg, addr, ofs)                              \
148     ({                                                         \
149         register rv_csr_t __addr = (rv_csr_t)(addr);           \
150         __ASM volatile("fld " STRINGIFY(freg) ", %0(%1)  "     \
151                      : : "I"(ofs), "r"(__addr)                 \
152                      : "memory");                              \
153     })
154 
155 /**
156  * \brief   Store a double-precision value from float point freg into memory using fsd instruction
157  * \details The FSD instruction stores double-precision value from floating point register to memory
158  * \param [in]    freg   The floating point register(f0-f31), eg. FREG(0), f0
159  * \param [in]    addr   The memory base address, 8 byte aligned required
160  * \param [in]    ofs    a 12-bit immediate signed byte offset value, should be an const value
161  * \attention
162  * * Function only available for double precision floating point unit, FLEN = 64
163  * \remarks
164  * * FLD and FSD operations need to make sure the address is 8 bytes aligned,
165  *   otherwise it will cause exception code 4(Load address misaligned) or 6 (Store/AMO address misaligned)
166  * * FLD and FSD do not modify the bits being transferred; in particular, the payloads of non-canonical
167  * NaNs are preserved.
168  *
169  */
170 #define __RV_FSD(freg, addr, ofs)                              \
171     ({                                                         \
172         register rv_csr_t __addr = (rv_csr_t)(addr);           \
173         __ASM volatile("fsd " STRINGIFY(freg) ", %0(%1)  "     \
174                      : : "I"(ofs), "r"(__addr)                 \
175                      : "memory");                              \
176     })
177 
178 /**
179  * \def __RV_FLOAD
180  * \brief   Load a float point value from memory into float point register freg using flw/fld instruction
181  * \details
182  * * For Single-Precison Floating-Point Mode(__FPU_PRESENT == 1, __RISCV_FLEN == 32):
183  *   It will call \ref __RV_FLW to load a single-precision floating point value from memory to floating point register
184  * * For Double-Precison Floating-Point Mode(__FPU_PRESENT == 2, __RISCV_FLEN == 64):
185  *   It will call \ref __RV_FLD to load a double-precision floating point value from memory to floating point register
186  *
187  * \attention
188  * Function behaviour is different for __FPU_PRESENT = 1 or 2, please see the real function this macro represent
189  */
190 /**
191  * \def __RV_FSTORE
192  * \brief   Store a float value from float point freg into memory using fsw/fsd instruction
193  * \details
194  * * For Single-Precison Floating-Point Mode(__FPU_PRESENT == 1, __RISCV_FLEN == 32):
195  *   It will call \ref __RV_FSW to store floating point register into memory
196  * * For Double-Precison Floating-Point Mode(__FPU_PRESENT == 2, __RISCV_FLEN == 64):
197  *   It will call \ref __RV_FSD to store floating point register into memory
198  *
199  * \attention
200  * Function behaviour is different for __FPU_PRESENT = 1 or 2, please see the real function this macro represent
201  */
202 #if __FPU_PRESENT == 1
203 #define __RV_FLOAD              __RV_FLW
204 #define __RV_FSTORE             __RV_FSW
205 /** \brief Type of FPU register, depends on the FLEN defined in RISC-V */
206 typedef uint32_t rv_fpu_t;
207 #elif __FPU_PRESENT == 2
208 #define __RV_FLOAD              __RV_FLD
209 #define __RV_FSTORE             __RV_FSD
210 /** \brief Type of FPU register, depends on the FLEN defined in RISC-V */
211 typedef uint64_t rv_fpu_t;
212 #endif /* __FPU_PRESENT == 2 */
213 
214 /**
215  * \brief   Save FPU context into variables for interrupt nesting
216  * \details
217  * This macro is used to declare variables which are used for saving
218  * FPU context, and it will store the nessary fpu registers into
219  * these variables, it need to be used in a interrupt when in this
220  * interrupt fpu registers are used.
221  * \remarks
222  * - It need to be used together with \ref RESTORE_FPU_CONTEXT
223  * - Don't use variable names __fpu_context in your ISR code
224  * - If you isr code will use fpu registers, and this interrupt is nested.
225  * Then you can do it like this:
226  * \code
227  * void eclic_mtip_handler(void)
228  * {
229  *     // !!!Interrupt is enabled here!!!
230  *     // !!!Higher priority interrupt could nest it!!!
231  *
232  *     // Necessary only when you need to use fpu registers
233  *     // in this isr handler functions
234  *     SAVE_FPU_CONTEXT();
235  *
236  *     // put you own interrupt handling code here
237  *
238  *     // pair of SAVE_FPU_CONTEXT()
239  *     RESTORE_FPU_CONTEXT();
240  * }
241  * \endcode
242  */
243 #define SAVE_FPU_CONTEXT()                                                  \
244         rv_fpu_t __fpu_context[20];                                         \
245         __RV_FSTORE(FREG(0),  __fpu_context, 0  << LOG_FPREGBYTES);         \
246         __RV_FSTORE(FREG(1),  __fpu_context, 1  << LOG_FPREGBYTES);         \
247         __RV_FSTORE(FREG(2),  __fpu_context, 2  << LOG_FPREGBYTES);         \
248         __RV_FSTORE(FREG(3),  __fpu_context, 3  << LOG_FPREGBYTES);         \
249         __RV_FSTORE(FREG(4),  __fpu_context, 4  << LOG_FPREGBYTES);         \
250         __RV_FSTORE(FREG(5),  __fpu_context, 5  << LOG_FPREGBYTES);         \
251         __RV_FSTORE(FREG(6),  __fpu_context, 6  << LOG_FPREGBYTES);         \
252         __RV_FSTORE(FREG(7),  __fpu_context, 7  << LOG_FPREGBYTES);         \
253         __RV_FSTORE(FREG(10), __fpu_context, 8  << LOG_FPREGBYTES);         \
254         __RV_FSTORE(FREG(11), __fpu_context, 9  << LOG_FPREGBYTES);         \
255         __RV_FSTORE(FREG(12), __fpu_context, 10 << LOG_FPREGBYTES);         \
256         __RV_FSTORE(FREG(13), __fpu_context, 11 << LOG_FPREGBYTES);         \
257         __RV_FSTORE(FREG(14), __fpu_context, 12 << LOG_FPREGBYTES);         \
258         __RV_FSTORE(FREG(15), __fpu_context, 13 << LOG_FPREGBYTES);         \
259         __RV_FSTORE(FREG(16), __fpu_context, 14 << LOG_FPREGBYTES);         \
260         __RV_FSTORE(FREG(17), __fpu_context, 15 << LOG_FPREGBYTES);         \
261         __RV_FSTORE(FREG(28), __fpu_context, 16 << LOG_FPREGBYTES);         \
262         __RV_FSTORE(FREG(29), __fpu_context, 17 << LOG_FPREGBYTES);         \
263         __RV_FSTORE(FREG(30), __fpu_context, 18 << LOG_FPREGBYTES);         \
264         __RV_FSTORE(FREG(31), __fpu_context, 19 << LOG_FPREGBYTES);
265 
266 /**
267  * \brief   Restore necessary fpu registers from variables for interrupt nesting
268  * \details
269  * This macro is used restore necessary fpu registers from pre-defined variables
270  * in \ref SAVE_FPU_CONTEXT macro.
271  * \remarks
272  * - It need to be used together with \ref SAVE_FPU_CONTEXT
273  */
274 #define RESTORE_FPU_CONTEXT()                                               \
275         __RV_FLOAD(FREG(0),  __fpu_context, 0  << LOG_FPREGBYTES);          \
276         __RV_FLOAD(FREG(1),  __fpu_context, 1  << LOG_FPREGBYTES);          \
277         __RV_FLOAD(FREG(2),  __fpu_context, 2  << LOG_FPREGBYTES);          \
278         __RV_FLOAD(FREG(3),  __fpu_context, 3  << LOG_FPREGBYTES);          \
279         __RV_FLOAD(FREG(4),  __fpu_context, 4  << LOG_FPREGBYTES);          \
280         __RV_FLOAD(FREG(5),  __fpu_context, 5  << LOG_FPREGBYTES);          \
281         __RV_FLOAD(FREG(6),  __fpu_context, 6  << LOG_FPREGBYTES);          \
282         __RV_FLOAD(FREG(7),  __fpu_context, 7  << LOG_FPREGBYTES);          \
283         __RV_FLOAD(FREG(10), __fpu_context, 8  << LOG_FPREGBYTES);          \
284         __RV_FLOAD(FREG(11), __fpu_context, 9  << LOG_FPREGBYTES);          \
285         __RV_FLOAD(FREG(12), __fpu_context, 10 << LOG_FPREGBYTES);          \
286         __RV_FLOAD(FREG(13), __fpu_context, 11 << LOG_FPREGBYTES);          \
287         __RV_FLOAD(FREG(14), __fpu_context, 12 << LOG_FPREGBYTES);          \
288         __RV_FLOAD(FREG(15), __fpu_context, 13 << LOG_FPREGBYTES);          \
289         __RV_FLOAD(FREG(16), __fpu_context, 14 << LOG_FPREGBYTES);          \
290         __RV_FLOAD(FREG(17), __fpu_context, 15 << LOG_FPREGBYTES);          \
291         __RV_FLOAD(FREG(28), __fpu_context, 16 << LOG_FPREGBYTES);          \
292         __RV_FLOAD(FREG(29), __fpu_context, 17 << LOG_FPREGBYTES);          \
293         __RV_FLOAD(FREG(30), __fpu_context, 18 << LOG_FPREGBYTES);          \
294         __RV_FLOAD(FREG(31), __fpu_context, 19 << LOG_FPREGBYTES);
295 #else
296 #define SAVE_FPU_CONTEXT()
297 #define RESTORE_FPU_CONTEXT()
298 #endif /* __FPU_PRESENT > 0 */
299 /** @} */ /* End of Doxygen Group NMSIS_Core_FPU_Functions */
300 
301 #ifdef __cplusplus
302 }
303 #endif
304 #endif /** __RISCV_EXT_FPU_H__  */
305