core_feature_fpu.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. #include "core_feature_base.h"
  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. #if __FPU_PRESENT == 1
  55. /** \brief Refer to the width of the floating point register in bits(either 32 or 64) */
  56. #define __RISCV_FLEN 32
  57. #elif __FPU_PRESENT == 2
  58. #define __RISCV_FLEN 64
  59. #else
  60. #define __RISCV_FLEN __riscv_flen
  61. #endif /* __FPU_PRESENT == 1 */
  62. /** \brief Get FCSR CSR Register */
  63. #define __get_FCSR() __RV_CSR_READ(CSR_FCSR)
  64. /** \brief Set FCSR CSR Register with val */
  65. #define __set_FCSR(val) __RV_CSR_WRITE(CSR_FCSR, (val))
  66. /** \brief Get FRM CSR Register */
  67. #define __get_FRM() __RV_CSR_READ(CSR_FRM)
  68. /** \brief Set FRM CSR Register with val */
  69. #define __set_FRM(val) __RV_CSR_WRITE(CSR_FRM, (val))
  70. /** \brief Get FFLAGS CSR Register */
  71. #define __get_FFLAGS() __RV_CSR_READ(CSR_FFLAGS)
  72. /** \brief Set FFLAGS CSR Register with val */
  73. #define __set_FFLAGS(val) __RV_CSR_WRITE(CSR_FFLAGS, (val))
  74. /** \brief Enable FPU Unit, and set state to initial */
  75. #define __enable_FPU() { __RV_CSR_CLEAR(CSR_MSTATUS, MSTATUS_FS); \
  76. __RV_CSR_SET(CSR_MSTATUS, MSTATUS_FS_INITIAL); \
  77. }
  78. /**
  79. * \brief Disable FPU Unit
  80. * \details
  81. * * We can save power by disable FPU Unit.
  82. * * When FPU Unit is disabled, any access to FPU related CSR registers
  83. * and FPU instructions will cause illegal Instuction Exception.
  84. * */
  85. #define __disable_FPU() __RV_CSR_CLEAR(CSR_MSTATUS, MSTATUS_FS)
  86. /**
  87. * \brief Load a single-precision value from memory into float point register freg using flw instruction
  88. * \details The FLW instruction loads a single-precision floating point value from memory
  89. * address (addr + ofs) into floating point register freg(f0-f31)
  90. * \param [in] freg The floating point register, eg. FREG(0), f0
  91. * \param [in] addr The memory base address, 4 byte aligned required
  92. * \param [in] ofs a 12-bit immediate signed byte offset value, should be an const value
  93. * \remarks
  94. * * FLW and FSW operations need to make sure the address is 4 bytes aligned,
  95. * otherwise it will cause exception code 4(Load address misaligned) or 6 (Store/AMO address misaligned)
  96. * * FLW and FSW do not modify the bits being transferred; in particular, the payloads of non-canonical
  97. * NaNs are preserved
  98. *
  99. */
  100. #define __RV_FLW(freg, addr, ofs) \
  101. ({ \
  102. rv_csr_t __addr = (rv_csr_t)(addr); \
  103. __ASM volatile("flw " STRINGIFY(freg) ", %0(%1) " \
  104. : : "I"(ofs), "r"(__addr) \
  105. : "memory"); \
  106. })
  107. /**
  108. * \brief Store a single-precision value from float point freg into memory using fsw instruction
  109. * \details The FSW instruction stores a single-precision value from floating point register to memory
  110. * \param [in] freg The floating point register(f0-f31), eg. FREG(0), f0
  111. * \param [in] addr The memory base address, 4 byte aligned required
  112. * \param [in] ofs a 12-bit immediate signed byte offset value, should be an const value
  113. * \remarks
  114. * * FLW and FSW operations need to make sure the address is 4 bytes aligned,
  115. * otherwise it will cause exception code 4(Load address misaligned) or 6 (Store/AMO address misaligned)
  116. * * FLW and FSW do not modify the bits being transferred; in particular, the payloads of non-canonical
  117. * NaNs are preserved
  118. *
  119. */
  120. #define __RV_FSW(freg, addr, ofs) \
  121. ({ \
  122. rv_csr_t __addr = (rv_csr_t)(addr); \
  123. __ASM volatile("fsw " STRINGIFY(freg) ", %0(%1) " \
  124. : : "I"(ofs), "r"(__addr) \
  125. : "memory"); \
  126. })
  127. /**
  128. * \brief Load a double-precision value from memory into float point register freg using fld instruction
  129. * \details The FLD instruction loads a double-precision floating point value from memory
  130. * address (addr + ofs) into floating point register freg(f0-f31)
  131. * \param [in] freg The floating point register, eg. FREG(0), f0
  132. * \param [in] addr The memory base address, 8 byte aligned required
  133. * \param [in] ofs a 12-bit immediate signed byte offset value, should be an const value
  134. * \attention
  135. * * Function only available for double precision floating point unit, FLEN = 64
  136. * \remarks
  137. * * FLD and FSD operations need to make sure the address is 8 bytes aligned,
  138. * otherwise it will cause exception code 4(Load address misaligned) or 6 (Store/AMO address misaligned)
  139. * * FLD and FSD do not modify the bits being transferred; in particular, the payloads of non-canonical
  140. * NaNs are preserved.
  141. */
  142. #define __RV_FLD(freg, addr, ofs) \
  143. ({ \
  144. rv_csr_t __addr = (rv_csr_t)(addr); \
  145. __ASM volatile("fld " STRINGIFY(freg) ", %0(%1) " \
  146. : : "I"(ofs), "r"(__addr) \
  147. : "memory"); \
  148. })
  149. /**
  150. * \brief Store a double-precision value from float point freg into memory using fsd instruction
  151. * \details The FSD instruction stores double-precision value from floating point register to memory
  152. * \param [in] freg The floating point register(f0-f31), eg. FREG(0), f0
  153. * \param [in] addr The memory base address, 8 byte aligned required
  154. * \param [in] ofs a 12-bit immediate signed byte offset value, should be an const value
  155. * \attention
  156. * * Function only available for double precision floating point unit, FLEN = 64
  157. * \remarks
  158. * * FLD and FSD operations need to make sure the address is 8 bytes aligned,
  159. * otherwise it will cause exception code 4(Load address misaligned) or 6 (Store/AMO address misaligned)
  160. * * FLD and FSD do not modify the bits being transferred; in particular, the payloads of non-canonical
  161. * NaNs are preserved.
  162. *
  163. */
  164. #define __RV_FSD(freg, addr, ofs) \
  165. ({ \
  166. rv_csr_t __addr = (rv_csr_t)(addr); \
  167. __ASM volatile("fsd " STRINGIFY(freg) ", %0(%1) " \
  168. : : "I"(ofs), "r"(__addr) \
  169. : "memory"); \
  170. })
  171. /**
  172. * \def __RV_FLOAD
  173. * \brief Load a float point value from memory into float point register freg using flw/fld instruction
  174. * \details
  175. * * For Single-Precison Floating-Point Mode(__FPU_PRESENT == 1, __RISCV_FLEN == 32):
  176. * It will call \ref __RV_FLW to load a single-precision floating point value from memory to floating point register
  177. * * For Double-Precison Floating-Point Mode(__FPU_PRESENT == 2, __RISCV_FLEN == 64):
  178. * It will call \ref __RV_FLD to load a double-precision floating point value from memory to floating point register
  179. *
  180. * \attention
  181. * Function behaviour is different for __FPU_PRESENT = 1 or 2, please see the real function this macro represent
  182. */
  183. /**
  184. * \def __RV_FSTORE
  185. * \brief Store a float value from float point freg into memory using fsw/fsd instruction
  186. * \details
  187. * * For Single-Precison Floating-Point Mode(__FPU_PRESENT == 1, __RISCV_FLEN == 32):
  188. * It will call \ref __RV_FSW to store floating point register into memory
  189. * * For Double-Precison Floating-Point Mode(__FPU_PRESENT == 2, __RISCV_FLEN == 64):
  190. * It will call \ref __RV_FSD to store floating point register into memory
  191. *
  192. * \attention
  193. * Function behaviour is different for __FPU_PRESENT = 1 or 2, please see the real function this macro represent
  194. */
  195. #if __FPU_PRESENT == 1
  196. #define __RV_FLOAD __RV_FLW
  197. #define __RV_FSTORE __RV_FSW
  198. /** \brief Type of FPU register, depends on the FLEN defined in RISC-V */
  199. typedef uint32_t rv_fpu_t;
  200. #elif __FPU_PRESENT == 2
  201. #define __RV_FLOAD __RV_FLD
  202. #define __RV_FSTORE __RV_FSD
  203. /** \brief Type of FPU register, depends on the FLEN defined in RISC-V */
  204. typedef uint64_t rv_fpu_t;
  205. #endif /* __FPU_PRESENT == 2 */
  206. /**
  207. * \brief Save FPU context into variables for interrupt nesting
  208. * \details
  209. * This macro is used to declare variables which are used for saving
  210. * FPU context, and it will store the nessary fpu registers into
  211. * these variables, it need to be used in a interrupt when in this
  212. * interrupt fpu registers are used.
  213. * \remarks
  214. * - It need to be used together with \ref RESTORE_FPU_CONTEXT
  215. * - Don't use variable names __fpu_context in your ISR code
  216. * - If you isr code will use fpu registers, and this interrupt is nested.
  217. * Then you can do it like this:
  218. * \code
  219. * void eclic_mtip_handler(void)
  220. * {
  221. * // !!!Interrupt is enabled here!!!
  222. * // !!!Higher priority interrupt could nest it!!!
  223. *
  224. * // Necessary only when you need to use fpu registers
  225. * // in this isr handler functions
  226. * SAVE_FPU_CONTEXT();
  227. *
  228. * // put you own interrupt handling code here
  229. *
  230. * // pair of SAVE_FPU_CONTEXT()
  231. * RESTORE_FPU_CONTEXT();
  232. * }
  233. * \endcode
  234. */
  235. #define SAVE_FPU_CONTEXT() \
  236. rv_fpu_t __fpu_context[20]; \
  237. __RV_FSTORE(FREG(0), __fpu_context, 0 << LOG_FPREGBYTES); \
  238. __RV_FSTORE(FREG(1), __fpu_context, 1 << LOG_FPREGBYTES); \
  239. __RV_FSTORE(FREG(2), __fpu_context, 2 << LOG_FPREGBYTES); \
  240. __RV_FSTORE(FREG(3), __fpu_context, 3 << LOG_FPREGBYTES); \
  241. __RV_FSTORE(FREG(4), __fpu_context, 4 << LOG_FPREGBYTES); \
  242. __RV_FSTORE(FREG(5), __fpu_context, 5 << LOG_FPREGBYTES); \
  243. __RV_FSTORE(FREG(6), __fpu_context, 6 << LOG_FPREGBYTES); \
  244. __RV_FSTORE(FREG(7), __fpu_context, 7 << LOG_FPREGBYTES); \
  245. __RV_FSTORE(FREG(10), __fpu_context, 8 << LOG_FPREGBYTES); \
  246. __RV_FSTORE(FREG(11), __fpu_context, 9 << LOG_FPREGBYTES); \
  247. __RV_FSTORE(FREG(12), __fpu_context, 10 << LOG_FPREGBYTES); \
  248. __RV_FSTORE(FREG(13), __fpu_context, 11 << LOG_FPREGBYTES); \
  249. __RV_FSTORE(FREG(14), __fpu_context, 12 << LOG_FPREGBYTES); \
  250. __RV_FSTORE(FREG(15), __fpu_context, 13 << LOG_FPREGBYTES); \
  251. __RV_FSTORE(FREG(16), __fpu_context, 14 << LOG_FPREGBYTES); \
  252. __RV_FSTORE(FREG(17), __fpu_context, 15 << LOG_FPREGBYTES); \
  253. __RV_FSTORE(FREG(28), __fpu_context, 16 << LOG_FPREGBYTES); \
  254. __RV_FSTORE(FREG(29), __fpu_context, 17 << LOG_FPREGBYTES); \
  255. __RV_FSTORE(FREG(30), __fpu_context, 18 << LOG_FPREGBYTES); \
  256. __RV_FSTORE(FREG(31), __fpu_context, 19 << LOG_FPREGBYTES);
  257. /**
  258. * \brief Restore necessary fpu registers from variables for interrupt nesting
  259. * \details
  260. * This macro is used restore necessary fpu registers from pre-defined variables
  261. * in \ref SAVE_FPU_CONTEXT macro.
  262. * \remarks
  263. * - It need to be used together with \ref SAVE_FPU_CONTEXT
  264. */
  265. #define RESTORE_FPU_CONTEXT() \
  266. __RV_FLOAD(FREG(0), __fpu_context, 0 << LOG_FPREGBYTES); \
  267. __RV_FLOAD(FREG(1), __fpu_context, 1 << LOG_FPREGBYTES); \
  268. __RV_FLOAD(FREG(2), __fpu_context, 2 << LOG_FPREGBYTES); \
  269. __RV_FLOAD(FREG(3), __fpu_context, 3 << LOG_FPREGBYTES); \
  270. __RV_FLOAD(FREG(4), __fpu_context, 4 << LOG_FPREGBYTES); \
  271. __RV_FLOAD(FREG(5), __fpu_context, 5 << LOG_FPREGBYTES); \
  272. __RV_FLOAD(FREG(6), __fpu_context, 6 << LOG_FPREGBYTES); \
  273. __RV_FLOAD(FREG(7), __fpu_context, 7 << LOG_FPREGBYTES); \
  274. __RV_FLOAD(FREG(10), __fpu_context, 8 << LOG_FPREGBYTES); \
  275. __RV_FLOAD(FREG(11), __fpu_context, 9 << LOG_FPREGBYTES); \
  276. __RV_FLOAD(FREG(12), __fpu_context, 10 << LOG_FPREGBYTES); \
  277. __RV_FLOAD(FREG(13), __fpu_context, 11 << LOG_FPREGBYTES); \
  278. __RV_FLOAD(FREG(14), __fpu_context, 12 << LOG_FPREGBYTES); \
  279. __RV_FLOAD(FREG(15), __fpu_context, 13 << LOG_FPREGBYTES); \
  280. __RV_FLOAD(FREG(16), __fpu_context, 14 << LOG_FPREGBYTES); \
  281. __RV_FLOAD(FREG(17), __fpu_context, 15 << LOG_FPREGBYTES); \
  282. __RV_FLOAD(FREG(28), __fpu_context, 16 << LOG_FPREGBYTES); \
  283. __RV_FLOAD(FREG(29), __fpu_context, 17 << LOG_FPREGBYTES); \
  284. __RV_FLOAD(FREG(30), __fpu_context, 18 << LOG_FPREGBYTES); \
  285. __RV_FLOAD(FREG(31), __fpu_context, 19 << LOG_FPREGBYTES);
  286. #else
  287. #define SAVE_FPU_CONTEXT()
  288. #define RESTORE_FPU_CONTEXT()
  289. #endif /* __FPU_PRESENT > 0 */
  290. /** @} */ /* End of Doxygen Group NMSIS_Core_FPU_Functions */
  291. #ifdef __cplusplus
  292. }
  293. #endif
  294. #endif /* __CORE_FEATURE_FPU_H__ */