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 */
f_get_excp_flags(CPUTriCoreState * env)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
f_maddsub_nan_result(float32 arg1,float32 arg2,float32 arg3,float32 result,uint32_t muladd_negate_c)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
f_update_psw_flags(CPUTriCoreState * env,uint8_t flags)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)
FADD_SUB(sub)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
helper_qseed(CPUTriCoreState * env,uint32_t r1)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
helper_fdiv(CPUTriCoreState * env,uint32_t r1,uint32_t r2)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
helper_fmadd(CPUTriCoreState * env,uint32_t r1,uint32_t r2,uint32_t r3)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
helper_fmsub(CPUTriCoreState * env,uint32_t r1,uint32_t r2,uint32_t r3)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
helper_fcmp(CPUTriCoreState * env,uint32_t r1,uint32_t r2)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
helper_ftoi(CPUTriCoreState * env,uint32_t arg)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
helper_hptof(CPUTriCoreState * env,uint32_t arg)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
helper_ftohp(CPUTriCoreState * env,uint32_t arg)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
helper_itof(CPUTriCoreState * env,uint32_t arg)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
helper_utof(CPUTriCoreState * env,uint32_t arg)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
helper_ftoiz(CPUTriCoreState * env,uint32_t arg)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
helper_ftou(CPUTriCoreState * env,uint32_t arg)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
helper_ftouz(CPUTriCoreState * env,uint32_t arg)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
helper_updfl(CPUTriCoreState * env,uint32_t arg)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