NEORV32 Software Framework Documentation
The NEORV32 RISC-V Processor
Loading...
Searching...
No Matches
neorv32_zfinx_extension_intrinsics.h
Go to the documentation of this file.
1// ================================================================================ //
2// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
3// Copyright (c) NEORV32 contributors. //
4// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
5// Licensed under the BSD-3-Clause license, see LICENSE for details. //
6// SPDX-License-Identifier: BSD-3-Clause //
7// ================================================================================ //
8
9
10/**********************************************************************/
24#ifndef NEORV32_ZFINX_EXTENSION_INTRINSICS_H
25#define NEORV32_ZFINX_EXTENSION_INTRINSICS_H
26
27#define __USE_GNU
28#define _GNU_SOURCE
29#include <float.h>
30#include <math.h>
31
32
33/**********************************************************************/
36typedef union
37{
38 uint32_t binary_value;
39 float float_value;
41
42
43// ################################################################################################
44// Helper functions
45// ################################################################################################
46
47/**********************************************************************/
55float subnormal_flush(float tmp) {
56
57 float res = tmp;
58
59 // flush to zero if subnormal
60 if (fpclassify(tmp) == FP_SUBNORMAL) {
61 if (signbit(tmp) != 0) {
62 res = -0.0f;
63 }
64 else {
65 res = +0.0f;
66 }
67 }
68
69 return res;
70}
71
72
73// ################################################################################################
74// "Intrinsics"
75// ################################################################################################
76
77/**********************************************************************/
84inline float __attribute__ ((always_inline)) riscv_intrinsic_fadds(float rs1, float rs2) {
85
86 float_conv_t opa, opb, res;
87 opa.float_value = rs1;
88 opb.float_value = rs2;
89
90 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0000000, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
91 return res.float_value;
92}
93
94
95/**********************************************************************/
102inline float __attribute__ ((always_inline)) riscv_intrinsic_fsubs(float rs1, float rs2) {
103
104 float_conv_t opa, opb, res;
105 opa.float_value = rs1;
106 opb.float_value = rs2;
107
108 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0000100, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
109 return res.float_value;
110}
111
112
113/**********************************************************************/
120inline float __attribute__ ((always_inline)) riscv_intrinsic_fmuls(float rs1, float rs2) {
121
122 float_conv_t opa, opb, res;
123 opa.float_value = rs1;
124 opb.float_value = rs2;
125
126 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0001000, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
127 return res.float_value;
128}
129
130
131/**********************************************************************/
138inline float __attribute__ ((always_inline)) riscv_intrinsic_fmins(float rs1, float rs2) {
139
140 float_conv_t opa, opb, res;
141 opa.float_value = rs1;
142 opb.float_value = rs2;
143
144 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0010100, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
145 return res.float_value;
146}
147
148
149/**********************************************************************/
156inline float __attribute__ ((always_inline)) riscv_intrinsic_fmaxs(float rs1, float rs2) {
157
158 float_conv_t opa, opb, res;
159 opa.float_value = rs1;
160 opb.float_value = rs2;
161
162 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0010100, opb.binary_value, opa.binary_value, 0b001, 0b1010011);
163 return res.float_value;
164}
165
166
167/**********************************************************************/
173inline uint32_t __attribute__ ((always_inline)) riscv_intrinsic_fcvt_wus(float rs1) {
174
175 float_conv_t opa;
176 opa.float_value = rs1;
177
178 return CUSTOM_INSTR_R2_TYPE(0b1100000, 0b00001, opa.binary_value, 0b000, 0b1010011);
179}
180
181
182/**********************************************************************/
188inline int32_t __attribute__ ((always_inline)) riscv_intrinsic_fcvt_ws(float rs1) {
189
190 float_conv_t opa;
191 opa.float_value = rs1;
192
193 return (int32_t)CUSTOM_INSTR_R2_TYPE(0b1100000, 0b00000, opa.binary_value, 0b000, 0b1010011);
194}
195
196
197/**********************************************************************/
203inline float __attribute__ ((always_inline)) riscv_intrinsic_fcvt_swu(uint32_t rs1) {
204
205 float_conv_t res;
206
207 res.binary_value = CUSTOM_INSTR_R2_TYPE(0b1101000, 0b00001, rs1, 0b000, 0b1010011);
208 return res.float_value;
209}
210
211
212/**********************************************************************/
218inline float __attribute__ ((always_inline)) riscv_intrinsic_fcvt_sw(int32_t rs1) {
219
220 float_conv_t res;
221
222 res.binary_value = CUSTOM_INSTR_R2_TYPE(0b1101000, 0b00000, rs1, 0b000, 0b1010011);
223 return res.float_value;
224}
225
226
227/**********************************************************************/
234inline uint32_t __attribute__ ((always_inline)) riscv_intrinsic_feqs(float rs1, float rs2) {
235
236 float_conv_t opa, opb;
237 opa.float_value = rs1;
238 opb.float_value = rs2;
239
240 return CUSTOM_INSTR_R3_TYPE(0b1010000, opb.binary_value, opa.binary_value, 0b010, 0b1010011);
241}
242
243
244/**********************************************************************/
251inline uint32_t __attribute__ ((always_inline)) riscv_intrinsic_flts(float rs1, float rs2) {
252
253 float_conv_t opa, opb;
254 opa.float_value = rs1;
255 opb.float_value = rs2;
256
257 return CUSTOM_INSTR_R3_TYPE(0b1010000, opb.binary_value, opa.binary_value, 0b001, 0b1010011);
258}
259
260
261/**********************************************************************/
268inline uint32_t __attribute__ ((always_inline)) riscv_intrinsic_fles(float rs1, float rs2) {
269
270 float_conv_t opa, opb;
271 opa.float_value = rs1;
272 opb.float_value = rs2;
273
274 return CUSTOM_INSTR_R3_TYPE(0b1010000, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
275}
276
277
278/**********************************************************************/
285inline float __attribute__ ((always_inline)) riscv_intrinsic_fsgnjs(float rs1, float rs2) {
286
287 float_conv_t opa, opb, res;
288 opa.float_value = rs1;
289 opb.float_value = rs2;
290
291 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0010000, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
292 return res.float_value;
293}
294
295
296/**********************************************************************/
303inline float __attribute__ ((always_inline)) riscv_intrinsic_fsgnjns(float rs1, float rs2) {
304
305 float_conv_t opa, opb, res;
306 opa.float_value = rs1;
307 opb.float_value = rs2;
308
309 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0010000, opb.binary_value, opa.binary_value, 0b001, 0b1010011);
310 return res.float_value;
311}
312
313
314/**********************************************************************/
321inline float __attribute__ ((always_inline)) riscv_intrinsic_fsgnjxs(float rs1, float rs2) {
322
323 float_conv_t opa, opb, res;
324 opa.float_value = rs1;
325 opb.float_value = rs2;
326
327 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0010000, opb.binary_value, opa.binary_value, 0b010, 0b1010011);
328 return res.float_value;
329}
330
331
332/**********************************************************************/
338inline uint32_t __attribute__ ((always_inline)) riscv_intrinsic_fclasss(float rs1) {
339
340 float_conv_t opa;
341 opa.float_value = rs1;
342
343 return CUSTOM_INSTR_R2_TYPE(0b1110000, 0b00000, opa.binary_value, 0b001, 0b1010011);
344}
345
346
347// ################################################################################################
348// !!! UNSUPPORTED instructions !!!
349// ################################################################################################
350
351/**********************************************************************/
360inline float __attribute__ ((always_inline)) riscv_intrinsic_fdivs(float rs1, float rs2) {
361
362 float_conv_t opa, opb, res;
363 opa.float_value = rs1;
364 opb.float_value = rs2;
365
366 res.binary_value = CUSTOM_INSTR_R3_TYPE(0b0001100, opb.binary_value, opa.binary_value, 0b000, 0b1010011);
367 return res.float_value;
368}
369
370
371/**********************************************************************/
379inline float __attribute__ ((always_inline)) riscv_intrinsic_fsqrts(float rs1) {
380
381 float_conv_t opa, res;
382 opa.float_value = rs1;
383
384 res.binary_value = CUSTOM_INSTR_R2_TYPE(0b0101100, 0b00000, opa.binary_value, 0b000, 0b1010011);
385 return res.float_value;
386}
387
388
389/**********************************************************************/
399inline float __attribute__ ((always_inline)) riscv_intrinsic_fmadds(float rs1, float rs2, float rs3) {
400
401 float_conv_t opa, opb, opc, res;
402 opa.float_value = rs1;
403 opb.float_value = rs2;
404 opc.float_value = rs3;
405
406 res.binary_value = CUSTOM_INSTR_R4_TYPE(opc.binary_value, opb.binary_value, opa.binary_value, 0b000, 0b1000011);
407 return res.float_value;
408}
409
410
411/**********************************************************************/
421inline float __attribute__ ((always_inline)) riscv_intrinsic_fmsubs(float rs1, float rs2, float rs3) {
422
423 float_conv_t opa, opb, opc, res;
424 opa.float_value = rs1;
425 opb.float_value = rs2;
426 opc.float_value = rs3;
427
428 res.binary_value = CUSTOM_INSTR_R4_TYPE(opc.binary_value, opb.binary_value, opa.binary_value, 0b000, 0b1000111);
429 return res.float_value;
430}
431
432
433/**********************************************************************/
443inline float __attribute__ ((always_inline)) riscv_intrinsic_fnmsubs(float rs1, float rs2, float rs3) {
444
445 float_conv_t opa, opb, opc, res;
446 opa.float_value = rs1;
447 opb.float_value = rs2;
448 opc.float_value = rs3;
449
450 res.binary_value = CUSTOM_INSTR_R4_TYPE(opc.binary_value, opb.binary_value, opa.binary_value, 0b000, 0b1001011);
451 return res.float_value;
452}
453
454
455/**********************************************************************/
465inline float __attribute__ ((always_inline)) riscv_intrinsic_fnmadds(float rs1, float rs2, float rs3) {
466
467 float_conv_t opa, opb, opc, res;
468 opa.float_value = rs1;
469 opb.float_value = rs2;
470 opc.float_value = rs3;
471
472 res.binary_value = CUSTOM_INSTR_R4_TYPE(opc.binary_value, opb.binary_value, opa.binary_value, 0b000, 0b1001111);
473 return res.float_value;
474}
475
476
477// ################################################################################################
478// Emulation functions
479// ################################################################################################
480
481/**********************************************************************/
488float __attribute__ ((noinline)) riscv_emulate_fadds(float rs1, float rs2) {
489
490 float opa = subnormal_flush(rs1);
491 float opb = subnormal_flush(rs2);
492
493 float res = opa + opb;
494
495 // make NAN canonical
496 if (fpclassify(res) == FP_NAN) {
497 res = NAN;
498 }
499
500 return subnormal_flush(res);
501}
502
503
504/**********************************************************************/
511float __attribute__ ((noinline)) riscv_emulate_fsubs(float rs1, float rs2) {
512
513 float opa = subnormal_flush(rs1);
514 float opb = subnormal_flush(rs2);
515
516 float res = opa - opb;
517
518 // make NAN canonical
519 if (fpclassify(res) == FP_NAN) {
520 res = NAN;
521 }
522
523 return subnormal_flush(res);
524}
525
526
527/**********************************************************************/
534float __attribute__ ((noinline)) riscv_emulate_fmuls(float rs1, float rs2) {
535
536 float opa = subnormal_flush(rs1);
537 float opb = subnormal_flush(rs2);
538
539 float res = opa * opb;
540 return subnormal_flush(res);
541}
542
543
544/**********************************************************************/
551float __attribute__ ((noinline)) riscv_emulate_fmins(float rs1, float rs2) {
552
553 float opa = subnormal_flush(rs1);
554 float opb = subnormal_flush(rs2);
555
556 union {
557 uint32_t binary_value;
558 float float_value;
559 } tmp_a, tmp_b;
560
561 if ((fpclassify(opa) == FP_NAN) && (fpclassify(opb) == FP_NAN)) {
562 return nanf("");
563 }
564
565 if (fpclassify(opa) == FP_NAN) {
566 return opb;
567 }
568
569 if (fpclassify(opb) == FP_NAN) {
570 return opa;
571 }
572
573 // RISC-V spec: -0 < +0
574 tmp_a.float_value = opa;
575 tmp_b.float_value = opb;
576 if (((tmp_a.binary_value == 0x80000000) && (tmp_b.binary_value == 0x00000000)) ||
577 ((tmp_a.binary_value == 0x00000000) && (tmp_b.binary_value == 0x80000000))) {
578 return -0.0f;
579 }
580
581 return fmin(opa, opb);
582}
583
584
585/**********************************************************************/
592float __attribute__ ((noinline)) riscv_emulate_fmaxs(float rs1, float rs2) {
593
594 float opa = subnormal_flush(rs1);
595 float opb = subnormal_flush(rs2);
596
597 union {
598 uint32_t binary_value;
599 float float_value;
600 } tmp_a, tmp_b;
601
602
603 if ((fpclassify(opa) == FP_NAN) && (fpclassify(opb) == FP_NAN)) {
604 return nanf("");
605 }
606
607 if (fpclassify(opa) == FP_NAN) {
608 return opb;
609 }
610
611 if (fpclassify(opb) == FP_NAN) {
612 return opa;
613 }
614
615 // RISC-V spec: -0 < +0
616 tmp_a.float_value = opa;
617 tmp_b.float_value = opb;
618 if (((tmp_a.binary_value == 0x80000000) && (tmp_b.binary_value == 0x00000000)) ||
619 ((tmp_a.binary_value == 0x00000000) && (tmp_b.binary_value == 0x80000000))) {
620 return +0.0f;
621 }
622
623 return fmax(opa, opb);
624}
625
626
627/**********************************************************************/
633uint32_t __attribute__ ((noinline)) riscv_emulate_fcvt_wus(float rs1) {
634
635 float opa = subnormal_flush(rs1);
636
637 return (uint32_t)rint(opa);
638}
639
640
641/**********************************************************************/
647int32_t __attribute__ ((noinline)) riscv_emulate_fcvt_ws(float rs1) {
648
649 float opa = subnormal_flush(rs1);
650
651 return (int32_t)rint(opa);
652}
653
654
655/**********************************************************************/
661float __attribute__ ((noinline)) riscv_emulate_fcvt_swu(uint32_t rs1) {
662
663 return (float)rs1;
664}
665
666
667/**********************************************************************/
673float __attribute__ ((noinline)) riscv_emulate_fcvt_sw(int32_t rs1) {
674
675 return (float)rs1;
676}
677
678
679/**********************************************************************/
686uint32_t __attribute__ ((noinline)) riscv_emulate_feqs(float rs1, float rs2) {
687
688 float opa = subnormal_flush(rs1);
689 float opb = subnormal_flush(rs2);
690
691 if ((fpclassify(opa) == FP_NAN) || (fpclassify(opb) == FP_NAN)) {
692 return 0;
693 }
694
695 if isless(opa, opb) {
696 return 0;
697 }
698 else if isgreater(opa, opb) {
699 return 0;
700 }
701 else {
702 return 1;
703 }
704}
705
706
707/**********************************************************************/
714uint32_t __attribute__ ((noinline)) riscv_emulate_flts(float rs1, float rs2) {
715
716 float opa = subnormal_flush(rs1);
717 float opb = subnormal_flush(rs2);
718
719 if ((fpclassify(opa) == FP_NAN) || (fpclassify(opb) == FP_NAN)) {
720 return 0;
721 }
722
723 if isless(opa, opb) {
724 return 1;
725 }
726 else {
727 return 0;
728 }
729}
730
731
732/**********************************************************************/
739uint32_t __attribute__ ((noinline)) riscv_emulate_fles(float rs1, float rs2) {
740
741 float opa = subnormal_flush(rs1);
742 float opb = subnormal_flush(rs2);
743
744 if ((fpclassify(opa) == FP_NAN) || (fpclassify(opb) == FP_NAN)) {
745 return 0;
746 }
747
748 if islessequal(opa, opb) {
749 return 1;
750 }
751 else {
752 return 0;
753 }
754}
755
756
757/**********************************************************************/
764float __attribute__ ((noinline)) riscv_emulate_fsgnjs(float rs1, float rs2) {
765
766 float opa = rs1;
767 float opb = rs2;
768
769 int sign_1 = (int)signbit(opa);
770 int sign_2 = (int)signbit(opb);
771 float res = 0;
772
773 if (sign_2 != 0) { // opb is negative
774 if (sign_1 == 0) {
775 res = -opa;
776 }
777 else {
778 res = opa;
779 }
780 }
781 else { // opb is positive
782 if (sign_1 == 0) {
783 res = opa;
784 }
785 else {
786 res = -opa;
787 }
788 }
789
790 return res;
791}
792
793
794/**********************************************************************/
801float __attribute__ ((noinline)) riscv_emulate_fsgnjns(float rs1, float rs2) {
802
803 float opa = rs1;
804 float opb = rs2;
805
806 int sign_1 = (int)signbit(opa);
807 int sign_2 = (int)signbit(opb);
808 float res = 0;
809
810 if (sign_2 != 0) { // opb is negative
811 if (sign_1 == 0) {
812 res = opa;
813 }
814 else {
815 res = -opa;
816 }
817 }
818 else { // opb is positive
819 if (sign_1 == 0) {
820 res = -opa;
821 }
822 else {
823 res = opa;
824 }
825 }
826
827 return res;
828}
829
830
831/**********************************************************************/
838float __attribute__ ((noinline)) riscv_emulate_fsgnjxs(float rs1, float rs2) {
839
840 float opa = rs1;
841 float opb = rs2;
842
843 int sign_1 = (int)signbit(opa);
844 int sign_2 = (int)signbit(opb);
845 float res = 0;
846
847 if (((sign_1 == 0) && (sign_2 != 0)) || ((sign_1 != 0) && (sign_2 == 0))) {
848 if (sign_1 == 0) {
849 res = -opa;
850 }
851 else {
852 res = opa;
853 }
854 }
855 else {
856 if (sign_1 == 0) {
857 res = opa;
858 }
859 else {
860 res = -opa;
861 }
862 }
863
864 return res;
865}
866
867
868/**********************************************************************/
874uint32_t __attribute__ ((noinline)) riscv_emulate_fclasss(float rs1) {
875
876 float opa = rs1;
877
878 union {
879 uint32_t binary_value;
880 float float_value;
881 } aux;
882
883 // RISC-V classify result layout
884 const uint32_t CLASS_NEG_INF = 1 << 0; // negative infinity
885 const uint32_t CLASS_NEG_NORM = 1 << 1; // negative normal number
886 const uint32_t CLASS_NEG_DENORM = 1 << 2; // negative subnormal number
887 const uint32_t CLASS_NEG_ZERO = 1 << 3; // negative zero
888 const uint32_t CLASS_POS_ZERO = 1 << 4; // positive zero
889 const uint32_t CLASS_POS_DENORM = 1 << 5; // positive subnormal number
890 const uint32_t CLASS_POS_NORM = 1 << 6; // positive normal number
891 const uint32_t CLASS_POS_INF = 1 << 7; // positive infinity
892 const uint32_t CLASS_SNAN = 1 << 8; // signaling NaN (sNaN)
893 const uint32_t CLASS_QNAN = 1 << 9; // quiet NaN (qNaN)
894
895 int tmp = fpclassify(opa);
896 int sgn = (int)signbit(opa);
897
898 uint32_t res = 0;
899
900 // infinity
901 if (tmp == FP_INFINITE) {
902 if (sgn) { res |= CLASS_NEG_INF; }
903 else { res |= CLASS_POS_INF; }
904 }
905
906 // zero
907 if (tmp == FP_ZERO) {
908 if (sgn) { res |= CLASS_NEG_ZERO; }
909 else { res |= CLASS_POS_ZERO; }
910 }
911
912 // normal
913 if (tmp == FP_NORMAL) {
914 if (sgn) { res |= CLASS_NEG_NORM; }
915 else { res |= CLASS_POS_NORM; }
916 }
917
918 // subnormal
919 if (tmp == FP_SUBNORMAL) {
920 if (sgn) { res |= CLASS_NEG_DENORM; }
921 else { res |= CLASS_POS_DENORM; }
922 }
923
924 // NaN
925 if (tmp == FP_NAN) {
926 aux.float_value = opa;
927 if ((aux.binary_value >> 22) & 0b1) { // bit 22 (mantissa's MSB) is set -> canonical (quiet) NAN
928 res |= CLASS_QNAN;
929 }
930 else {
931 res |= CLASS_SNAN;
932 }
933 }
934
935 return res;
936}
937
938
939/**********************************************************************/
946float __attribute__ ((noinline)) riscv_emulate_fdivs(float rs1, float rs2) {
947
948 float opa = subnormal_flush(rs1);
949 float opb = subnormal_flush(rs2);
950
951 float res = opa / opb;
952 return subnormal_flush(res);
953}
954
955
956/**********************************************************************/
962float __attribute__ ((noinline)) riscv_emulate_fsqrts(float rs1) {
963
964 float opa = subnormal_flush(rs1);
965
966 float res = sqrtf(opa);
967 return subnormal_flush(res);
968}
969
970
971/**********************************************************************/
981float __attribute__ ((noinline)) riscv_emulate_fmadds(float rs1, float rs2, float rs3) {
982
983 float opa = subnormal_flush(rs1);
984 float opb = subnormal_flush(rs2);
985 float opc = subnormal_flush(rs3);
986
987 float res = (opa * opb) + opc;
988 return subnormal_flush(res);
989}
990
991
992/**********************************************************************/
1000float __attribute__ ((noinline)) riscv_emulate_fmsubs(float rs1, float rs2, float rs3) {
1001
1002 float opa = subnormal_flush(rs1);
1003 float opb = subnormal_flush(rs2);
1004 float opc = subnormal_flush(rs3);
1005
1006 float res = (opa * opb) - opc;
1007 return subnormal_flush(res);
1008}
1009
1010
1011/**********************************************************************/
1019float __attribute__ ((noinline)) riscv_emulate_fnmsubs(float rs1, float rs2, float rs3) {
1020
1021 float opa = subnormal_flush(rs1);
1022 float opb = subnormal_flush(rs2);
1023 float opc = subnormal_flush(rs3);
1024
1025 float res = -(opa * opb) + opc;
1026 return subnormal_flush(res);
1027}
1028
1029
1030/**********************************************************************/
1038float __attribute__ ((noinline)) riscv_emulate_fnmadds(float rs1, float rs2, float rs3) {
1039
1040 float opa = subnormal_flush(rs1);
1041 float opb = subnormal_flush(rs2);
1042 float opc = subnormal_flush(rs3);
1043
1044 float res = -(opa * opb) - opc;
1045 return subnormal_flush(res);
1046}
1047
1048
1049#endif // NEORV32_ZFINX_EXTENSION_INTRINSICS_H
1050
Definition neorv32_zfinx_extension_intrinsics.h:76
uint32_t binary_value
Definition neorv32_zfinx_extension_intrinsics.h:77
float float_value
Definition neorv32_zfinx_extension_intrinsics.h:78