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>
This commit is contained in:
parent
bdc0bf29c6
commit
97694c57d7
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user