target/xtensa: add DFPU registers and opcodes
DFPU may be configured with 32-bit or with 64-bit registers. Xtensa ISA does not specify how single-precision values are stored in 64-bit registers. Existing implementations store them in the low half of the registers. Add value extraction and write back to single-precision opcodes. Add new double precision opcodes. Add 64-bit register file. Add 64-bit values dumping to the xtensa_cpu_dump_state. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
This commit is contained in:
parent
de6b55cbda
commit
cfa9f05181
@ -31,6 +31,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "cpu.h"
|
||||
#include "fpu/softfloat.h"
|
||||
#include "qemu/module.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
@ -73,6 +74,8 @@ static void xtensa_cpu_reset(DeviceState *dev)
|
||||
XtensaCPU *cpu = XTENSA_CPU(s);
|
||||
XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(cpu);
|
||||
CPUXtensaState *env = &cpu->env;
|
||||
bool dfpu = xtensa_option_enabled(env->config,
|
||||
XTENSA_OPTION_DFP_COPROCESSOR);
|
||||
|
||||
xcc->parent_reset(dev);
|
||||
|
||||
@ -104,6 +107,8 @@ static void xtensa_cpu_reset(DeviceState *dev)
|
||||
reset_mmu(env);
|
||||
s->halted = env->runstall;
|
||||
#endif
|
||||
set_no_signaling_nans(!dfpu, &env->fp_status);
|
||||
set_use_first_nan(!dfpu, &env->fp_status);
|
||||
}
|
||||
|
||||
static ObjectClass *xtensa_cpu_class_by_name(const char *cpu_model)
|
||||
|
@ -422,6 +422,7 @@ typedef struct XtensaOpcodeTranslators {
|
||||
|
||||
extern const XtensaOpcodeTranslators xtensa_core_opcodes;
|
||||
extern const XtensaOpcodeTranslators xtensa_fpu2000_opcodes;
|
||||
extern const XtensaOpcodeTranslators xtensa_fpu_opcodes;
|
||||
|
||||
struct XtensaConfig {
|
||||
const char *name;
|
||||
@ -484,6 +485,8 @@ struct XtensaConfig {
|
||||
unsigned n_mpu_fg_segments;
|
||||
unsigned n_mpu_bg_segments;
|
||||
const xtensa_mpu_entry *mpu_bg;
|
||||
|
||||
bool use_first_nan;
|
||||
};
|
||||
|
||||
typedef struct XtensaConfigList {
|
||||
|
@ -33,6 +33,30 @@
|
||||
#include "exec/exec-all.h"
|
||||
#include "fpu/softfloat.h"
|
||||
|
||||
enum {
|
||||
XTENSA_FP_I = 0x1,
|
||||
XTENSA_FP_U = 0x2,
|
||||
XTENSA_FP_O = 0x4,
|
||||
XTENSA_FP_Z = 0x8,
|
||||
XTENSA_FP_V = 0x10,
|
||||
};
|
||||
|
||||
enum {
|
||||
XTENSA_FCR_FLAGS_SHIFT = 2,
|
||||
XTENSA_FSR_FLAGS_SHIFT = 7,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uint32_t xtensa_fp_flag;
|
||||
int softfloat_fp_flag;
|
||||
} xtensa_fp_flag_map[] = {
|
||||
{ XTENSA_FP_I, float_flag_inexact, },
|
||||
{ XTENSA_FP_U, float_flag_underflow, },
|
||||
{ XTENSA_FP_O, float_flag_overflow, },
|
||||
{ XTENSA_FP_Z, float_flag_divbyzero, },
|
||||
{ XTENSA_FP_V, float_flag_invalid, },
|
||||
};
|
||||
|
||||
void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v)
|
||||
{
|
||||
static const int rounding_mode[] = {
|
||||
@ -46,11 +70,72 @@ void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v)
|
||||
set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status);
|
||||
}
|
||||
|
||||
void HELPER(wur_fpu_fcr)(CPUXtensaState *env, uint32_t v)
|
||||
{
|
||||
static const int rounding_mode[] = {
|
||||
float_round_nearest_even,
|
||||
float_round_to_zero,
|
||||
float_round_up,
|
||||
float_round_down,
|
||||
};
|
||||
|
||||
if (v & 0xfffff000) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"MBZ field of FCR is written non-zero: %08x\n", v);
|
||||
}
|
||||
env->uregs[FCR] = v & 0x0000007f;
|
||||
set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status);
|
||||
}
|
||||
|
||||
void HELPER(wur_fpu_fsr)(CPUXtensaState *env, uint32_t v)
|
||||
{
|
||||
uint32_t flags = v >> XTENSA_FSR_FLAGS_SHIFT;
|
||||
int fef = 0;
|
||||
unsigned i;
|
||||
|
||||
if (v & 0xfffff000) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"MBZ field of FSR is written non-zero: %08x\n", v);
|
||||
}
|
||||
env->uregs[FSR] = v & 0x00000f80;
|
||||
for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) {
|
||||
if (flags & xtensa_fp_flag_map[i].xtensa_fp_flag) {
|
||||
fef |= xtensa_fp_flag_map[i].softfloat_fp_flag;
|
||||
}
|
||||
}
|
||||
set_float_exception_flags(fef, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(rur_fpu_fsr)(CPUXtensaState *env)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
int fef = get_float_exception_flags(&env->fp_status);
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) {
|
||||
if (fef & xtensa_fp_flag_map[i].softfloat_fp_flag) {
|
||||
flags |= xtensa_fp_flag_map[i].xtensa_fp_flag;
|
||||
}
|
||||
}
|
||||
env->uregs[FSR] = flags << XTENSA_FSR_FLAGS_SHIFT;
|
||||
return flags << XTENSA_FSR_FLAGS_SHIFT;
|
||||
}
|
||||
|
||||
float64 HELPER(abs_d)(float64 v)
|
||||
{
|
||||
return float64_abs(v);
|
||||
}
|
||||
|
||||
float32 HELPER(abs_s)(float32 v)
|
||||
{
|
||||
return float32_abs(v);
|
||||
}
|
||||
|
||||
float64 HELPER(neg_d)(float64 v)
|
||||
{
|
||||
return float64_chs(v);
|
||||
}
|
||||
|
||||
float32 HELPER(neg_s)(float32 v)
|
||||
{
|
||||
return float32_chs(v);
|
||||
@ -84,28 +169,144 @@ float32 HELPER(fpu2k_msub_s)(CPUXtensaState *env,
|
||||
&env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(ftoi_s)(float32 v, uint32_t rounding_mode, uint32_t scale)
|
||||
float64 HELPER(add_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
float_status fp_status = {0};
|
||||
|
||||
set_float_rounding_mode(rounding_mode, &fp_status);
|
||||
return float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status);
|
||||
set_use_first_nan(true, &env->fp_status);
|
||||
return float64_add(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(ftoui_s)(float32 v, uint32_t rounding_mode, uint32_t scale)
|
||||
float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
{
|
||||
float_status fp_status = {0};
|
||||
set_use_first_nan(env->config->use_first_nan, &env->fp_status);
|
||||
return float32_add(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(sub_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
set_use_first_nan(true, &env->fp_status);
|
||||
return float64_sub(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
{
|
||||
set_use_first_nan(env->config->use_first_nan, &env->fp_status);
|
||||
return float32_sub(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(mul_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
set_use_first_nan(true, &env->fp_status);
|
||||
return float64_mul(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
{
|
||||
set_use_first_nan(env->config->use_first_nan, &env->fp_status);
|
||||
return float32_mul(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(madd_d)(CPUXtensaState *env, float64 a, float64 b, float64 c)
|
||||
{
|
||||
set_use_first_nan(env->config->use_first_nan, &env->fp_status);
|
||||
return float64_muladd(b, c, a, 0, &env->fp_status);
|
||||
}
|
||||
|
||||
float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c)
|
||||
{
|
||||
set_use_first_nan(env->config->use_first_nan, &env->fp_status);
|
||||
return float32_muladd(b, c, a, 0, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(msub_d)(CPUXtensaState *env, float64 a, float64 b, float64 c)
|
||||
{
|
||||
set_use_first_nan(env->config->use_first_nan, &env->fp_status);
|
||||
return float64_muladd(b, c, a, float_muladd_negate_product,
|
||||
&env->fp_status);
|
||||
}
|
||||
|
||||
float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c)
|
||||
{
|
||||
set_use_first_nan(env->config->use_first_nan, &env->fp_status);
|
||||
return float32_muladd(b, c, a, float_muladd_negate_product,
|
||||
&env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(ftoi_d)(CPUXtensaState *env, float64 v,
|
||||
uint32_t rounding_mode, uint32_t scale)
|
||||
{
|
||||
float_status fp_status = env->fp_status;
|
||||
uint32_t res;
|
||||
|
||||
set_float_rounding_mode(rounding_mode, &fp_status);
|
||||
res = float64_to_int32(float64_scalbn(v, scale, &fp_status), &fp_status);
|
||||
set_float_exception_flags(get_float_exception_flags(&fp_status),
|
||||
&env->fp_status);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t HELPER(ftoi_s)(CPUXtensaState *env, float32 v,
|
||||
uint32_t rounding_mode, uint32_t scale)
|
||||
{
|
||||
float_status fp_status = env->fp_status;
|
||||
uint32_t res;
|
||||
|
||||
set_float_rounding_mode(rounding_mode, &fp_status);
|
||||
res = float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status);
|
||||
set_float_exception_flags(get_float_exception_flags(&fp_status),
|
||||
&env->fp_status);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t HELPER(ftoui_d)(CPUXtensaState *env, float64 v,
|
||||
uint32_t rounding_mode, uint32_t scale)
|
||||
{
|
||||
float_status fp_status = env->fp_status;
|
||||
float64 res;
|
||||
uint32_t rv;
|
||||
|
||||
set_float_rounding_mode(rounding_mode, &fp_status);
|
||||
|
||||
res = float64_scalbn(v, scale, &fp_status);
|
||||
|
||||
if (float64_is_neg(v) && !float64_is_any_nan(v)) {
|
||||
set_float_exception_flags(float_flag_invalid, &fp_status);
|
||||
rv = float64_to_int32(res, &fp_status);
|
||||
} else {
|
||||
rv = float64_to_uint32(res, &fp_status);
|
||||
}
|
||||
set_float_exception_flags(get_float_exception_flags(&fp_status),
|
||||
&env->fp_status);
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint32_t HELPER(ftoui_s)(CPUXtensaState *env, float32 v,
|
||||
uint32_t rounding_mode, uint32_t scale)
|
||||
{
|
||||
float_status fp_status = env->fp_status;
|
||||
float32 res;
|
||||
uint32_t rv;
|
||||
|
||||
set_float_rounding_mode(rounding_mode, &fp_status);
|
||||
|
||||
res = float32_scalbn(v, scale, &fp_status);
|
||||
|
||||
if (float32_is_neg(v) && !float32_is_any_nan(v)) {
|
||||
return float32_to_int32(res, &fp_status);
|
||||
rv = float32_to_int32(res, &fp_status);
|
||||
if (rv) {
|
||||
set_float_exception_flags(float_flag_invalid, &fp_status);
|
||||
}
|
||||
} else {
|
||||
return float32_to_uint32(res, &fp_status);
|
||||
rv = float32_to_uint32(res, &fp_status);
|
||||
}
|
||||
set_float_exception_flags(get_float_exception_flags(&fp_status),
|
||||
&env->fp_status);
|
||||
return rv;
|
||||
}
|
||||
|
||||
float64 HELPER(itof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale)
|
||||
{
|
||||
return float64_scalbn(int32_to_float64(v, &env->fp_status),
|
||||
(int32_t)scale, &env->fp_status);
|
||||
}
|
||||
|
||||
float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
|
||||
@ -114,22 +315,56 @@ float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
|
||||
(int32_t)scale, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(uitof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale)
|
||||
{
|
||||
return float64_scalbn(uint32_to_float64(v, &env->fp_status),
|
||||
(int32_t)scale, &env->fp_status);
|
||||
}
|
||||
|
||||
float32 HELPER(uitof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
|
||||
{
|
||||
return float32_scalbn(uint32_to_float32(v, &env->fp_status),
|
||||
(int32_t)scale, &env->fp_status);
|
||||
}
|
||||
|
||||
float64 HELPER(cvtd_s)(CPUXtensaState *env, float32 v)
|
||||
{
|
||||
return float32_to_float64(v, &env->fp_status);
|
||||
}
|
||||
|
||||
float32 HELPER(cvts_d)(CPUXtensaState *env, float64 v)
|
||||
{
|
||||
return float64_to_float32(v, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(un_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
return float64_unordered_quiet(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(un_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
{
|
||||
return float32_unordered_quiet(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(oeq_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
return float64_eq_quiet(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(oeq_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
{
|
||||
return float32_eq_quiet(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(ueq_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
|
||||
|
||||
return v == float_relation_equal ||
|
||||
v == float_relation_unordered;
|
||||
}
|
||||
|
||||
uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
{
|
||||
FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
|
||||
@ -138,9 +373,22 @@ uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
v == float_relation_unordered;
|
||||
}
|
||||
|
||||
uint32_t HELPER(olt_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
return float64_lt(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(olt_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
{
|
||||
return float32_lt_quiet(a, b, &env->fp_status);
|
||||
return float32_lt(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(ult_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
|
||||
|
||||
return v == float_relation_less ||
|
||||
v == float_relation_unordered;
|
||||
}
|
||||
|
||||
uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
@ -151,9 +399,21 @@ uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
v == float_relation_unordered;
|
||||
}
|
||||
|
||||
uint32_t HELPER(ole_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
return float64_le(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(ole_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
{
|
||||
return float32_le_quiet(a, b, &env->fp_status);
|
||||
return float32_le(a, b, &env->fp_status);
|
||||
}
|
||||
|
||||
uint32_t HELPER(ule_d)(CPUXtensaState *env, float64 a, float64 b)
|
||||
{
|
||||
FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
|
||||
|
||||
return v != float_relation_greater;
|
||||
}
|
||||
|
||||
uint32_t HELPER(ule_s)(CPUXtensaState *env, float32 a, float32 b)
|
||||
|
@ -54,10 +54,11 @@ DEF_HELPER_3(fpu2k_sub_s, f32, env, f32, f32)
|
||||
DEF_HELPER_3(fpu2k_mul_s, f32, env, f32, f32)
|
||||
DEF_HELPER_4(fpu2k_madd_s, f32, env, f32, f32, f32)
|
||||
DEF_HELPER_4(fpu2k_msub_s, f32, env, f32, f32, f32)
|
||||
DEF_HELPER_FLAGS_3(ftoi_s, TCG_CALL_NO_RWG_SE, i32, f32, i32, i32)
|
||||
DEF_HELPER_FLAGS_3(ftoui_s, TCG_CALL_NO_RWG_SE, i32, f32, i32, i32)
|
||||
DEF_HELPER_4(ftoi_s, i32, env, f32, i32, i32)
|
||||
DEF_HELPER_4(ftoui_s, i32, env, f32, i32, i32)
|
||||
DEF_HELPER_3(itof_s, f32, env, i32, i32)
|
||||
DEF_HELPER_3(uitof_s, f32, env, i32, i32)
|
||||
DEF_HELPER_2(cvtd_s, f64, env, f32)
|
||||
|
||||
DEF_HELPER_3(un_s, i32, env, f32, f32)
|
||||
DEF_HELPER_3(oeq_s, i32, env, f32, f32)
|
||||
@ -67,5 +68,34 @@ DEF_HELPER_3(ult_s, i32, env, f32, f32)
|
||||
DEF_HELPER_3(ole_s, i32, env, f32, f32)
|
||||
DEF_HELPER_3(ule_s, i32, env, f32, f32)
|
||||
|
||||
DEF_HELPER_2(wur_fpu_fcr, void, env, i32)
|
||||
DEF_HELPER_1(rur_fpu_fsr, i32, env)
|
||||
DEF_HELPER_2(wur_fpu_fsr, void, env, i32)
|
||||
DEF_HELPER_FLAGS_1(abs_d, TCG_CALL_NO_RWG_SE, f64, f64)
|
||||
DEF_HELPER_FLAGS_1(neg_d, TCG_CALL_NO_RWG_SE, f64, f64)
|
||||
DEF_HELPER_3(add_d, f64, env, f64, f64)
|
||||
DEF_HELPER_3(add_s, f32, env, f32, f32)
|
||||
DEF_HELPER_3(sub_d, f64, env, f64, f64)
|
||||
DEF_HELPER_3(sub_s, f32, env, f32, f32)
|
||||
DEF_HELPER_3(mul_d, f64, env, f64, f64)
|
||||
DEF_HELPER_3(mul_s, f32, env, f32, f32)
|
||||
DEF_HELPER_4(madd_d, f64, env, f64, f64, f64)
|
||||
DEF_HELPER_4(madd_s, f32, env, f32, f32, f32)
|
||||
DEF_HELPER_4(msub_d, f64, env, f64, f64, f64)
|
||||
DEF_HELPER_4(msub_s, f32, env, f32, f32, f32)
|
||||
DEF_HELPER_4(ftoi_d, i32, env, f64, i32, i32)
|
||||
DEF_HELPER_4(ftoui_d, i32, env, f64, i32, i32)
|
||||
DEF_HELPER_3(itof_d, f64, env, i32, i32)
|
||||
DEF_HELPER_3(uitof_d, f64, env, i32, i32)
|
||||
DEF_HELPER_2(cvts_d, f32, env, f64)
|
||||
|
||||
DEF_HELPER_3(un_d, i32, env, f64, f64)
|
||||
DEF_HELPER_3(oeq_d, i32, env, f64, f64)
|
||||
DEF_HELPER_3(ueq_d, i32, env, f64, f64)
|
||||
DEF_HELPER_3(olt_d, i32, env, f64, f64)
|
||||
DEF_HELPER_3(ult_d, i32, env, f64, f64)
|
||||
DEF_HELPER_3(ole_d, i32, env, f64, f64)
|
||||
DEF_HELPER_3(ule_d, i32, env, f64, f64)
|
||||
|
||||
DEF_HELPER_2(rer, i32, env, i32)
|
||||
DEF_HELPER_3(wer, void, env, i32, i32)
|
||||
|
@ -538,6 +538,7 @@
|
||||
.ndepc = (XCHAL_XEA_VERSION >= 2), \
|
||||
.inst_fetch_width = XCHAL_INST_FETCH_WIDTH, \
|
||||
.max_insn_size = XCHAL_MAX_INSTRUCTION_SIZE, \
|
||||
.use_first_nan = !XCHAL_HAVE_DFPU, \
|
||||
EXCEPTIONS_SECTION, \
|
||||
INTERRUPTS_SECTION, \
|
||||
TLB_SECTION, \
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user