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