qemu/target/tricore/fpu_helper.c
<<
>>
Prefs
   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 */
  40static 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
  51static 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
  87static 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)                                                           \
 121uint32_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}
 144FADD_SUB(add)
 145FADD_SUB(sub)
 146
 147uint32_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 */
 193static 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
 206uint32_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
 254uint32_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
 280uint32_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
 306uint32_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
 334uint32_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
 357uint32_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
 376uint32_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
 391uint32_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
 407uint32_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
 432uint32_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
 459void 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