diff options
author | Edgar E. Iglesias <edgar.iglesias@petalogix.com> | 2010-09-09 10:20:17 +0200 |
---|---|---|
committer | Edgar E. Iglesias <edgar.iglesias@gmail.com> | 2010-09-09 10:20:17 +0200 |
commit | 97694c57d73b150296c5f5333960eb4d56225cd7 (patch) | |
tree | b778a82fb00be1572d202b92ecd6c0a290de127f | |
parent | bdc0bf29c65dbd4f7d5119435e0d05da5de2b5c4 (diff) | |
download | qemu-97694c57d73b150296c5f5333960eb4d56225cd7.zip qemu-97694c57d73b150296c5f5333960eb4d56225cd7.tar.gz qemu-97694c57d73b150296c5f5333960eb4d56225cd7.tar.bz2 |
microblaze: Add basic FPU emulation
Missing:
* fcmp.un insn
* Denormalized exceptions
* Exception model is not accurate
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@petalogix.com>
-rw-r--r-- | target-microblaze/cpu.h | 2 | ||||
-rw-r--r-- | target-microblaze/helper.h | 16 | ||||
-rw-r--r-- | target-microblaze/op_helper.c | 230 | ||||
-rw-r--r-- | target-microblaze/translate.c | 119 |
4 files changed, 358 insertions, 9 deletions
diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h index dfcf25a..3aa28bf 100644 --- a/target-microblaze/cpu.h +++ b/target-microblaze/cpu.h @@ -24,6 +24,7 @@ #define CPUState struct CPUMBState #include "cpu-defs.h" +#include "softfloat.h" struct CPUMBState; #if !defined(CONFIG_USER_ONLY) #include "mmu.h" @@ -215,6 +216,7 @@ typedef struct CPUMBState { uint32_t imm; uint32_t regs[33]; uint32_t sregs[24]; + float_status fp_status; /* Internal flags. */ #define IMM_FLAG 4 diff --git a/target-microblaze/helper.h b/target-microblaze/helper.h index 28f251c..11ad1b6 100644 --- a/target-microblaze/helper.h +++ b/target-microblaze/helper.h @@ -10,6 +10,22 @@ DEF_HELPER_2(cmpu, i32, i32, i32) DEF_HELPER_2(divs, i32, i32, i32) DEF_HELPER_2(divu, i32, i32, i32) +DEF_HELPER_2(fadd, i32, i32, i32) +DEF_HELPER_2(frsub, i32, i32, i32) +DEF_HELPER_2(fmul, i32, i32, i32) +DEF_HELPER_2(fdiv, i32, i32, i32) +DEF_HELPER_1(flt, i32, i32) +DEF_HELPER_1(fint, i32, i32) +DEF_HELPER_1(fsqrt, i32, i32) + +DEF_HELPER_2(fcmp_un, i32, i32, i32) +DEF_HELPER_2(fcmp_lt, i32, i32, i32) +DEF_HELPER_2(fcmp_eq, i32, i32, i32) +DEF_HELPER_2(fcmp_le, i32, i32, i32) +DEF_HELPER_2(fcmp_gt, i32, i32, i32) +DEF_HELPER_2(fcmp_ne, i32, i32, i32) +DEF_HELPER_2(fcmp_ge, i32, i32, i32) + DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32) #if !defined(CONFIG_USER_ONLY) DEF_HELPER_1(mmu_read, i32, i32) diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c index be0c829..294e08c 100644 --- a/target-microblaze/op_helper.c +++ b/target-microblaze/op_helper.c @@ -202,6 +202,236 @@ uint32_t helper_divu(uint32_t a, uint32_t b) return a / b; } +/* raise FPU exception. */ +static void raise_fpu_exception(void) +{ + env->sregs[SR_ESR] = ESR_EC_FPU; + helper_raise_exception(EXCP_HW_EXCP); +} + +static void update_fpu_flags(int flags) +{ + int raise = 0; + + if (flags & float_flag_invalid) { + env->sregs[SR_FSR] |= FSR_IO; + raise = 1; + } + if (flags & float_flag_divbyzero) { + env->sregs[SR_FSR] |= FSR_DZ; + raise = 1; + } + if (flags & float_flag_overflow) { + env->sregs[SR_FSR] |= FSR_OF; + raise = 1; + } + if (flags & float_flag_underflow) { + env->sregs[SR_FSR] |= FSR_UF; + raise = 1; + } + if (raise + && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK) + && (env->sregs[SR_MSR] & MSR_EE)) { + raise_fpu_exception(); + } +} + +uint32_t helper_fadd(uint32_t a, uint32_t b) +{ + CPU_FloatU fd, fa, fb; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + fd.f = float32_add(fa.f, fb.f, &env->fp_status); + + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + return fd.l; +} + +uint32_t helper_frsub(uint32_t a, uint32_t b) +{ + CPU_FloatU fd, fa, fb; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + fd.f = float32_sub(fb.f, fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + return fd.l; +} + +uint32_t helper_fmul(uint32_t a, uint32_t b) +{ + CPU_FloatU fd, fa, fb; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + fd.f = float32_mul(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + + return fd.l; +} + +uint32_t helper_fdiv(uint32_t a, uint32_t b) +{ + CPU_FloatU fd, fa, fb; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + fd.f = float32_div(fb.f, fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + + return fd.l; +} + +uint32_t helper_fcmp_un(uint32_t a, uint32_t b) +{ + cpu_abort(env, "Unsupported fcmp.un\n"); + return 0; +} + +uint32_t helper_fcmp_lt(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int r; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + r = float32_lt(fb.f, fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + return r; +} + +uint32_t helper_fcmp_eq(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags; + int r; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + r = float32_eq(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + return r; +} + +uint32_t helper_fcmp_le(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags; + int r; + + fa.l = a; + fb.l = b; + set_float_exception_flags(0, &env->fp_status); + r = float32_le(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + + return r; +} + +uint32_t helper_fcmp_gt(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags, r; + + fa.l = a; + fb.l = b; + set_float_exception_flags(0, &env->fp_status); + r = float32_lt(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + return r; +} + +uint32_t helper_fcmp_ne(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags, r; + + fa.l = a; + fb.l = b; + set_float_exception_flags(0, &env->fp_status); + r = !float32_eq(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + return r; +} + +uint32_t helper_fcmp_ge(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags, r; + + fa.l = a; + fb.l = b; + set_float_exception_flags(0, &env->fp_status); + r = !float32_lt(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + return r; +} + +uint32_t helper_flt(uint32_t a) +{ + CPU_FloatU fd, fa; + + fa.l = a; + fd.f = int32_to_float32(fa.l, &env->fp_status); + return fd.l; +} + +uint32_t helper_fint(uint32_t a) +{ + CPU_FloatU fa; + uint32_t r; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + r = float32_to_int32(fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + + return r; +} + +uint32_t helper_fsqrt(uint32_t a) +{ + CPU_FloatU fd, fa; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fd.l = float32_sqrt(fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + + return fd.l; +} + uint32_t helper_pcmpbf(uint32_t a, uint32_t b) { unsigned int i; diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index f618290..6c305d4 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -457,7 +457,7 @@ static void dec_msr(DisasContext *dc) tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]); break; case 0x7: - /* Ignored at the moment. */ + tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); break; default: cpu_abort(dc->env, "unknown mts reg %x\n", sr); @@ -480,7 +480,7 @@ static void dec_msr(DisasContext *dc) tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]); break; case 0x7: - tcg_gen_movi_tl(cpu_R[dc->rd], 0); + tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]); break; case 0xb: tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]); @@ -1134,18 +1134,115 @@ static void dec_rts(DisasContext *dc) tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } +static int dec_check_fpuv2(DisasContext *dc) +{ + int r; + + r = dc->env->pvr.regs[2] & PVR2_USE_FPU2_MASK; + + if (!r && (dc->tb_flags & MSR_EE_FLAG)) { + tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + } + return r; +} + static void dec_fpu(DisasContext *dc) { + unsigned int fpu_insn; + if ((dc->tb_flags & MSR_EE_FLAG) && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && !((dc->env->pvr.regs[2] & PVR2_USE_FPU_MASK))) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU); + tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } - qemu_log ("unimplemented FPU insn pc=%x opc=%x\n", dc->pc, dc->opcode); - dc->abort_at_next_insn = 1; + fpu_insn = (dc->ir >> 7) & 7; + + switch (fpu_insn) { + case 0: + gen_helper_fadd(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + + case 1: + gen_helper_frsub(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + + case 2: + gen_helper_fmul(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + + case 3: + gen_helper_fdiv(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + + case 4: + switch ((dc->ir >> 4) & 7) { + case 0: + gen_helper_fcmp_un(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 1: + gen_helper_fcmp_lt(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 2: + gen_helper_fcmp_eq(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 3: + gen_helper_fcmp_le(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 4: + gen_helper_fcmp_gt(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 5: + gen_helper_fcmp_ne(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 6: + gen_helper_fcmp_ge(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + default: + qemu_log ("unimplemented fcmp fpu_insn=%x pc=%x opc=%x\n", + fpu_insn, dc->pc, dc->opcode); + dc->abort_at_next_insn = 1; + break; + } + break; + + case 5: + if (!dec_check_fpuv2(dc)) { + return; + } + gen_helper_flt(cpu_R[dc->rd], cpu_R[dc->ra]); + break; + + case 6: + if (!dec_check_fpuv2(dc)) { + return; + } + gen_helper_fint(cpu_R[dc->rd], cpu_R[dc->ra]); + break; + + case 7: + if (!dec_check_fpuv2(dc)) { + return; + } + gen_helper_fsqrt(cpu_R[dc->rd], cpu_R[dc->ra]); + break; + + default: + qemu_log ("unimplemented FPU insn fpu_insn=%x pc=%x opc=%x\n", + fpu_insn, dc->pc, dc->opcode); + dc->abort_at_next_insn = 1; + break; + } } static void dec_null(DisasContext *dc) @@ -1448,9 +1545,9 @@ void cpu_dump_state (CPUState *env, FILE *f, cpu_fprintf(f, "IN: PC=%x %s\n", env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); - cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n", + cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], - env->debug, env->imm, env->iflags); + env->debug, env->imm, env->iflags, env->sregs[SR_FSR]); cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", @@ -1476,7 +1573,7 @@ CPUState *cpu_mb_init (const char *cpu_model) cpu_exec_init(env); cpu_reset(env); - + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); if (tcg_initialized) return env; @@ -1545,15 +1642,19 @@ void cpu_reset (CPUState *env) | PVR2_USE_DIV_MASK \ | PVR2_USE_HW_MUL_MASK \ | PVR2_USE_MUL64_MASK \ + | PVR2_USE_FPU_MASK \ + | PVR2_USE_FPU2_MASK \ + | PVR2_FPU_EXC_MASK \ | 0; env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */ env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17); - env->sregs[SR_MSR] = 0; #if defined(CONFIG_USER_ONLY) /* start in user mode with interrupts enabled. */ + env->sregs[SR_MSR] = MSR_EE | MSR_IE | MSR_VM | MSR_UM; env->pvr.regs[10] = 0x0c000000; /* Spartan 3a dsp. */ #else + env->sregs[SR_MSR] = 0; mmu_init(&env->mmu); env->mmu.c_mmu = 3; env->mmu.c_mmu_tlb_access = 3; |