diff options
-rw-r--r-- | fpu/softfloat-specialize.h | 2 | ||||
-rw-r--r-- | target-tricore/Makefile.objs | 2 | ||||
-rw-r--r-- | target-tricore/cpu.h | 6 | ||||
-rw-r--r-- | target-tricore/fpu_helper.c | 217 | ||||
-rw-r--r-- | target-tricore/helper.c | 12 | ||||
-rw-r--r-- | target-tricore/helper.h | 7 | ||||
-rw-r--r-- | target-tricore/op_helper.c | 2 | ||||
-rw-r--r-- | target-tricore/translate.c | 34 | ||||
-rw-r--r-- | target-tricore/tricore-opcodes.h | 18 |
9 files changed, 293 insertions, 7 deletions
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 0875436b83..a4cbdad452 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -113,7 +113,7 @@ const float16 float16_default_nan = const_float16(0xFE00); #if defined(TARGET_SPARC) const float32 float32_default_nan = const_float32(0x7FFFFFFF); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) || defined(TARGET_S390X) + defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE) const float32 float32_default_nan = const_float32(0x7FC00000); #elif SNAN_BIT_IS_ONE const float32 float32_default_nan = const_float32(0x7FBFFFFF); diff --git a/target-tricore/Makefile.objs b/target-tricore/Makefile.objs index 21e820d8f9..7a05670718 100644 --- a/target-tricore/Makefile.objs +++ b/target-tricore/Makefile.objs @@ -1 +1 @@ -obj-y += translate.o helper.o cpu.o op_helper.o +obj-y += translate.o helper.o cpu.o op_helper.o fpu_helper.o diff --git a/target-tricore/cpu.h b/target-tricore/cpu.h index 5fee376674..90045a93d2 100644 --- a/target-tricore/cpu.h +++ b/target-tricore/cpu.h @@ -183,8 +183,7 @@ struct CPUTriCoreState { uint32_t M2CNT; uint32_t M3CNT; /* Floating Point Registers */ - /* XXX: */ - + float_status fp_status; /* QEMU */ int error_code; uint32_t hflags; /* CPU State */ @@ -217,6 +216,7 @@ struct CPUTriCoreState { #define MASK_PSW_GW 0x00000100 #define MASK_PSW_CDE 0x00000080 #define MASK_PSW_CDC 0x0000007f +#define MASK_PSW_FPU_RM 0x3000000 #define MASK_SYSCON_PRO_TEN 0x2 #define MASK_SYSCON_FCD_SF 0x1 @@ -339,6 +339,8 @@ enum { uint32_t psw_read(CPUTriCoreState *env); void psw_write(CPUTriCoreState *env, uint32_t val); +void fpu_set_state(CPUTriCoreState *env); + #include "cpu-qom.h" #define MMU_USER_IDX 2 diff --git a/target-tricore/fpu_helper.c b/target-tricore/fpu_helper.c new file mode 100644 index 0000000000..98fe9472b1 --- /dev/null +++ b/target-tricore/fpu_helper.c @@ -0,0 +1,217 @@ +/* + * TriCore emulation for qemu: fpu helper. + * + * Copyright (c) 2016 Bastian Koppelmann University of Paderborn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" + +#define ADD_NAN 0x7cf00001 +#define DIV_NAN 0x7fc00008 +#define MUL_NAN 0x7fc00002 +#define FPU_FS PSW_USB_C +#define FPU_FI PSW_USB_V +#define FPU_FV PSW_USB_SV +#define FPU_FZ PSW_USB_AV +#define FPU_FU PSW_USB_SAV + +/* we don't care about input_denormal */ +static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) +{ + return get_float_exception_flags(&env->fp_status) + & (float_flag_invalid + | float_flag_overflow + | float_flag_underflow + | float_flag_output_denormal + | float_flag_divbyzero + | float_flag_inexact); +} + +static inline bool f_is_denormal(float32 arg) +{ + return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg); +} + +static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) +{ + uint8_t some_excp = 0; + set_float_exception_flags(0, &env->fp_status); + + if (flags & float_flag_invalid) { + env->FPU_FI = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_overflow) { + env->FPU_FV = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_underflow || flags & float_flag_output_denormal) { + env->FPU_FU = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_divbyzero) { + env->FPU_FZ = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_inexact || flags & float_flag_output_denormal) { + env->PSW |= 1 << 26; + some_excp = 1; + } + + env->FPU_FS = some_excp; +} + +#define FADD_SUB(op) \ +uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2) \ +{ \ + float32 arg1 = make_float32(r1); \ + float32 arg2 = make_float32(r2); \ + uint32_t flags; \ + float32 f_result; \ + \ + f_result = float32_##op(arg2, arg1, &env->fp_status); \ + flags = f_get_excp_flags(env); \ + if (flags) { \ + /* If the output is a NaN, but the inputs aren't, \ + we return a unique value. */ \ + if ((flags & float_flag_invalid) \ + && !float32_is_any_nan(arg1) \ + && !float32_is_any_nan(arg2)) { \ + f_result = ADD_NAN; \ + } \ + f_update_psw_flags(env, flags); \ + } else { \ + env->FPU_FS = 0; \ + } \ + return (uint32_t)f_result; \ +} +FADD_SUB(add) +FADD_SUB(sub) + +uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + float32 f_result; + + f_result = float32_mul(arg1, arg2, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + /* If the output is a NaN, but the inputs aren't, + we return a unique value. */ + if ((flags & float_flag_invalid) + && !float32_is_any_nan(arg1) + && !float32_is_any_nan(arg2)) { + f_result = MUL_NAN; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)f_result; + +} + +uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + float32 f_result; + + f_result = float32_div(arg1, arg2 , &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + /* If the output is a NaN, but the inputs aren't, + we return a unique value. */ + if ((flags & float_flag_invalid) + && !float32_is_any_nan(arg1) + && !float32_is_any_nan(arg2)) { + f_result = DIV_NAN; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + + return (uint32_t)f_result; +} + +uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t result, flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + + set_flush_inputs_to_zero(0, &env->fp_status); + + result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1); + result |= f_is_denormal(arg1) << 4; + result |= f_is_denormal(arg2) << 5; + + flags = f_get_excp_flags(env); + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + + set_flush_inputs_to_zero(1, &env->fp_status); + return result; +} + +uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) +{ + float32 f_arg = make_float32(arg); + int32_t result, flags; + + result = float32_to_int32(f_arg, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + if (float32_is_any_nan(f_arg)) { + result = 0; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)result; +} + +uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) +{ + float32 f_result; + uint32_t flags; + f_result = int32_to_float32(arg, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)f_result; +} diff --git a/target-tricore/helper.c b/target-tricore/helper.c index 7d96daddb1..71b31cdb9b 100644 --- a/target-tricore/helper.c +++ b/target-tricore/helper.c @@ -110,10 +110,18 @@ void tricore_cpu_list(FILE *f, fprintf_function cpu_fprintf) g_slist_free(list); } +void fpu_set_state(CPUTriCoreState *env) +{ + set_float_rounding_mode(env->PSW & MASK_PSW_FPU_RM, &env->fp_status); + set_flush_inputs_to_zero(1, &env->fp_status); + set_flush_to_zero(1, &env->fp_status); + set_default_nan_mode(1, &env->fp_status); +} + uint32_t psw_read(CPUTriCoreState *env) { /* clear all USB bits */ - env->PSW &= 0xffffff; + env->PSW &= 0x6ffffff; /* now set them from the cache */ env->PSW |= ((env->PSW_USB_C != 0) << 31); env->PSW |= ((env->PSW_USB_V & (1 << 31)) >> 1); @@ -132,4 +140,6 @@ void psw_write(CPUTriCoreState *env, uint32_t val) env->PSW_USB_AV = (val & MASK_USB_AV) << 3; env->PSW_USB_SAV = (val & MASK_USB_SAV) << 4; env->PSW = val; + + fpu_set_state(env); } diff --git a/target-tricore/helper.h b/target-tricore/helper.h index 2c8ed78940..9333e161ab 100644 --- a/target-tricore/helper.h +++ b/target-tricore/helper.h @@ -105,6 +105,13 @@ DEF_HELPER_FLAGS_1(parity, TCG_CALL_NO_RWG_SE, i32, i32) /* float */ DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32, i32) DEF_HELPER_1(unpack, i64, i32) +DEF_HELPER_3(fadd, i32, env, i32, i32) +DEF_HELPER_3(fsub, i32, env, i32, i32) +DEF_HELPER_3(fmul, i32, env, i32, i32) +DEF_HELPER_3(fdiv, i32, env, i32, i32) +DEF_HELPER_3(fcmp, i32, env, i32, i32) +DEF_HELPER_2(ftoi, i32, env, i32) +DEF_HELPER_2(itof, i32, env, i32) /* dvinit */ DEF_HELPER_3(dvinit_b_13, i64, env, i32, i32) DEF_HELPER_3(dvinit_b_131, i64, env, i32, i32) diff --git a/target-tricore/op_helper.c b/target-tricore/op_helper.c index 55f6724da8..40656c357c 100644 --- a/target-tricore/op_helper.c +++ b/target-tricore/op_helper.c @@ -1045,6 +1045,8 @@ uint64_t helper_msub64_q_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2, } else { result = INT64_MIN; } + } else { + env->PSW_USB_V = 0; } } else { if (ovf < 0) { diff --git a/target-tricore/translate.c b/target-tricore/translate.c index d13e5c8c62..912bf226be 100644 --- a/target-tricore/translate.c +++ b/target-tricore/translate.c @@ -6672,6 +6672,21 @@ static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; + case OPC2_32_RR_MUL_F: + gen_helper_fmul(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_DIV_F: + gen_helper_fdiv(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_CMP_F: + gen_helper_fcmp(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_FTOI: + gen_helper_ftoi(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + break; + case OPC2_32_RR_ITOF: + gen_helper_itof(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -7013,48 +7028,60 @@ static void decode_rrr_divide(CPUTriCoreState *env, DisasContext *ctx) r3 = MASK_OP_RRR_S3(ctx->opcode); r4 = MASK_OP_RRR_D(ctx->opcode); - CHECK_REG_PAIR(r3); - switch (op2) { case OPC2_32_RRR_DVADJ: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvadj, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_DVSTEP: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvstep, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_DVSTEP_U: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvstep_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMAX: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmax, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMAX_U: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmax_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMIN: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmin, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMIN_U: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmin_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_PACK: + CHECK_REG_PAIR(r3); gen_helper_pack(cpu_gpr_d[r4], cpu_PSW_C, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; + case OPC2_32_RRR_ADD_F: + gen_helper_fadd(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); + break; + case OPC2_32_RRR_SUB_F: + gen_helper_fsub(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); + break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -8632,6 +8659,7 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) break; case OPCM_32_RRR_DIVIDE: decode_rrr_divide(env, ctx); + break; /* RRR2 Format */ case OPCM_32_RRR2_MADD: decode_rrr2_madd(env, ctx); @@ -8661,6 +8689,7 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) /* RRRR format */ case OPCM_32_RRRR_EXTRACT_INSERT: decode_rrrr_extract_insert(env, ctx); + break; /* RRRW format */ case OPCM_32_RRRW_EXTRACT_INSERT: decode_rrrw_extract_insert(env, ctx); @@ -8771,6 +8800,7 @@ void cpu_state_reset(CPUTriCoreState *env) { /* Reset Regs to Default Value */ env->PSW = 0xb80; + fpu_set_state(env); } static void tricore_tcg_init_csfr(void) diff --git a/target-tricore/tricore-opcodes.h b/target-tricore/tricore-opcodes.h index 1bfed0ce48..df666b081f 100644 --- a/target-tricore/tricore-opcodes.h +++ b/target-tricore/tricore-opcodes.h @@ -1126,6 +1126,20 @@ enum { OPC2_32_RR_CRC32 = 0x03, OPC2_32_RR_DIV = 0x20, OPC2_32_RR_DIV_U = 0x21, + OPC2_32_RR_MUL_F = 0x04, + OPC2_32_RR_DIV_F = 0x05, + OPC2_32_RR_FTOI = 0x10, + OPC2_32_RR_ITOF = 0x14, + OPC2_32_RR_CMP_F = 0x00, + OPC2_32_RR_FTOIZ = 0x13, + OPC2_32_RR_FTOQ31 = 0x11, + OPC2_32_RR_FTOQ31Z = 0x18, + OPC2_32_RR_FTOU = 0x12, + OPC2_32_RR_FTOUZ = 0x17, + OPC2_32_RR_Q31TOF = 0x15, + OPC2_32_RR_QSEED_F = 0x19, + OPC2_32_RR_UPDFL = 0x0c, + OPC2_32_RR_UTOF = 0x16, }; /* OPCM_32_RR_IDIRECT */ enum { @@ -1209,6 +1223,10 @@ enum { OPC2_32_RRR_IXMIN = 0x08, OPC2_32_RRR_IXMIN_U = 0x09, OPC2_32_RRR_PACK = 0x00, + OPC2_32_RRR_ADD_F = 0x02, + OPC2_32_RRR_SUB_F = 0x03, + OPC2_32_RRR_MADD_F = 0x06, + OPC2_32_RRR_MSUB_F = 0x07, }; /* * RRR1 Format |