From afce8392d8de8e02dfcd2c7049dd59161017724c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 9 Dec 2020 22:12:43 +0300 Subject: [PATCH] target: e2k: add basic fpu instructions --- target/e2k/cpu.c | 1 + target/e2k/cpu.h | 56 +++++++++++++++++- target/e2k/helper.h | 19 ++++++ target/e2k/helper_fpu.c | 115 +++++++++++++++++++++++++++++++++++++ target/e2k/helper_int.c | 8 ++- target/e2k/meson.build | 1 + target/e2k/translate/alc.c | 110 +++++++++++++++++++++++++++++++++++ 7 files changed, 307 insertions(+), 3 deletions(-) create mode 100644 target/e2k/helper_fpu.c diff --git a/target/e2k/cpu.c b/target/e2k/cpu.c index 1ea3665676..1fa764f3d3 100644 --- a/target/e2k/cpu.c +++ b/target/e2k/cpu.c @@ -53,6 +53,7 @@ static void e2k_cpu_reset(DeviceState *dev) env->bn.size = 8; env->bn.cur = 0; env->aau.incrs[0] = 1; /* always one */ + env->fpcr._one = 1; // FIXME: testing env->idr = 0x3a207; // mimic 8c diff --git a/target/e2k/cpu.h b/target/e2k/cpu.h index 0ed66f97ff..a8cc9a53f6 100644 --- a/target/e2k/cpu.h +++ b/target/e2k/cpu.h @@ -4,6 +4,7 @@ #include "qemu/bswap.h" #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "fpu/softfloat.h" void e2k_tcg_initialize(void); @@ -447,6 +448,54 @@ typedef union { uint64_t raw; } E2KCtpr; +/* E2K FPU regs are compatible with x87 regs */ +#define FPSR_IE (1U << 0) /* invalid operation */ +#define FPSR_DE (1U << 1) /* denormalized operand */ +#define FPSR_ZE (1U << 2) /* zero divide */ +#define FPSR_OE (1U << 3) /* overflow */ +#define FPSR_UE (1U << 4) /* underflow */ +#define FPSR_PE (1U << 5) /* precision */ + +typedef union { + struct { + uint32_t ef : 6; /* exception flags */ + uint32_t sf : 1; /* stack fault, unused */ + uint32_t es : 1; /* error summary status */ + uint32_t _c0 : 1; /* condition code 0, unused */ + uint32_t c1 : 1; /* condition code 1 */ + uint32_t _c2 : 1; /* condition code 2, unused */ + uint32_t top: 3; /* stack top, unused */ + uint32_t _c3 : 1; /* condition code 3, unused */ + uint32_t b : 1; /* fpu busy */ + }; + uint32_t raw; +} E2KFpsrState; + +#define FPCR_EM (FPSR_IE|FPSR_DE|FPSR_ZE|FPSR_OE|FPSR_UE|FPSR_PE) + +#define FPCR_PC_SP 0 /* single precision (32 bits) */ +#define FPCR_PC_RESERVED 1 /* reserved */ +#define FPCR_PC_DP 2 /* double precision (64 bits) */ +#define FPCR_PC_XP 3 /* extended precision (80 bits) */ + +#define FPCR_RC_NEAR 0 /* round to nearest */ +#define FPCR_RC_DOWN 1 /* round down */ +#define FPCR_RC_UP 2 /* round up */ +#define FPCR_RC_CHOP 3 /* round toward zero (truncate) */ + +typedef union { + struct { + uint32_t em : 6; /* masks flags */ + uint32_t _one : 1; /* reserved, always 1 (?) */ + uint32_t _zero0 : 1; /* reserved, always 0 */ + uint32_t pc : 2; /* precision control */ + uint32_t rc : 2; /* rounding control */ + uint32_t ic : 1; /* infinity control */ + uint32_t _zero1 : 3; /* reserved */ + }; + uint32_t raw; +} E2KFpcrState; + typedef struct { union { uint64_t lo; @@ -510,9 +559,11 @@ typedef struct { uint64_t idr; uint32_t pfpfr; // Packed Floating Point Flag Register (PFPFR) - uint32_t fpcr; // Floating point control register (FPCR) - uint32_t fpsr; // Floating point state register (FPSR) + E2KFpsrState fpsr; // Floating point state register (FPSR) + E2KFpcrState fpcr; // Floating point control register (FPCR) + float_status fp_status; + E2KAauState aau; int interrupt_index; @@ -563,6 +614,7 @@ void e2k_break_save_state(CPUE2KState *env); bool e2k_cpu_tlb_fill(CPUState *cpu, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +void e2k_update_fp_status(CPUE2KState *env); #define cpu_signal_handler e2k_cpu_signal_handler #define cpu_list e2k_cpu_list diff --git a/target/e2k/helper.h b/target/e2k/helper.h index 2340c100a9..a9c843ba7a 100644 --- a/target/e2k/helper.h +++ b/target/e2k/helper.h @@ -27,3 +27,22 @@ DEF_HELPER_2(pmovmskb, i64, i64, i64) DEF_HELPER_1(aau_load_program, void, env) DEF_HELPER_3(mova_ptr, tl, env, int, int) DEF_HELPER_3(aau_am, void, env, int, int) + +#define DEF_HELPER_3_32_64(name) \ + DEF_HELPER_3(name##s, i32, env, i32, i32) \ + DEF_HELPER_3(name##d, i64, env, i64, i64) + +DEF_HELPER_3_32_64(fadd) +DEF_HELPER_3_32_64(fsub) +DEF_HELPER_3_32_64(fmin) +DEF_HELPER_3_32_64(fmax) +DEF_HELPER_3_32_64(fmul) +DEF_HELPER_3_32_64(fdiv) +DEF_HELPER_3_32_64(fcmpeq) +DEF_HELPER_3_32_64(fcmpneq) +DEF_HELPER_3_32_64(fcmple) +DEF_HELPER_3_32_64(fcmpnle) +DEF_HELPER_3_32_64(fcmplt) +DEF_HELPER_3_32_64(fcmpnlt) +DEF_HELPER_3_32_64(fcmpuod) +DEF_HELPER_3_32_64(fcmpod) diff --git a/target/e2k/helper_fpu.c b/target/e2k/helper_fpu.c new file mode 100644 index 0000000000..2ee925af11 --- /dev/null +++ b/target/e2k/helper_fpu.c @@ -0,0 +1,115 @@ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" + +static inline void fpu_set_exception(CPUE2KState *env, int mask) +{ + env->fpsr.ef |= mask; + if (env->fpsr.ef & (~env->fpcr.em & FPCR_EM)) { + env->fpsr.es = 1; + env->fpsr.b = 1; + } +} + +static inline uint8_t save_exception_flags(CPUE2KState *env) +{ + uint8_t old_flags = get_float_exception_flags(&env->fp_status); + set_float_exception_flags(0, &env->fp_status); + return old_flags; +} + +static inline void merge_exception_flags(CPUE2KState *env, uint8_t old_flags) +{ + uint8_t new_flags = get_float_exception_flags(&env->fp_status); + float_raise(old_flags, &env->fp_status); + fpu_set_exception(env, + ((new_flags & float_flag_invalid ? FPSR_IE : 0) | + (new_flags & float_flag_divbyzero ? FPSR_ZE : 0) | + (new_flags & float_flag_overflow ? FPSR_OE : 0) | + (new_flags & float_flag_underflow ? FPSR_UE : 0) | + (new_flags & float_flag_inexact ? FPSR_PE : 0) | + (new_flags & float_flag_input_denormal ? FPSR_DE : 0))); +} + +void e2k_update_fp_status(CPUE2KState *env) +{ + int x; + + switch(env->fpcr.rc) { + case FPCR_RC_UP: + x = float_round_up; + break; + case FPCR_RC_DOWN: + x = float_round_down; + break; + case FPCR_RC_CHOP: + x = float_round_to_zero; + break; + case FPCR_RC_NEAR: + x = float_round_nearest_even; + break; + default: + abort(); + break; + } + + set_float_rounding_mode(x, &env->fp_status); + + switch(env->fpcr.pc) { + case FPCR_PC_XP: x = 80; break; + case FPCR_PC_DP: x = 64; break; + case FPCR_PC_SP: x = 32; break; + case FPCR_PC_RESERVED: + default: + abort(); + break; + } + + set_floatx80_rounding_precision(x, &env->fp_status); + +} + +#define GENERATE_SIMPLE_FLOAT2_OP(name, function, size) \ + uint##size##_t HELPER(name)(CPUE2KState *env, uint##size##_t x, uint##size##_t y) \ + { \ + uint8_t old_flags = save_exception_flags(env); \ + float##size z = float##size##_##function (make_float##size (x), make_float##size (y), &env->fp_status); \ + merge_exception_flags(env, old_flags); \ + return float##size##_val(z); \ + } + +#define GENERATE_CMP_FLOAT2_OP(name, function, expr, size) \ + uint##size##_t HELPER(name)(CPUE2KState *env, uint##size##_t x, uint##size##_t y) \ + { \ + uint8_t old_flags = save_exception_flags(env); \ + uint##size##_t z = expr float##size##_##function (make_float##size (x), make_float##size (y), &env->fp_status); \ + merge_exception_flags(env, old_flags); \ + return z; \ + } + +#define GENERATE_SIMPLE_FLOAT2_OPS_32_64(name, function) \ + GENERATE_SIMPLE_FLOAT2_OP(name##s, function, 32) \ + GENERATE_SIMPLE_FLOAT2_OP(name##d, function, 64) + +#define GENERATE_CMP_FLOAT2_OPS_32_64(name, function, expr) \ + GENERATE_CMP_FLOAT2_OP(name##s, function, 32) \ + GENERATE_CMP_FLOAT2_OP(name##d, function, 64) + + +GENERATE_SIMPLE_FLOAT2_OPS_32_64(fadd, add) +GENERATE_SIMPLE_FLOAT2_OPS_32_64(fsub, sub) +GENERATE_SIMPLE_FLOAT2_OPS_32_64(fmin, min) +GENERATE_SIMPLE_FLOAT2_OPS_32_64(fmax, max) +GENERATE_SIMPLE_FLOAT2_OPS_32_64(fmul, mul) +GENERATE_SIMPLE_FLOAT2_OPS_32_64(fdiv, div) +GENERATE_CMP_FLOAT2_OPS_32_64(fcmpeq, eq, ) +GENERATE_CMP_FLOAT2_OPS_32_64(fcmpneq, eq, !) +GENERATE_CMP_FLOAT2_OPS_32_64(fcmple, le, ) +GENERATE_CMP_FLOAT2_OPS_32_64(fcmpnle, le, !) +GENERATE_CMP_FLOAT2_OPS_32_64(fcmplt, lt, ) +GENERATE_CMP_FLOAT2_OPS_32_64(fcmpnlt, lt, !) +GENERATE_CMP_FLOAT2_OPS_32_64(fcmpuod, unordered, ) +GENERATE_CMP_FLOAT2_OPS_32_64(fcmpod, unordered, !) diff --git a/target/e2k/helper_int.c b/target/e2k/helper_int.c index 0bc04acd0e..0fc59de6e3 100644 --- a/target/e2k/helper_int.c +++ b/target/e2k/helper_int.c @@ -40,6 +40,8 @@ static uint64_t* state_reg_ptr(CPUE2KState *env, int idx) case 0x53: return &env->pregs; /* %cr1.lo */ case 0x80: return &env->upsr; /* %upsr */ case 0x83: return &env->lsr; /* %lsr */ + case 0x85: return &env->fpcr.raw; /* %fpcr */ + case 0x86: return &env->fpsr.raw; /* %fpsr */ default: return NULL; } } @@ -86,7 +88,7 @@ uint32_t helper_state_reg_read_i32(CPUE2KState *env, int idx) void helper_state_reg_write_i64(CPUE2KState *env, int idx, uint64_t val) { uint64_t *p = state_reg_ptr(env, idx); - + if (p != NULL) { *p = val; } else { @@ -101,6 +103,10 @@ void helper_state_reg_write_i32(CPUE2KState *env, int idx, uint32_t val) if (p != NULL) { *p = val; + + if (idx == 0x85) { /* %fpcr */ + e2k_update_fp_status(env); + } } else { qemu_log_mask(LOG_UNIMP, "unknown state register 0x%x\n", idx); abort(); diff --git a/target/e2k/meson.build b/target/e2k/meson.build index 4de84b27f4..f2f994a68f 100644 --- a/target/e2k/meson.build +++ b/target/e2k/meson.build @@ -4,6 +4,7 @@ e2k_ss.add(files( 'gdbstub.c', 'helper.c', 'helper_aau.c', + 'helper_fpu.c', 'helper_int.c', 'helper_sm.c', 'helper_vec.c', diff --git a/target/e2k/translate/alc.c b/target/e2k/translate/alc.c index 9af9729829..cd50c06ae0 100644 --- a/target/e2k/translate/alc.c +++ b/target/e2k/translate/alc.c @@ -1741,6 +1741,19 @@ static void gen_alopf1_i64(DisasContext *ctx, int chan, gen_al_result_i64(ctx, chan, dst, tag); } +static void gen_alopf1_i64_env(DisasContext *ctx, int chan, + void (*op)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) +{ + Src64 s1 = get_src1_i64(ctx, chan); + Src64 s2 = get_src2_i64(ctx, chan); + TCGv_i32 tag = e2k_get_temp_i32(ctx); + TCGv_i64 dst = e2k_get_temp_i64(ctx); + + gen_tag2_i64(tag, s1.tag, s2.tag); + (*op)(dst, cpu_env, s1.value, s2.value); + gen_al_result_i64(ctx, chan, dst, tag); +} + static void gen_alopf1_i32(DisasContext *ctx, int chan, void (*op)(TCGv_i32, TCGv_i32, TCGv_i32)) { @@ -1754,6 +1767,19 @@ static void gen_alopf1_i32(DisasContext *ctx, int chan, gen_al_result_i32(ctx, chan, dst, tag); } +static void gen_alopf1_i32_env(DisasContext *ctx, int chan, + void (*op)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) +{ + Src32 s1 = get_src1_i32(ctx, chan); + Src32 s2 = get_src2_i32(ctx, chan); + TCGv_i32 tag = e2k_get_temp_i32(ctx); + TCGv_i32 dst = e2k_get_temp_i32(ctx); + + gen_tag2_i32(tag, s1.tag, s2.tag); + (*op)(dst, cpu_env, s1.value, s2.value); + gen_al_result_i32(ctx, chan, dst, tag); +} + static void gen_alopf1_i32_i64(DisasContext *ctx, int chan, void (*op)(TCGv_i64, TCGv_i32, TCGv_i32)) { @@ -2034,6 +2060,76 @@ static void execute_ext_00(DisasContext *ctx, Instr *instr) } break; } + case 0x30: + if (ctx->version >= 4 || is_chan_0134(chan)) { + /* faddd */ + gen_alopf1_i32_env(ctx, chan, gen_helper_fadds); + return; + } + break; + case 0x31: + if (ctx->version >= 4 || is_chan_0134(chan)) { + /* faddd */ + gen_alopf1_i64_env(ctx, chan, gen_helper_faddd); + return; + } + break; + case 0x32: + if (ctx->version >= 4 || is_chan_0134(chan)) { + /* fsubs */ + gen_alopf1_i32_env(ctx, chan, gen_helper_fsubs); + return; + } + break; + case 0x33: + if (ctx->version >= 4 || is_chan_0134(chan)) { + /* fsubd */ + gen_alopf1_i64_env(ctx, chan, gen_helper_fsubd); + return; + } + break; + case 0x34: + if (is_chan_0134(chan)) { + /* fmins */ + gen_alopf1_i32_env(ctx, chan, gen_helper_fmins); + return; + } + break; + case 0x35: + if (is_chan_0134(chan)) { + /* fmind */ + gen_alopf1_i64_env(ctx, chan, gen_helper_fmind); + return; + } + break; + case 0x36: + if (is_chan_0134(chan)) { + /* fmaxs */ + gen_alopf1_i32_env(ctx, chan, gen_helper_fmaxs); + return; + } + break; + case 0x37: + if (is_chan_0134(chan)) { + /* fmaxd */ + gen_alopf1_i64_env(ctx, chan, gen_helper_fmaxd); + return; + } + break; + case 0x38: + if (ctx->version >= 4 || is_chan_0134(chan)) { + /* fmuls */ + gen_alopf1_i32_env(ctx, chan, gen_helper_fmuls); + return; + } + break; + case 0x39: + if (is_chan_0134(chan)) { + /* fmuld */ + gen_alopf1_i64_env(ctx, chan, gen_helper_fmuld); + return; + } + break; case 0x40: if (chan == 5) { // FIXME: temp hack @@ -2068,6 +2164,20 @@ static void execute_ext_00(DisasContext *ctx, Instr *instr) return; } break; + case 0x48: + if (chan == 5) { + /* fdivs */ + gen_alopf1_i32_env(ctx, chan, gen_helper_fdivs); + return; + } + break; + case 0x49: + if (chan == 5) { + /* fdivd */ + gen_alopf1_i64_env(ctx, chan, gen_helper_fdivd); + return; + } + break; case 0x61: if (is_chan_0134(chan)) { gen_movtd(ctx, chan);