diff options
Diffstat (limited to 'sim/mips/cp1.c')
-rw-r--r-- | sim/mips/cp1.c | 432 |
1 files changed, 402 insertions, 30 deletions
diff --git a/sim/mips/cp1.c b/sim/mips/cp1.c index a6d1b56..196173c 100644 --- a/sim/mips/cp1.c +++ b/sim/mips/cp1.c @@ -100,6 +100,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #define FPQNaN_LONG (UNSIGNED64 (0x7FFFFFFFFFFFFFFF)) #define FPQNaN_PS (FP_PS_cat (FPQNaN_SINGLE, FPQNaN_SINGLE)) +static void update_fcsr (sim_cpu *, address_word, sim_fpu_status); + static const char *fpu_format_name (FP_formats fmt); #ifdef DEBUG static const char *fpu_rounding_mode_name (int rm); @@ -127,7 +129,7 @@ value_fpr (sim_cpu *cpu, } /* For values not yet accessed, set to the desired format. */ - if (fmt < fmt_uninterpreted) + if (fmt < fmt_uninterpreted && fmt != fmt_dc32) { if (FPR_STATE[fpr] == fmt_uninterpreted) { @@ -137,7 +139,10 @@ value_fpr (sim_cpu *cpu, fpu_format_name (fmt)); #endif /* DEBUG */ } - else if (fmt != FPR_STATE[fpr]) + else if (fmt != FPR_STATE[fpr] + && !(fmt == fmt_single + && FPR_STATE[fpr] == fmt_double + && (FGR[fpr] == 0 || FGR[fpr] == 0xFFFFFFFF))) { sim_io_eprintf (SD, "FPR %d (format %s) being accessed with format %s - setting to unknown (PC = 0x%s)\n", fpr, fpu_format_name (FPR_STATE[fpr]), @@ -166,6 +171,7 @@ value_fpr (sim_cpu *cpu, case fmt_uninterpreted_32: case fmt_single: case fmt_word: + case fmt_dc32: value = (FGR[fpr] & 0xFFFFFFFF); break; @@ -557,8 +563,8 @@ fp_test(uint64_t op1, if (sim_fpu_is_nan (&wop1) || sim_fpu_is_nan (&wop2)) { - if ((cond & (1 << 3)) || - sim_fpu_is_snan (&wop1) || sim_fpu_is_snan (&wop2)) + if ((cond & (1 << 3)) + || sim_fpu_is_snan (&wop1) || sim_fpu_is_snan (&wop2)) status = sim_fpu_status_invalid_snan; less = 0; equal = 0; @@ -581,6 +587,109 @@ fp_test(uint64_t op1, return status; } +static const int sim_fpu_class_mips_mapping[] = { + FP_R6CLASS_SNAN, /* SIM_FPU_IS_SNAN = 1, Noisy not-a-number */ + FP_R6CLASS_QNAN, /* SIM_FPU_IS_QNAN = 2, Quiet not-a-number */ + FP_R6CLASS_NEGINF, /* SIM_FPU_IS_NINF = 3, -infinity */ + FP_R6CLASS_POSINF, /* SIM_FPU_IS_PINF = 4, +infinity */ + FP_R6CLASS_NEGNORM, /* SIM_FPU_IS_NNUMBER = 5, -num - [-MAX .. -MIN] */ + FP_R6CLASS_POSNORM, /* SIM_FPU_IS_PNUMBER = 6, +num - [+MIN .. +MAX] */ + FP_R6CLASS_NEGSUB, /* SIM_FPU_IS_NDENORM = 7, -denorm - (MIN .. 0) */ + FP_R6CLASS_POSSUB, /* SIM_FPU_IS_PDENORM = 8, +denorm - (0 .. MIN) */ + FP_R6CLASS_NEGZERO, /* SIM_FPU_IS_NZERO = 9, -0 */ + FP_R6CLASS_POSZERO /* SIM_FPU_IS_PZERO = 10, +0 */ +}; + +uint64_t +fp_classify (sim_cpu *cpu, + address_word cia, + uint64_t op, + FP_formats fmt) +{ + sim_fpu wop; + + switch (fmt) + { + case fmt_single: + sim_fpu_32to (&wop, op); + break; + case fmt_double: + sim_fpu_64to (&wop, op); + break; + default: + sim_io_error (SD, "Bad switch\n"); + } + return sim_fpu_class_mips_mapping[sim_fpu_classify (&wop) - 1]; +} + +int +fp_rint (sim_cpu *cpu, + address_word cia, + uint64_t op, + uint64_t *ans, + FP_formats fmt) +{ + sim_fpu wop = {0}, wtemp = {0}, wmagic = {0}, wans = {0}; + int64_t intermediate; + int status = 0; + sim_fpu_round round = rounding_mode (GETRM()); + + switch (fmt) + { + case fmt_single: + sim_fpu_32to (&wop, op); + sim_fpu_32to (&wmagic, 0x4b000000); + break; + case fmt_double: + sim_fpu_64to (&wop, op); + sim_fpu_64to (&wmagic, 0x4330000000000000); + break; + default: + sim_io_error (SD, "Bad switch\n"); + } + + if (sim_fpu_is_nan (&wop) || sim_fpu_is_infinity (&wop)) + { + status = sim_fpu_status_invalid_cvi; + update_fcsr (cpu, cia, status); + return status; + } + + switch (fmt) + { + case fmt_single: + if (sim_fpu_is_ge (&wop, &wmagic)) + wans = wop; + else + { + sim_fpu_add (&wtemp, &wop, &wmagic); + sim_fpu_round_32 (&wtemp, round, sim_fpu_denorm_default); + sim_fpu_sub (&wans, &wtemp, &wmagic); + } + sim_fpu_to32 ((uint32_t *) ans, &wans); + break; + case fmt_double: + if (sim_fpu_is_ge (&wop, &wmagic)) + wans = wop; + else + { + sim_fpu_add (&wtemp, &wop, &wmagic); + sim_fpu_round_64 (&wtemp, round, sim_fpu_denorm_default); + sim_fpu_sub (&wans, &wtemp, &wmagic); + } + sim_fpu_to64 (ans, &wans); + break; + default: + sim_io_error (SD, "Bad switch\n"); + } + + if (*ans != op && status == 0) + status = sim_fpu_status_inexact; + + update_fcsr (cpu, cia, status); + return status; +} + void fp_cmp(sim_cpu *cpu, address_word cia, @@ -620,11 +729,91 @@ fp_cmp(sim_cpu *cpu, break; } default: - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } } +uint64_t +fp_r6_cmp (sim_cpu *cpu, + address_word cia, + uint64_t op1, + uint64_t op2, + FP_formats fmt, + int cond) +{ + sim_fpu wop1, wop2; + int result = 0; + int signalling = cond & 0x8; + + switch (fmt) + { + case fmt_single: + sim_fpu_32to (&wop1, op1); + sim_fpu_32to (&wop2, op2); + break; + case fmt_double: + sim_fpu_64to (&wop1, op1); + sim_fpu_64to (&wop2, op2); + break; + default: + sim_io_error (SD, "Bad switch\n"); + } + + switch (cond) + { + case FP_R6CMP_AF: + result = 0; + break; + case FP_R6CMP_UN: + result = sim_fpu_is_un (&wop1, &wop2); + break; + case FP_R6CMP_OR: + result = sim_fpu_is_or (&wop1, &wop2); + break; + case FP_R6CMP_EQ: + result = sim_fpu_is_eq (&wop1, &wop2); + break; + case FP_R6CMP_NE: + result = sim_fpu_is_ne (&wop1, &wop2); + break; + case FP_R6CMP_LT: + result = sim_fpu_is_lt (&wop1, &wop2); + break; + case FP_R6CMP_LE: + result = sim_fpu_is_le (&wop1, &wop2); + break; + case FP_R6CMP_UEQ: + result = sim_fpu_is_un (&wop1, &wop2) || sim_fpu_is_eq (&wop1, &wop2); + break; + case FP_R6CMP_UNE: + result = sim_fpu_is_un (&wop1, &wop2) || sim_fpu_is_ne (&wop1, &wop2); + break; + case FP_R6CMP_ULT: + result = sim_fpu_is_un (&wop1, &wop2) || sim_fpu_is_lt (&wop1, &wop2); + break; + case FP_R6CMP_ULE: + result = sim_fpu_is_un (&wop1, &wop2) || sim_fpu_is_le (&wop1, &wop2); + break; + default: + update_fcsr (cpu, cia, sim_fpu_status_invalid_cmp); + break; + } + + if (result) + { + switch (fmt) + { + case fmt_single: + return 0xFFFFFFFF; + case fmt_double: + return 0xFFFFFFFFFFFFFFFF; + default: + sim_io_error (SD, "Bad switch\n"); + } + } + else + return 0; +} /* Basic arithmetic operations. */ @@ -635,7 +824,7 @@ fp_unary(sim_cpu *cpu, uint64_t op, FP_formats fmt) { - sim_fpu wop; + sim_fpu wop = {0}; sim_fpu ans; sim_fpu_round round = rounding_mode (GETRM()); sim_fpu_denorm denorm = denorm_mode (cpu); @@ -680,8 +869,7 @@ fp_unary(sim_cpu *cpu, break; } default: - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } update_fcsr (cpu, cia, status); @@ -696,9 +884,9 @@ fp_binary(sim_cpu *cpu, uint64_t op2, FP_formats fmt) { - sim_fpu wop1; - sim_fpu wop2; - sim_fpu ans; + sim_fpu wop1 = {0}; + sim_fpu wop2 = {0}; + sim_fpu ans = {0}; sim_fpu_round round = rounding_mode (GETRM()); sim_fpu_denorm denorm = denorm_mode (cpu); sim_fpu_status status = 0; @@ -746,8 +934,7 @@ fp_binary(sim_cpu *cpu, break; } default: - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } update_fcsr (cpu, cia, status); @@ -786,7 +973,7 @@ inner_mac(int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *), ans.normal_exp += scale; status |= sim_fpu_round_32 (&ans, round, denorm); wop1 = ans; - op_status = 0; + op_status = 0; sim_fpu_32to (&wop2, op3); op_status |= (*sim_fpu_op) (&ans, &wop1, &wop2); op_status |= sim_fpu_round_32 (&ans, round, denorm); @@ -812,7 +999,7 @@ inner_mac(int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *), ans.normal_exp += scale; status |= sim_fpu_round_64 (&ans, round, denorm); wop1 = ans; - op_status = 0; + op_status = 0; sim_fpu_64to (&wop2, op3); op_status |= (*sim_fpu_op) (&ans, &wop1, &wop2); op_status |= sim_fpu_round_64 (&ans, round, denorm); @@ -881,8 +1068,89 @@ fp_mac(sim_cpu *cpu, break; } default: - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); + } + + update_fcsr (cpu, cia, status); + return result; +} + +/* Common FMAC code for .s, .d. Defers setting FCSR to caller. */ +static sim_fpu_status +inner_fmac (sim_cpu *cpu, + int (*sim_fpu_op) (sim_fpu *, const sim_fpu *, const sim_fpu *), + uint64_t op1, + uint64_t op2, + uint64_t op3, + sim_fpu_round round, + sim_fpu_denorm denorm, + FP_formats fmt, + uint64_t *result) +{ + sim_fpu wop1, wop2, ans; + sim_fpu_status status = 0; + sim_fpu_status op_status; + uint32_t t32 = 0; + uint64_t t64 = 0; + + switch (fmt) + { + case fmt_single: + sim_fpu_32to (&wop1, op1); + sim_fpu_32to (&wop2, op2); + status |= sim_fpu_mul (&ans, &wop1, &wop2); + wop1 = ans; + op_status = 0; + sim_fpu_32to (&wop2, op3); + op_status |= (*sim_fpu_op) (&ans, &wop2, &wop1); + op_status |= sim_fpu_round_32 (&ans, round, denorm); + status |= op_status; + sim_fpu_to32 (&t32, &ans); + t64 = t32; + break; + case fmt_double: + sim_fpu_64to (&wop1, op1); + sim_fpu_64to (&wop2, op2); + status |= sim_fpu_mul (&ans, &wop1, &wop2); + wop1 = ans; + op_status = 0; + sim_fpu_64to (&wop2, op3); + op_status |= (*sim_fpu_op) (&ans, &wop2, &wop1); + op_status |= sim_fpu_round_64 (&ans, round, denorm); + status |= op_status; + sim_fpu_to64 (&t64, &ans); + break; + default: + sim_io_error (SD, "Bad switch\n"); + } + + *result = t64; + return status; +} + +static uint64_t +fp_fmac (sim_cpu *cpu, + address_word cia, + int (*sim_fpu_op) (sim_fpu *, const sim_fpu *, const sim_fpu *), + uint64_t op1, + uint64_t op2, + uint64_t op3, + FP_formats fmt) +{ + sim_fpu_round round = rounding_mode (GETRM()); + sim_fpu_denorm denorm = denorm_mode (cpu); + sim_fpu_status status = 0; + uint64_t result = 0; + + switch (fmt) + { + case fmt_single: + case fmt_double: + status = inner_fmac (cpu, sim_fpu_op, op1, op2, op3, + round, denorm, fmt, &result); + break; + default: + sim_io_error (SD, "Bad switch\n"); } update_fcsr (cpu, cia, status); @@ -972,8 +1240,7 @@ fp_inv_sqrt(sim_cpu *cpu, break; } default: - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } update_fcsr (cpu, cia, status); @@ -1040,6 +1307,94 @@ fp_div(sim_cpu *cpu, } uint64_t +fp_min (sim_cpu *cpu, + address_word cia, + uint64_t op1, + uint64_t op2, + FP_formats fmt) +{ + return fp_binary (cpu, cia, &sim_fpu_min, op1, op2, fmt); +} + +uint64_t +fp_max (sim_cpu *cpu, + address_word cia, + uint64_t op1, + uint64_t op2, + FP_formats fmt) +{ + return fp_binary (cpu, cia, &sim_fpu_max, op1, op2, fmt); +} + +uint64_t +fp_mina (sim_cpu *cpu, + address_word cia, + uint64_t op1, + uint64_t op2, + FP_formats fmt) +{ + uint64_t ret; + sim_fpu wop1 = {0}, wop2 = {0}, waop1, waop2, wans; + sim_fpu_status status = 0; + + switch (fmt) + { + case fmt_single: + sim_fpu_32to (&wop1, op1); + sim_fpu_32to (&wop2, op2); + break; + case fmt_double: + sim_fpu_64to (&wop1, op1); + sim_fpu_64to (&wop2, op2); + break; + default: + sim_io_error (SD, "Bad switch\n"); + } + + status |= sim_fpu_abs (&waop1, &wop1); + status |= sim_fpu_abs (&waop2, &wop2); + status |= sim_fpu_min (&wans, &waop1, &waop2); + ret = (sim_fpu_is_eq (&wans, &waop1)) ? op1 : op2; + + update_fcsr (cpu, cia, status); + return ret; +} + +uint64_t +fp_maxa (sim_cpu *cpu, + address_word cia, + uint64_t op1, + uint64_t op2, + FP_formats fmt) +{ + uint64_t ret; + sim_fpu wop1 = {0}, wop2 = {0}, waop1, waop2, wans; + sim_fpu_status status = 0; + + switch (fmt) + { + case fmt_single: + sim_fpu_32to (&wop1, op1); + sim_fpu_32to (&wop2, op2); + break; + case fmt_double: + sim_fpu_64to (&wop1, op1); + sim_fpu_64to (&wop2, op2); + break; + default: + sim_io_error (SD, "Bad switch\n"); + } + + status |= sim_fpu_abs (&waop1, &wop1); + status |= sim_fpu_abs (&waop2, &wop2); + status |= sim_fpu_max (&wans, &waop1, &waop2); + ret = (sim_fpu_is_eq (&wans, &waop1)) ? op1 : op2; + + update_fcsr (cpu, cia, status); + return ret; +} + +uint64_t fp_recip(sim_cpu *cpu, address_word cia, uint64_t op, @@ -1089,6 +1444,28 @@ fp_msub(sim_cpu *cpu, } uint64_t +fp_fmadd (sim_cpu *cpu, + address_word cia, + uint64_t op1, + uint64_t op2, + uint64_t op3, + FP_formats fmt) +{ + return fp_fmac (cpu, cia, &sim_fpu_add, op1, op2, op3, fmt); +} + +uint64_t +fp_fmsub (sim_cpu *cpu, + address_word cia, + uint64_t op1, + uint64_t op2, + uint64_t op3, + FP_formats fmt) +{ + return fp_fmac (cpu, cia, &sim_fpu_sub, op1, op2, op3, fmt); +} + +uint64_t fp_nmadd(sim_cpu *cpu, address_word cia, uint64_t op1, @@ -1394,8 +1771,7 @@ convert (sim_cpu *cpu, status = sim_fpu_i64to (&wop, op, round); break; default: - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } /* Convert sim_fpu format into the output */ @@ -1430,8 +1806,7 @@ convert (sim_cpu *cpu, break; default: result64 = 0; - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } update_fcsr (cpu, cia, status); @@ -1481,8 +1856,7 @@ pack_ps(sim_cpu *cpu, break; } default: - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } return result; @@ -1517,8 +1891,7 @@ convert_ps (sim_cpu *cpu, sim_fpu_32to (&wop_l, FP_PS_lower(op)); break; default: - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } /* Convert sim_fpu format into the output */ @@ -1538,8 +1911,7 @@ convert_ps (sim_cpu *cpu, break; default: result = 0; - sim_io_eprintf (SD, "Bad switch\n"); - abort (); + sim_io_error (SD, "Bad switch\n"); } update_fcsr (cpu, cia, status_u | status_l); |