xref: /openbmc/qemu/target/tricore/fpu_helper.c (revision 2e1cacfb)
1 /*
2  *  TriCore emulation for qemu: fpu helper.
3  *
4  *  Copyright (c) 2016 Bastian Koppelmann University of Paderborn
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "cpu.h"
22 #include "exec/helper-proto.h"
23 #include "fpu/softfloat.h"
24 
25 #define QUIET_NAN 0x7fc00000
26 #define ADD_NAN   0x7fc00001
27 #define SQRT_NAN  0x7fc00004
28 #define DIV_NAN   0x7fc00008
29 #define MUL_NAN   0x7fc00002
30 #define FPU_FS PSW_USB_C
31 #define FPU_FI PSW_USB_V
32 #define FPU_FV PSW_USB_SV
33 #define FPU_FZ PSW_USB_AV
34 #define FPU_FU PSW_USB_SAV
35 
36 #define float32_sqrt_nan make_float32(SQRT_NAN)
37 #define float32_quiet_nan make_float32(QUIET_NAN)
38 
39 /* we don't care about input_denormal */
40 static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
41 {
42     return get_float_exception_flags(&env->fp_status)
43            & (float_flag_invalid
44               | float_flag_overflow
45               | float_flag_underflow
46               | float_flag_output_denormal
47               | float_flag_divbyzero
48               | float_flag_inexact);
49 }
50 
51 static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2,
52                                            float32 arg3, float32 result,
53                                            uint32_t muladd_negate_c)
54 {
55     uint32_t aSign, bSign, cSign;
56     uint32_t aExp, bExp, cExp;
57 
58     if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) ||
59         float32_is_any_nan(arg3)) {
60         return QUIET_NAN;
61     } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) {
62         return MUL_NAN;
63     } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) {
64         return MUL_NAN;
65     } else {
66         aSign = arg1 >> 31;
67         bSign = arg2 >> 31;
68         cSign = arg3 >> 31;
69 
70         aExp = (arg1 >> 23) & 0xff;
71         bExp = (arg2 >> 23) & 0xff;
72         cExp = (arg3 >> 23) & 0xff;
73 
74         if (muladd_negate_c) {
75             cSign ^= 1;
76         }
77         if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) {
78             if (aSign ^ bSign ^ cSign) {
79                 return ADD_NAN;
80             }
81         }
82     }
83 
84     return result;
85 }
86 
87 static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags)
88 {
89     uint8_t some_excp = 0;
90     set_float_exception_flags(0, &env->fp_status);
91 
92     if (flags & float_flag_invalid) {
93         env->FPU_FI = 1 << 31;
94         some_excp = 1;
95     }
96 
97     if (flags & float_flag_overflow) {
98         env->FPU_FV = 1 << 31;
99         some_excp = 1;
100     }
101 
102     if (flags & float_flag_underflow || flags & float_flag_output_denormal) {
103         env->FPU_FU = 1 << 31;
104         some_excp = 1;
105     }
106 
107     if (flags & float_flag_divbyzero) {
108         env->FPU_FZ = 1 << 31;
109         some_excp = 1;
110     }
111 
112     if (flags & float_flag_inexact || flags & float_flag_output_denormal) {
113         env->PSW |= 1 << 26;
114         some_excp = 1;
115     }
116 
117     env->FPU_FS = some_excp;
118 }
119 
120 #define FADD_SUB(op)                                                           \
121 uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2)          \
122 {                                                                              \
123     float32 arg1 = make_float32(r1);                                           \
124     float32 arg2 = make_float32(r2);                                           \
125     uint32_t flags;                                                            \
126     float32 f_result;                                                          \
127                                                                                \
128     f_result = float32_##op(arg2, arg1, &env->fp_status);                      \
129     flags = f_get_excp_flags(env);                                             \
130     if (flags) {                                                               \
131         /* If the output is a NaN, but the inputs aren't,                      \
132            we return a unique value.  */                                       \
133         if ((flags & float_flag_invalid)                                       \
134             && !float32_is_any_nan(arg1)                                       \
135             && !float32_is_any_nan(arg2)) {                                    \
136             f_result = ADD_NAN;                                                \
137         }                                                                      \
138         f_update_psw_flags(env, flags);                                        \
139     } else {                                                                   \
140         env->FPU_FS = 0;                                                       \
141     }                                                                          \
142     return (uint32_t)f_result;                                                 \
143 }
144 FADD_SUB(add)
145 FADD_SUB(sub)
146 
147 uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
148 {
149     uint32_t flags;
150     float32 arg1 = make_float32(r1);
151     float32 arg2 = make_float32(r2);
152     float32 f_result;
153 
154     f_result = float32_mul(arg1, arg2, &env->fp_status);
155 
156     flags = f_get_excp_flags(env);
157     if (flags) {
158         /* If the output is a NaN, but the inputs aren't,
159            we return a unique value.  */
160         if ((flags & float_flag_invalid)
161             && !float32_is_any_nan(arg1)
162             && !float32_is_any_nan(arg2)) {
163                 f_result = MUL_NAN;
164         }
165         f_update_psw_flags(env, flags);
166     } else {
167         env->FPU_FS = 0;
168     }
169     return (uint32_t)f_result;
170 
171 }
172 
173 /*
174  * Target TriCore QSEED.F significand Lookup Table
175  *
176  * The QSEED.F output significand depends on the least-significant
177  * exponent bit and the 6 most-significant significand bits.
178  *
179  * IEEE 754 float datatype
180  * partitioned into Sign (S), Exponent (E) and Significand (M):
181  *
182  * S   E E E E E E E E   M M M M M M ...
183  *    |             |               |
184  *    +------+------+-------+-------+
185  *           |              |
186  *          for        lookup table
187  *      calculating     index for
188  *        output E       output M
189  *
190  * This lookup table was extracted by analyzing QSEED output
191  * from the real hardware
192  */
193 static const uint8_t target_qseed_significand_table[128] = {
194     253, 252, 245, 244, 239, 238, 231, 230, 225, 224, 217, 216,
195     211, 210, 205, 204, 201, 200, 195, 194, 189, 188, 185, 184,
196     179, 178, 175, 174, 169, 168, 165, 164, 161, 160, 157, 156,
197     153, 152, 149, 148, 145, 144, 141, 140, 137, 136, 133, 132,
198     131, 130, 127, 126, 123, 122, 121, 120, 117, 116, 115, 114,
199     111, 110, 109, 108, 103, 102, 99, 98, 93, 92, 89, 88, 83,
200     82, 79, 78, 75, 74, 71, 70, 67, 66, 63, 62, 59, 58, 55,
201     54, 53, 52, 49, 48, 45, 44, 43, 42, 39, 38, 37, 36, 33,
202     32, 31, 30, 27, 26, 25, 24, 23, 22, 19, 18, 17, 16, 15,
203     14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2
204 };
205 
206 uint32_t helper_qseed(CPUTriCoreState *env, uint32_t r1)
207 {
208     uint32_t arg1, S, E, M, E_minus_one, m_idx;
209     uint32_t new_E, new_M, new_S, result;
210 
211     arg1 = make_float32(r1);
212 
213     /* fetch IEEE-754 fields S, E and the uppermost 6-bit of M */
214     S = extract32(arg1, 31, 1);
215     E = extract32(arg1, 23, 8);
216     M = extract32(arg1, 17, 6);
217 
218     if (float32_is_any_nan(arg1)) {
219         result = float32_quiet_nan;
220     } else if (float32_is_zero_or_denormal(arg1)) {
221         if (float32_is_neg(arg1)) {
222             result = float32_infinity | (1 << 31);
223         } else {
224             result = float32_infinity;
225         }
226     } else if (float32_is_neg(arg1)) {
227         result = float32_sqrt_nan;
228     } else if (float32_is_infinity(arg1)) {
229         result = float32_zero;
230     } else {
231         E_minus_one = E - 1;
232         m_idx = ((E_minus_one & 1) << 6) | M;
233         new_S = S;
234         new_E = 0xBD - E_minus_one / 2;
235         new_M = target_qseed_significand_table[m_idx];
236 
237         result = 0;
238         result = deposit32(result, 31, 1, new_S);
239         result = deposit32(result, 23, 8, new_E);
240         result = deposit32(result, 15, 8, new_M);
241     }
242 
243     if (float32_is_signaling_nan(arg1, &env->fp_status)
244         || result == float32_sqrt_nan) {
245         env->FPU_FI = 1 << 31;
246         env->FPU_FS = 1;
247     } else {
248         env->FPU_FS = 0;
249     }
250 
251     return (uint32_t) result;
252 }
253 
254 uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
255 {
256     uint32_t flags;
257     float32 arg1 = make_float32(r1);
258     float32 arg2 = make_float32(r2);
259     float32 f_result;
260 
261     f_result = float32_div(arg1, arg2 , &env->fp_status);
262 
263     flags = f_get_excp_flags(env);
264     if (flags) {
265         /* If the output is a NaN, but the inputs aren't,
266            we return a unique value.  */
267         if ((flags & float_flag_invalid)
268             && !float32_is_any_nan(arg1)
269             && !float32_is_any_nan(arg2)) {
270                 f_result = DIV_NAN;
271         }
272         f_update_psw_flags(env, flags);
273     } else {
274         env->FPU_FS = 0;
275     }
276 
277     return (uint32_t)f_result;
278 }
279 
280 uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1,
281                       uint32_t r2, uint32_t r3)
282 {
283     uint32_t flags;
284     float32 arg1 = make_float32(r1);
285     float32 arg2 = make_float32(r2);
286     float32 arg3 = make_float32(r3);
287     float32 f_result;
288 
289     f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status);
290 
291     flags = f_get_excp_flags(env);
292     if (flags) {
293         if (flags & float_flag_invalid) {
294             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
295             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
296             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
297             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0);
298         }
299         f_update_psw_flags(env, flags);
300     } else {
301         env->FPU_FS = 0;
302     }
303     return (uint32_t)f_result;
304 }
305 
306 uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1,
307                       uint32_t r2, uint32_t r3)
308 {
309     uint32_t flags;
310     float32 arg1 = make_float32(r1);
311     float32 arg2 = make_float32(r2);
312     float32 arg3 = make_float32(r3);
313     float32 f_result;
314 
315     f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product,
316                               &env->fp_status);
317 
318     flags = f_get_excp_flags(env);
319     if (flags) {
320         if (flags & float_flag_invalid) {
321             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
322             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
323             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
324 
325             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1);
326         }
327         f_update_psw_flags(env, flags);
328     } else {
329         env->FPU_FS = 0;
330     }
331     return (uint32_t)f_result;
332 }
333 
334 uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
335 {
336     uint32_t result, flags;
337     float32 arg1 = make_float32(r1);
338     float32 arg2 = make_float32(r2);
339 
340     set_flush_inputs_to_zero(0, &env->fp_status);
341 
342     result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
343     result |= float32_is_denormal(arg1) << 4;
344     result |= float32_is_denormal(arg2) << 5;
345 
346     flags = f_get_excp_flags(env);
347     if (flags) {
348         f_update_psw_flags(env, flags);
349     } else {
350         env->FPU_FS = 0;
351     }
352 
353     set_flush_inputs_to_zero(1, &env->fp_status);
354     return result;
355 }
356 
357 uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg)
358 {
359     float32 f_arg = make_float32(arg);
360     int32_t result, flags;
361 
362     result = float32_to_int32(f_arg, &env->fp_status);
363 
364     flags = f_get_excp_flags(env);
365     if (flags) {
366         if (float32_is_any_nan(f_arg)) {
367             result = 0;
368         }
369         f_update_psw_flags(env, flags);
370     } else {
371         env->FPU_FS = 0;
372     }
373     return (uint32_t)result;
374 }
375 
376 uint32_t helper_hptof(CPUTriCoreState *env, uint32_t arg)
377 {
378     float16 f_arg = make_float16(arg);
379     uint32_t result = 0;
380     int32_t flags = 0;
381 
382     /*
383      * if we have any NAN we need to move the top 2 and lower 8 input mantissa
384      * bits to the top 2 and lower 8 output mantissa bits respectively.
385      * Softfloat on the other hand uses the top 10 mantissa bits.
386      */
387     if (float16_is_any_nan(f_arg)) {
388         if (float16_is_signaling_nan(f_arg, &env->fp_status)) {
389             flags |= float_flag_invalid;
390         }
391         result = 0;
392         result = float32_set_sign(result, f_arg >> 15);
393         result = deposit32(result, 23, 8, 0xff);
394         result = deposit32(result, 21, 2, extract32(f_arg, 8, 2));
395         result = deposit32(result, 0, 8, extract32(f_arg, 0, 8));
396     } else {
397         set_flush_inputs_to_zero(0, &env->fp_status);
398         result = float16_to_float32(f_arg, true, &env->fp_status);
399         set_flush_inputs_to_zero(1, &env->fp_status);
400         flags = f_get_excp_flags(env);
401     }
402 
403     if (flags) {
404         f_update_psw_flags(env, flags);
405     } else {
406         env->FPU_FS = 0;
407     }
408 
409     return result;
410 }
411 
412 uint32_t helper_ftohp(CPUTriCoreState *env, uint32_t arg)
413 {
414     float32 f_arg = make_float32(arg);
415     uint32_t result = 0;
416     int32_t flags = 0;
417 
418     /*
419      * if we have any NAN we need to move the top 2 and lower 8 input mantissa
420      * bits to the top 2 and lower 8 output mantissa bits respectively.
421      * Softfloat on the other hand uses the top 10 mantissa bits.
422      */
423     if (float32_is_any_nan(f_arg)) {
424         if (float32_is_signaling_nan(f_arg, &env->fp_status)) {
425             flags |= float_flag_invalid;
426         }
427         result = float16_set_sign(result, arg >> 31);
428         result = deposit32(result, 10, 5, 0x1f);
429         result = deposit32(result, 8, 2, extract32(arg, 21, 2));
430         result = deposit32(result, 0, 8, extract32(arg, 0, 8));
431         if (extract32(result, 0, 10) == 0) {
432             result |= (1 << 8);
433         }
434     } else {
435         set_flush_to_zero(0, &env->fp_status);
436         result = float32_to_float16(f_arg, true, &env->fp_status);
437         set_flush_to_zero(1, &env->fp_status);
438         flags = f_get_excp_flags(env);
439     }
440 
441     if (flags) {
442         f_update_psw_flags(env, flags);
443     } else {
444         env->FPU_FS = 0;
445     }
446 
447     return result;
448 }
449 
450 uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
451 {
452     float32 f_result;
453     uint32_t flags;
454     f_result = int32_to_float32(arg, &env->fp_status);
455 
456     flags = f_get_excp_flags(env);
457     if (flags) {
458         f_update_psw_flags(env, flags);
459     } else {
460         env->FPU_FS = 0;
461     }
462     return (uint32_t)f_result;
463 }
464 
465 uint32_t helper_utof(CPUTriCoreState *env, uint32_t arg)
466 {
467     float32 f_result;
468     uint32_t flags;
469 
470     f_result = uint32_to_float32(arg, &env->fp_status);
471 
472     flags = f_get_excp_flags(env);
473     if (flags) {
474         f_update_psw_flags(env, flags);
475     } else {
476         env->FPU_FS = 0;
477     }
478     return (uint32_t)f_result;
479 }
480 
481 uint32_t helper_ftoiz(CPUTriCoreState *env, uint32_t arg)
482 {
483     float32 f_arg = make_float32(arg);
484     uint32_t result;
485     int32_t flags;
486 
487     result = float32_to_int32_round_to_zero(f_arg, &env->fp_status);
488 
489     flags = f_get_excp_flags(env);
490     if (flags & float_flag_invalid) {
491         flags &= ~float_flag_inexact;
492         if (float32_is_any_nan(f_arg)) {
493             result = 0;
494         }
495     }
496 
497     if (flags) {
498         f_update_psw_flags(env, flags);
499     } else {
500         env->FPU_FS = 0;
501     }
502 
503     return result;
504 }
505 
506 uint32_t helper_ftou(CPUTriCoreState *env, uint32_t arg)
507 {
508     float32 f_arg = make_float32(arg);
509     uint32_t result;
510     int32_t flags = 0;
511 
512     result = float32_to_uint32(f_arg, &env->fp_status);
513 
514     flags = f_get_excp_flags(env);
515     if (flags & float_flag_invalid) {
516         flags &= ~float_flag_inexact;
517         if (float32_is_any_nan(f_arg)) {
518             result = 0;
519         }
520     /*
521      * we need to check arg < 0.0 before rounding as TriCore needs to raise
522      * float_flag_invalid as well. For instance, when we have a negative
523      * exponent and sign, softfloat would only raise float_flat_inexact.
524      */
525     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
526         flags = float_flag_invalid;
527         result = 0;
528     }
529 
530     if (flags) {
531         f_update_psw_flags(env, flags);
532     } else {
533         env->FPU_FS = 0;
534     }
535     return result;
536 }
537 
538 uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg)
539 {
540     float32 f_arg = make_float32(arg);
541     uint32_t result;
542     int32_t flags;
543 
544     result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status);
545 
546     flags = f_get_excp_flags(env);
547     if (flags & float_flag_invalid) {
548         flags &= ~float_flag_inexact;
549         if (float32_is_any_nan(f_arg)) {
550             result = 0;
551         }
552     /*
553      * we need to check arg < 0.0 before rounding as TriCore needs to raise
554      * float_flag_invalid as well. For instance, when we have a negative
555      * exponent and sign, softfloat would only raise float_flat_inexact.
556      */
557     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
558         flags = float_flag_invalid;
559         result = 0;
560     }
561 
562     if (flags) {
563         f_update_psw_flags(env, flags);
564     } else {
565         env->FPU_FS = 0;
566     }
567     return result;
568 }
569 
570 void helper_updfl(CPUTriCoreState *env, uint32_t arg)
571 {
572     env->FPU_FS =  extract32(arg, 7, 1) & extract32(arg, 15, 1);
573     env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31;
574     env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31;
575     env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31;
576     env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31;
577     /* clear FX and RM */
578     env->PSW &= ~(extract32(arg, 10, 1) << 26);
579     env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26;
580 
581     fpu_set_state(env);
582 }
583