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_itof(CPUTriCoreState *env, uint32_t arg) |
377 | { |
378 | float32 f_result; |
379 | uint32_t flags; |
380 | f_result = int32_to_float32(arg, &env->fp_status); |
381 | |
382 | flags = f_get_excp_flags(env); |
383 | if (flags) { |
384 | f_update_psw_flags(env, flags); |
385 | } else { |
386 | env->FPU_FS = 0; |
387 | } |
388 | return (uint32_t)f_result; |
389 | } |
390 | |
391 | uint32_t helper_utof(CPUTriCoreState *env, uint32_t arg) |
392 | { |
393 | float32 f_result; |
394 | uint32_t flags; |
395 | |
396 | f_result = uint32_to_float32(arg, &env->fp_status); |
397 | |
398 | flags = f_get_excp_flags(env); |
399 | if (flags) { |
400 | f_update_psw_flags(env, flags); |
401 | } else { |
402 | env->FPU_FS = 0; |
403 | } |
404 | return (uint32_t)f_result; |
405 | } |
406 | |
407 | uint32_t helper_ftoiz(CPUTriCoreState *env, uint32_t arg) |
408 | { |
409 | float32 f_arg = make_float32(arg); |
410 | uint32_t result; |
411 | int32_t flags; |
412 | |
413 | result = float32_to_int32_round_to_zero(f_arg, &env->fp_status); |
414 | |
415 | flags = f_get_excp_flags(env); |
416 | if (flags & float_flag_invalid) { |
417 | flags &= ~float_flag_inexact; |
418 | if (float32_is_any_nan(f_arg)) { |
419 | result = 0; |
420 | } |
421 | } |
422 | |
423 | if (flags) { |
424 | f_update_psw_flags(env, flags); |
425 | } else { |
426 | env->FPU_FS = 0; |
427 | } |
428 | |
429 | return result; |
430 | } |
431 | |
432 | uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg) |
433 | { |
434 | float32 f_arg = make_float32(arg); |
435 | uint32_t result; |
436 | int32_t flags; |
437 | |
438 | result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status); |
439 | |
440 | flags = f_get_excp_flags(env); |
441 | if (flags & float_flag_invalid) { |
442 | flags &= ~float_flag_inexact; |
443 | if (float32_is_any_nan(f_arg)) { |
444 | result = 0; |
445 | } |
446 | } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) { |
447 | flags = float_flag_invalid; |
448 | result = 0; |
449 | } |
450 | |
451 | if (flags) { |
452 | f_update_psw_flags(env, flags); |
453 | } else { |
454 | env->FPU_FS = 0; |
455 | } |
456 | return result; |
457 | } |
458 | |
459 | void helper_updfl(CPUTriCoreState *env, uint32_t arg) |
460 | { |
461 | env->FPU_FS = extract32(arg, 7, 1) & extract32(arg, 15, 1); |
462 | env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31; |
463 | env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31; |
464 | env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31; |
465 | env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31; |
466 | /* clear FX and RM */ |
467 | env->PSW &= ~(extract32(arg, 10, 1) << 26); |
468 | env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26; |
469 | |
470 | fpu_set_state(env); |
471 | } |
472 | |