diff options
47 files changed, 777 insertions, 729 deletions
@@ -87,8 +87,9 @@ Huacai Chen <chenhuacai@kernel.org> <chenhc@lemote.com> Huacai Chen <chenhuacai@kernel.org> <chenhuacai@loongson.cn> James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> Juan Quintela <quintela@trasno.org> <quintela@redhat.com> -Leif Lindholm <quic_llindhol@quicinc.com> <leif.lindholm@linaro.org> -Leif Lindholm <quic_llindhol@quicinc.com> <leif@nuviainc.com> +Leif Lindholm <leif.lindholm@oss.qualcomm.com> <quic_llindhol@quicinc.com> +Leif Lindholm <leif.lindholm@oss.qualcomm.com> <leif.lindholm@linaro.org> +Leif Lindholm <leif.lindholm@oss.qualcomm.com> <leif@nuviainc.com> Luc Michel <luc@lmichel.fr> <luc.michel@git.antfield.fr> Luc Michel <luc@lmichel.fr> <luc.michel@greensocs.com> Luc Michel <luc@lmichel.fr> <lmichel@kalray.eu> diff --git a/MAINTAINERS b/MAINTAINERS index dc583d5..025b5d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -917,7 +917,7 @@ F: include/hw/ssi/imx_spi.h SBSA-REF M: Radoslaw Biernacki <rad@semihalf.com> M: Peter Maydell <peter.maydell@linaro.org> -R: Leif Lindholm <quic_llindhol@quicinc.com> +R: Leif Lindholm <leif.lindholm@oss.qualcomm.com> R: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org> L: qemu-arm@nongnu.org S: Maintained @@ -1918,6 +1918,7 @@ F: tests/qtest/fuzz-sb16-test.c Xilinx CAN M: Francisco Iglesias <francisco.iglesias@amd.com> +M: Vikram Garhwal <vikram.garhwal@bytedance.com> S: Maintained F: hw/net/can/xlnx-* F: include/hw/net/xlnx-* @@ -2677,6 +2678,7 @@ F: include/hw/rx/ CAN bus subsystem and hardware M: Pavel Pisa <pisa@cmp.felk.cvut.cz> M: Francisco Iglesias <francisco.iglesias@amd.com> +M: Vikram Garhwal <vikram.garhwal@bytedance.com> S: Maintained W: https://canbus.pages.fel.cvut.cz/ F: net/can/* diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index cc6e06b..ba8de7b 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -39,65 +39,151 @@ static void partsN(return_nan)(FloatPartsN *a, float_status *s) static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, float_status *s) { + bool have_snan = false; + FloatPartsN *ret; + int cmp; + if (is_snan(a->cls) || is_snan(b->cls)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); + have_snan = true; } if (s->default_nan_mode) { parts_default_nan(a, s); - } else { - int cmp = frac_cmp(a, b); - if (cmp == 0) { - cmp = a->sign < b->sign; - } + return a; + } - if (pickNaN(a->cls, b->cls, cmp > 0, s)) { - a = b; + switch (s->float_2nan_prop_rule) { + case float_2nan_prop_s_ab: + if (have_snan) { + ret = is_snan(a->cls) ? a : b; + break; } + /* fall through */ + case float_2nan_prop_ab: + ret = is_nan(a->cls) ? a : b; + break; + case float_2nan_prop_s_ba: + if (have_snan) { + ret = is_snan(b->cls) ? b : a; + break; + } + /* fall through */ + case float_2nan_prop_ba: + ret = is_nan(b->cls) ? b : a; + break; + case float_2nan_prop_x87: + /* + * This implements x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ if (is_snan(a->cls)) { - parts_silence_nan(a, s); + if (!is_snan(b->cls)) { + ret = is_qnan(b->cls) ? b : a; + break; + } + } else if (is_qnan(a->cls)) { + if (is_snan(b->cls) || !is_qnan(b->cls)) { + ret = a; + break; + } + } else { + ret = b; + break; } + cmp = frac_cmp(a, b); + if (cmp == 0) { + cmp = a->sign < b->sign; + } + ret = cmp > 0 ? a : b; + break; + default: + g_assert_not_reached(); } - return a; + + if (is_snan(ret->cls)) { + parts_silence_nan(ret, s); + } + return ret; } static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, FloatPartsN *c, float_status *s, int ab_mask, int abc_mask) { - int which; + bool infzero = (ab_mask == float_cmask_infzero); + bool have_snan = (abc_mask & float_cmask_snan); + FloatPartsN *ret; - if (unlikely(abc_mask & float_cmask_snan)) { + if (unlikely(have_snan)) { float_raise(float_flag_invalid | float_flag_invalid_snan, s); } - which = pickNaNMulAdd(a->cls, b->cls, c->cls, - ab_mask == float_cmask_infzero, s); + if (infzero) { + /* This is (0 * inf) + NaN or (inf * 0) + NaN */ + float_raise(float_flag_invalid | float_flag_invalid_imz, s); + } - if (s->default_nan_mode || which == 3) { + if (s->default_nan_mode) { /* - * Note that this check is after pickNaNMulAdd so that function - * has an opportunity to set the Invalid flag for infzero. + * We guarantee not to require the target to tell us how to + * pick a NaN if we're always returning the default NaN. + * But if we're not in default-NaN mode then the target must + * specify. */ - parts_default_nan(a, s); - return a; + goto default_nan; + } else if (infzero) { + /* + * Inf * 0 + NaN -- some implementations return the + * default NaN here, and some return the input NaN. + */ + switch (s->float_infzeronan_rule) { + case float_infzeronan_dnan_never: + break; + case float_infzeronan_dnan_always: + goto default_nan; + case float_infzeronan_dnan_if_qnan: + if (is_qnan(c->cls)) { + goto default_nan; + } + break; + default: + g_assert_not_reached(); + } + ret = c; + } else { + FloatPartsN *val[R_3NAN_1ST_MASK + 1] = { a, b, c }; + Float3NaNPropRule rule = s->float_3nan_prop_rule; + + assert(rule != float_3nan_prop_none); + if (have_snan && (rule & R_3NAN_SNAN_MASK)) { + /* We have at least one SNaN input and should prefer it */ + do { + ret = val[rule & R_3NAN_1ST_MASK]; + rule >>= R_3NAN_1ST_LENGTH; + } while (!is_snan(ret->cls)); + } else { + do { + ret = val[rule & R_3NAN_1ST_MASK]; + rule >>= R_3NAN_1ST_LENGTH; + } while (!is_nan(ret->cls)); + } } - switch (which) { - case 0: - break; - case 1: - a = b; - break; - case 2: - a = c; - break; - default: - g_assert_not_reached(); - } - if (is_snan(a->cls)) { - parts_silence_nan(a, s); + if (is_snan(ret->cls)) { + parts_silence_nan(ret, s); } + return ret; + + default_nan: + parts_default_nan(a, s); return a; } diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 9bca03c..cbbbab5 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -133,35 +133,17 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) { bool sign = 0; uint64_t frac; + uint8_t dnan_pattern = status->default_nan_pattern; -#if defined(TARGET_SPARC) || defined(TARGET_M68K) - /* !snan_bit_is_one, set all bits */ - frac = (1ULL << DECOMPOSED_BINARY_POINT) - 1; -#elif defined(TARGET_I386) || defined(TARGET_X86_64) \ - || defined(TARGET_MICROBLAZE) - /* !snan_bit_is_one, set sign and msb */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); - sign = 1; -#elif defined(TARGET_HPPA) - /* snan_bit_is_one, set msb-1. */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2); -#elif defined(TARGET_HEXAGON) - sign = 1; - frac = ~0ULL; -#else + assert(dnan_pattern != 0); + + sign = dnan_pattern >> 7; /* - * This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, - * S390, SH4, TriCore, and Xtensa. Our other supported targets - * do not have floating-point. + * Place default_nan_pattern [6:0] into bits [62:56], + * and replecate bit [0] down into [55:0] */ - if (snan_bit_is_one(status)) { - /* set all bits other than msb */ - frac = (1ULL << (DECOMPOSED_BINARY_POINT - 1)) - 1; - } else { - /* set msb */ - frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); - } -#endif + frac = deposit64(0, DECOMPOSED_BINARY_POINT - 7, 7, dnan_pattern); + frac = deposit64(frac, 0, DECOMPOSED_BINARY_POINT - 7, -(dnan_pattern & 1)); *p = (FloatParts64) { .cls = float_class_qnan, @@ -227,17 +209,17 @@ static void parts128_silence_nan(FloatParts128 *p, float_status *status) floatx80 floatx80_default_nan(float_status *status) { floatx80 r; + /* + * Extrapolate from the choices made by parts64_default_nan to fill + * in the floatx80 format. We assume that floatx80's explicit + * integer bit is always set (this is true for i386 and m68k, + * which are the only real users of this format). + */ + FloatParts64 p64; + parts64_default_nan(&p64, status); - /* None of the targets that have snan_bit_is_one use floatx80. */ - assert(!snan_bit_is_one(status)); -#if defined(TARGET_M68K) - r.low = UINT64_C(0xFFFFFFFFFFFFFFFF); - r.high = 0x7FFF; -#else - /* X86 */ - r.low = UINT64_C(0xC000000000000000); - r.high = 0xFFFF; -#endif + r.high = 0x7FFF | (p64.sign << 15); + r.low = (1ULL << DECOMPOSED_BINARY_POINT) | p64.frac; return r; } @@ -371,312 +353,6 @@ bool float32_is_signaling_nan(float32 a_, float_status *status) } /*---------------------------------------------------------------------------- -| Select which NaN to propagate for a two-input operation. -| IEEE754 doesn't specify all the details of this, so the -| algorithm is target-specific. -| The routine is passed various bits of information about the -| two NaNs and should return 0 to select NaN a and 1 for NaN b. -| Note that signalling NaNs are always squashed to quiet NaNs -| by the caller, by calling floatXX_silence_nan() before -| returning them. -| -| aIsLargerSignificand is only valid if both a and b are NaNs -| of some kind, and is true if a has the larger significand, -| or if both a and b have the same significand but a is -| positive but b is negative. It is only needed for the x87 -| tie-break rule. -*----------------------------------------------------------------------------*/ - -static int pickNaN(FloatClass a_cls, FloatClass b_cls, - bool aIsLargerSignificand, float_status *status) -{ - /* - * We guarantee not to require the target to tell us how to - * pick a NaN if we're always returning the default NaN. - * But if we're not in default-NaN mode then the target must - * specify via set_float_2nan_prop_rule(). - */ - assert(!status->default_nan_mode); - - switch (status->float_2nan_prop_rule) { - case float_2nan_prop_s_ab: - if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } - break; - case float_2nan_prop_s_ba: - if (is_snan(b_cls)) { - return 1; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_qnan(b_cls)) { - return 1; - } else { - return 0; - } - break; - case float_2nan_prop_ab: - if (is_nan(a_cls)) { - return 0; - } else { - return 1; - } - break; - case float_2nan_prop_ba: - if (is_nan(b_cls)) { - return 1; - } else { - return 0; - } - break; - case float_2nan_prop_x87: - /* - * This implements x87 NaN propagation rules: - * SNaN + QNaN => return the QNaN - * two SNaNs => return the one with the larger significand, silenced - * two QNaNs => return the one with the larger significand - * SNaN and a non-NaN => return the SNaN, silenced - * QNaN and a non-NaN => return the QNaN - * - * If we get down to comparing significands and they are the same, - * return the NaN with the positive sign bit (if any). - */ - if (is_snan(a_cls)) { - if (is_snan(b_cls)) { - return aIsLargerSignificand ? 0 : 1; - } - return is_qnan(b_cls) ? 1 : 0; - } else if (is_qnan(a_cls)) { - if (is_snan(b_cls) || !is_qnan(b_cls)) { - return 0; - } else { - return aIsLargerSignificand ? 0 : 1; - } - } else { - return 1; - } - default: - g_assert_not_reached(); - } -} - -/*---------------------------------------------------------------------------- -| Select which NaN to propagate for a three-input operation. -| For the moment we assume that no CPU needs the 'larger significand' -| information. -| Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN -*----------------------------------------------------------------------------*/ -static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, - bool infzero, float_status *status) -{ -#if defined(TARGET_ARM) - /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns - * the default NaN - */ - if (infzero && is_qnan(c_cls)) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 3; - } - - /* This looks different from the ARM ARM pseudocode, because the ARM ARM - * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. - */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } -#elif defined(TARGET_MIPS) - if (snan_bit_is_one(status)) { - /* - * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) - * case sets InvalidOp and returns the default NaN - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 3; - } - /* Prefer sNaN over qNaN, in the a, b, c order. */ - if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_snan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else if (is_qnan(b_cls)) { - return 1; - } else { - return 2; - } - } else { - /* - * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) - * case sets InvalidOp and returns the input value 'c' - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - /* Prefer sNaN over qNaN, in the c, a, b order. */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } - } -#elif defined(TARGET_LOONGARCH64) - /* - * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) - * case sets InvalidOp and returns the input value 'c' - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - /* Prefer sNaN over qNaN, in the c, a, b order. */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } -#elif defined(TARGET_PPC) - /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer - * to return an input NaN if we have one (ie c) rather than generating - * a default NaN - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - - /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; - * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB - */ - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(c_cls)) { - return 2; - } else { - return 1; - } -#elif defined(TARGET_RISCV) - /* For RISC-V, InvalidOp is set when multiplicands are Inf and zero */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - } - return 3; /* default NaN */ -#elif defined(TARGET_S390X) - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 3; - } - - if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_snan(c_cls)) { - return 2; - } else if (is_qnan(a_cls)) { - return 0; - } else if (is_qnan(b_cls)) { - return 1; - } else { - return 2; - } -#elif defined(TARGET_SPARC) - /* For (inf,0,nan) return c. */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - /* Prefer SNaN over QNaN, order C, B, A. */ - if (is_snan(c_cls)) { - return 2; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_snan(a_cls)) { - return 0; - } else if (is_qnan(c_cls)) { - return 2; - } else if (is_qnan(b_cls)) { - return 1; - } else { - return 0; - } -#elif defined(TARGET_XTENSA) - /* - * For Xtensa, the (inf,zero,nan) case sets InvalidOp and returns - * an input NaN if we have one (ie c). - */ - if (infzero) { - float_raise(float_flag_invalid | float_flag_invalid_imz, status); - return 2; - } - if (status->use_first_nan) { - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(b_cls)) { - return 1; - } else { - return 2; - } - } else { - if (is_nan(c_cls)) { - return 2; - } else if (is_nan(b_cls)) { - return 1; - } else { - return 0; - } - } -#else - /* A default implementation: prefer a to b to c. - * This is unlikely to actually match any real implementation. - */ - if (is_nan(a_cls)) { - return 0; - } else if (is_nan(b_cls)) { - return 1; - } else { - return 2; - } -#endif -} - -/*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ @@ -780,58 +456,6 @@ floatx80 floatx80_silence_nan(floatx80 a, float_status *status) } /*---------------------------------------------------------------------------- -| Takes two extended double-precision floating-point values `a' and `b', one -| of which is a NaN, and returns the appropriate NaN result. If either `a' or -| `b' is a signaling NaN, the invalid exception is raised. -*----------------------------------------------------------------------------*/ - -floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) -{ - bool aIsLargerSignificand; - FloatClass a_cls, b_cls; - - /* This is not complete, but is good enough for pickNaN. */ - a_cls = (!floatx80_is_any_nan(a) - ? float_class_normal - : floatx80_is_signaling_nan(a, status) - ? float_class_snan - : float_class_qnan); - b_cls = (!floatx80_is_any_nan(b) - ? float_class_normal - : floatx80_is_signaling_nan(b, status) - ? float_class_snan - : float_class_qnan); - - if (is_snan(a_cls) || is_snan(b_cls)) { - float_raise(float_flag_invalid, status); - } - - if (status->default_nan_mode) { - return floatx80_default_nan(status); - } - - if (a.low < b.low) { - aIsLargerSignificand = 0; - } else if (b.low < a.low) { - aIsLargerSignificand = 1; - } else { - aIsLargerSignificand = (a.high < b.high) ? 1 : 0; - } - - if (pickNaN(a_cls, b_cls, aIsLargerSignificand, status)) { - if (is_snan(b_cls)) { - return floatx80_silence_nan(b, status); - } - return b; - } else { - if (is_snan(a_cls)) { - return floatx80_silence_nan(a, status); - } - return a; - } -} - -/*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 027a8e5..8de8d5f 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -4921,6 +4921,25 @@ void normalizeFloatx80Subnormal(uint64_t aSig, int32_t *zExpPtr, } /*---------------------------------------------------------------------------- +| Takes two extended double-precision floating-point values `a' and `b', one +| of which is a NaN, and returns the appropriate NaN result. If either `a' or +| `b' is a signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) +{ + FloatParts128 pa, pb, *pr; + + if (!floatx80_unpack_canonical(&pa, a, status) || + !floatx80_unpack_canonical(&pb, b, status)) { + return floatx80_default_nan(status); + } + + pr = parts_pick_nan(&pa, &pb, status); + return floatx80_round_pack_canonical(pr, status); +} + +/*---------------------------------------------------------------------------- | Takes an abstract floating-point value having sign `zSign', exponent `zExp', | and extended significand formed by the concatenation of `zSig0' and `zSig1', | and returns the proper extended double-precision floating-point value diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 7fcc0d7..7f80218 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -62,8 +62,12 @@ config VMXNET3_PCI config SMC91C111 bool +config LAN9118_PHY + bool + config LAN9118 bool + select LAN9118_PHY select PTIMER config NE2000_ISA @@ -89,6 +93,7 @@ config ALLWINNER_SUN8I_EMAC config IMX_FEC bool + select LAN9118_PHY config CADENCE bool diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 6294d29..4ee6f74 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -203,17 +203,12 @@ static const VMStateDescription vmstate_imx_eth_txdescs = { static const VMStateDescription vmstate_imx_eth = { .name = TYPE_IMX_FEC, - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX), VMSTATE_UINT32(rx_descriptor, IMXFECState), VMSTATE_UINT32(tx_descriptor[0], IMXFECState), - VMSTATE_UINT32(phy_status, IMXFECState), - VMSTATE_UINT32(phy_control, IMXFECState), - VMSTATE_UINT32(phy_advertise, IMXFECState), - VMSTATE_UINT32(phy_int, IMXFECState), - VMSTATE_UINT32(phy_int_mask, IMXFECState), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * const []) { @@ -222,14 +217,6 @@ static const VMStateDescription vmstate_imx_eth = { }, }; -#define PHY_INT_ENERGYON (1 << 7) -#define PHY_INT_AUTONEG_COMPLETE (1 << 6) -#define PHY_INT_FAULT (1 << 5) -#define PHY_INT_DOWN (1 << 4) -#define PHY_INT_AUTONEG_LP (1 << 3) -#define PHY_INT_PARFAULT (1 << 2) -#define PHY_INT_AUTONEG_PAGE (1 << 1) - static void imx_eth_update(IMXFECState *s); /* @@ -238,47 +225,19 @@ static void imx_eth_update(IMXFECState *s); * For now we don't handle any GPIO/interrupt line, so the OS will * have to poll for the PHY status. */ -static void imx_phy_update_irq(IMXFECState *s) -{ - imx_eth_update(s); -} - -static void imx_phy_update_link(IMXFECState *s) +static void imx_phy_update_irq(void *opaque, int n, int level) { - /* Autonegotiation status mirrors link status. */ - if (qemu_get_queue(s->nic)->link_down) { - trace_imx_phy_update_link("down"); - s->phy_status &= ~0x0024; - s->phy_int |= PHY_INT_DOWN; - } else { - trace_imx_phy_update_link("up"); - s->phy_status |= 0x0024; - s->phy_int |= PHY_INT_ENERGYON; - s->phy_int |= PHY_INT_AUTONEG_COMPLETE; - } - imx_phy_update_irq(s); + imx_eth_update(opaque); } static void imx_eth_set_link(NetClientState *nc) { - imx_phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); -} - -static void imx_phy_reset(IMXFECState *s) -{ - trace_imx_phy_reset(); - - s->phy_status = 0x7809; - s->phy_control = 0x3000; - s->phy_advertise = 0x01e1; - s->phy_int_mask = 0; - s->phy_int = 0; - imx_phy_update_link(s); + lan9118_phy_update_link(&IMX_FEC(qemu_get_nic_opaque(nc))->mii, + nc->link_down); } static uint32_t imx_phy_read(IMXFECState *s, int reg) { - uint32_t val; uint32_t phy = reg / 32; if (!s->phy_connected) { @@ -296,54 +255,7 @@ static uint32_t imx_phy_read(IMXFECState *s, int reg) reg %= 32; - switch (reg) { - case 0: /* Basic Control */ - val = s->phy_control; - break; - case 1: /* Basic Status */ - val = s->phy_status; - break; - case 2: /* ID1 */ - val = 0x0007; - break; - case 3: /* ID2 */ - val = 0xc0d1; - break; - case 4: /* Auto-neg advertisement */ - val = s->phy_advertise; - break; - case 5: /* Auto-neg Link Partner Ability */ - val = 0x0f71; - break; - case 6: /* Auto-neg Expansion */ - val = 1; - break; - case 29: /* Interrupt source. */ - val = s->phy_int; - s->phy_int = 0; - imx_phy_update_irq(s); - break; - case 30: /* Interrupt mask */ - val = s->phy_int_mask; - break; - case 17: - case 18: - case 27: - case 31: - qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n", - TYPE_IMX_FEC, __func__, reg); - val = 0; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", - TYPE_IMX_FEC, __func__, reg); - val = 0; - break; - } - - trace_imx_phy_read(val, phy, reg); - - return val; + return lan9118_phy_read(&s->mii, reg); } static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) @@ -365,39 +277,7 @@ static void imx_phy_write(IMXFECState *s, int reg, uint32_t val) reg %= 32; - trace_imx_phy_write(val, phy, reg); - - switch (reg) { - case 0: /* Basic Control */ - if (val & 0x8000) { - imx_phy_reset(s); - } else { - s->phy_control = val & 0x7980; - /* Complete autonegotiation immediately. */ - if (val & 0x1000) { - s->phy_status |= 0x0020; - } - } - break; - case 4: /* Auto-neg advertisement */ - s->phy_advertise = (val & 0x2d7f) | 0x80; - break; - case 30: /* Interrupt mask */ - s->phy_int_mask = val & 0xff; - imx_phy_update_irq(s); - break; - case 17: - case 18: - case 27: - case 31: - qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n", - TYPE_IMX_FEC, __func__, reg); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n", - TYPE_IMX_FEC, __func__, reg); - break; - } + lan9118_phy_write(&s->mii, reg, val); } static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) @@ -682,9 +562,6 @@ static void imx_eth_reset(DeviceState *d) s->rx_descriptor = 0; memset(s->tx_descriptor, 0, sizeof(s->tx_descriptor)); - - /* We also reset the PHY */ - imx_phy_reset(s); } static uint32_t imx_default_read(IMXFECState *s, uint32_t index) @@ -1329,6 +1206,13 @@ static void imx_eth_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq[0]); sysbus_init_irq(sbd, &s->irq[1]); + qemu_init_irq(&s->mii_irq, imx_phy_update_irq, s, 0); + object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY); + if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) { + return; + } + qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq); + qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf, diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index db28a0e..99e87b7 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -16,6 +16,7 @@ #include "net/net.h" #include "net/eth.h" #include "hw/irq.h" +#include "hw/net/lan9118_phy.h" #include "hw/net/lan9118.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" @@ -139,14 +140,6 @@ do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0) #define MAC_CR_RXEN 0x00000004 #define MAC_CR_RESERVED 0x7f404213 -#define PHY_INT_ENERGYON 0x80 -#define PHY_INT_AUTONEG_COMPLETE 0x40 -#define PHY_INT_FAULT 0x20 -#define PHY_INT_DOWN 0x10 -#define PHY_INT_AUTONEG_LP 0x08 -#define PHY_INT_PARFAULT 0x04 -#define PHY_INT_AUTONEG_PAGE 0x02 - #define GPT_TIMER_EN 0x20000000 /* @@ -228,11 +221,8 @@ struct lan9118_state { uint32_t mac_mii_data; uint32_t mac_flow; - uint32_t phy_status; - uint32_t phy_control; - uint32_t phy_advertise; - uint32_t phy_int; - uint32_t phy_int_mask; + Lan9118PhyState mii; + IRQState mii_irq; int32_t eeprom_writable; uint8_t eeprom[128]; @@ -274,8 +264,8 @@ struct lan9118_state { static const VMStateDescription vmstate_lan9118 = { .name = "lan9118", - .version_id = 2, - .minimum_version_id = 1, + .version_id = 3, + .minimum_version_id = 3, .fields = (const VMStateField[]) { VMSTATE_PTIMER(timer, lan9118_state), VMSTATE_UINT32(irq_cfg, lan9118_state), @@ -301,11 +291,6 @@ static const VMStateDescription vmstate_lan9118 = { VMSTATE_UINT32(mac_mii_acc, lan9118_state), VMSTATE_UINT32(mac_mii_data, lan9118_state), VMSTATE_UINT32(mac_flow, lan9118_state), - VMSTATE_UINT32(phy_status, lan9118_state), - VMSTATE_UINT32(phy_control, lan9118_state), - VMSTATE_UINT32(phy_advertise, lan9118_state), - VMSTATE_UINT32(phy_int, lan9118_state), - VMSTATE_UINT32(phy_int_mask, lan9118_state), VMSTATE_INT32(eeprom_writable, lan9118_state), VMSTATE_UINT8_ARRAY(eeprom, lan9118_state, 128), VMSTATE_INT32(tx_fifo_size, lan9118_state), @@ -385,9 +370,11 @@ static void lan9118_reload_eeprom(lan9118_state *s) lan9118_mac_changed(s); } -static void phy_update_irq(lan9118_state *s) +static void lan9118_update_irq(void *opaque, int n, int level) { - if (s->phy_int & s->phy_int_mask) { + lan9118_state *s = opaque; + + if (level) { s->int_sts |= PHY_INT; } else { s->int_sts &= ~PHY_INT; @@ -395,33 +382,10 @@ static void phy_update_irq(lan9118_state *s) lan9118_update(s); } -static void phy_update_link(lan9118_state *s) -{ - /* Autonegotiation status mirrors link status. */ - if (qemu_get_queue(s->nic)->link_down) { - s->phy_status &= ~0x0024; - s->phy_int |= PHY_INT_DOWN; - } else { - s->phy_status |= 0x0024; - s->phy_int |= PHY_INT_ENERGYON; - s->phy_int |= PHY_INT_AUTONEG_COMPLETE; - } - phy_update_irq(s); -} - static void lan9118_set_link(NetClientState *nc) { - phy_update_link(qemu_get_nic_opaque(nc)); -} - -static void phy_reset(lan9118_state *s) -{ - s->phy_status = 0x7809; - s->phy_control = 0x3000; - s->phy_advertise = 0x01e1; - s->phy_int_mask = 0; - s->phy_int = 0; - phy_update_link(s); + lan9118_phy_update_link(&LAN9118(qemu_get_nic_opaque(nc))->mii, + nc->link_down); } static void lan9118_reset(DeviceState *d) @@ -478,8 +442,6 @@ static void lan9118_reset(DeviceState *d) s->read_word_n = 0; s->write_word_n = 0; - phy_reset(s); - s->eeprom_writable = 0; lan9118_reload_eeprom(s); } @@ -678,7 +640,7 @@ static void do_tx_packet(lan9118_state *s) uint32_t status; /* FIXME: Honor TX disable, and allow queueing of packets. */ - if (s->phy_control & 0x4000) { + if (s->mii.control & 0x4000) { /* This assumes the receive routine doesn't touch the VLANClient. */ qemu_receive_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } else { @@ -834,68 +796,6 @@ static void tx_fifo_push(lan9118_state *s, uint32_t val) } } -static uint32_t do_phy_read(lan9118_state *s, int reg) -{ - uint32_t val; - - switch (reg) { - case 0: /* Basic Control */ - return s->phy_control; - case 1: /* Basic Status */ - return s->phy_status; - case 2: /* ID1 */ - return 0x0007; - case 3: /* ID2 */ - return 0xc0d1; - case 4: /* Auto-neg advertisement */ - return s->phy_advertise; - case 5: /* Auto-neg Link Partner Ability */ - return 0x0f71; - case 6: /* Auto-neg Expansion */ - return 1; - /* TODO 17, 18, 27, 29, 30, 31 */ - case 29: /* Interrupt source. */ - val = s->phy_int; - s->phy_int = 0; - phy_update_irq(s); - return val; - case 30: /* Interrupt mask */ - return s->phy_int_mask; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "do_phy_read: PHY read reg %d\n", reg); - return 0; - } -} - -static void do_phy_write(lan9118_state *s, int reg, uint32_t val) -{ - switch (reg) { - case 0: /* Basic Control */ - if (val & 0x8000) { - phy_reset(s); - break; - } - s->phy_control = val & 0x7980; - /* Complete autonegotiation immediately. */ - if (val & 0x1000) { - s->phy_status |= 0x0020; - } - break; - case 4: /* Auto-neg advertisement */ - s->phy_advertise = (val & 0x2d7f) | 0x80; - break; - /* TODO 17, 18, 27, 31 */ - case 30: /* Interrupt mask */ - s->phy_int_mask = val & 0xff; - phy_update_irq(s); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "do_phy_write: PHY write reg %d = 0x%04x\n", reg, val); - } -} - static void do_mac_write(lan9118_state *s, int reg, uint32_t val) { switch (reg) { @@ -929,9 +829,9 @@ static void do_mac_write(lan9118_state *s, int reg, uint32_t val) if (val & 2) { DPRINTF("PHY write %d = 0x%04x\n", (val >> 6) & 0x1f, s->mac_mii_data); - do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data); + lan9118_phy_write(&s->mii, (val >> 6) & 0x1f, s->mac_mii_data); } else { - s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f); + s->mac_mii_data = lan9118_phy_read(&s->mii, (val >> 6) & 0x1f); DPRINTF("PHY read %d = 0x%04x\n", (val >> 6) & 0x1f, s->mac_mii_data); } @@ -1126,7 +1026,7 @@ static void lan9118_writel(void *opaque, hwaddr offset, break; case CSR_PMT_CTRL: if (val & 0x400) { - phy_reset(s); + lan9118_phy_reset(&s->mii); } s->pmt_ctrl &= ~0x34e; s->pmt_ctrl |= (val & 0x34e); @@ -1373,6 +1273,13 @@ static void lan9118_realize(DeviceState *dev, Error **errp) const MemoryRegionOps *mem_ops = s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops; + qemu_init_irq(&s->mii_irq, lan9118_update_irq, s, 0); + object_initialize_child(OBJECT(s), "mii", &s->mii, TYPE_LAN9118_PHY); + if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(&s->mii), errp)) { + return; + } + qdev_connect_gpio_out(DEVICE(&s->mii), 0, &s->mii_irq); + memory_region_init_io(&s->mmio, OBJECT(dev), mem_ops, s, "lan9118-mmio", 0x100); sysbus_init_mmio(sbd, &s->mmio); diff --git a/hw/net/lan9118_phy.c b/hw/net/lan9118_phy.c new file mode 100644 index 0000000..5c53a4a --- /dev/null +++ b/hw/net/lan9118_phy.c @@ -0,0 +1,222 @@ +/* + * SMSC LAN9118 PHY emulation + * + * Copyright (c) 2009 CodeSourcery, LLC. + * Written by Paul Brook + * + * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net> + * + * This code is licensed under the GNU GPL v2 + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "hw/net/lan9118_phy.h" +#include "hw/net/mii.h" +#include "hw/irq.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "trace.h" + +#define PHY_INT_ENERGYON (1 << 7) +#define PHY_INT_AUTONEG_COMPLETE (1 << 6) +#define PHY_INT_FAULT (1 << 5) +#define PHY_INT_DOWN (1 << 4) +#define PHY_INT_AUTONEG_LP (1 << 3) +#define PHY_INT_PARFAULT (1 << 2) +#define PHY_INT_AUTONEG_PAGE (1 << 1) + +static void lan9118_phy_update_irq(Lan9118PhyState *s) +{ + qemu_set_irq(s->irq, !!(s->ints & s->int_mask)); +} + +uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg) +{ + uint16_t val; + + switch (reg) { + case MII_BMCR: + val = s->control; + break; + case MII_BMSR: + val = s->status; + break; + case MII_PHYID1: + val = SMSCLAN9118_PHYID1; + break; + case MII_PHYID2: + val = SMSCLAN9118_PHYID2; + break; + case MII_ANAR: + val = s->advertise; + break; + case MII_ANLPAR: + val = MII_ANLPAR_PAUSEASY | MII_ANLPAR_PAUSE | MII_ANLPAR_T4 | + MII_ANLPAR_TXFD | MII_ANLPAR_TX | MII_ANLPAR_10FD | + MII_ANLPAR_10 | MII_ANLPAR_CSMACD; + break; + case MII_ANER: + val = MII_ANER_NWAY; + break; + case 29: /* Interrupt source. */ + val = s->ints; + s->ints = 0; + lan9118_phy_update_irq(s); + break; + case 30: /* Interrupt mask */ + val = s->int_mask; + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + val = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + val = 0; + break; + } + + trace_lan9118_phy_read(val, reg); + + return val; +} + +void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val) +{ + trace_lan9118_phy_write(val, reg); + + switch (reg) { + case MII_BMCR: + if (val & MII_BMCR_RESET) { + lan9118_phy_reset(s); + } else { + s->control = val & (MII_BMCR_LOOPBACK | MII_BMCR_SPEED100 | + MII_BMCR_AUTOEN | MII_BMCR_PDOWN | MII_BMCR_FD | + MII_BMCR_CTST); + /* Complete autonegotiation immediately. */ + if (val & MII_BMCR_AUTOEN) { + s->status |= MII_BMSR_AN_COMP; + } + } + break; + case MII_ANAR: + s->advertise = (val & (MII_ANAR_RFAULT | MII_ANAR_PAUSE_ASYM | + MII_ANAR_PAUSE | MII_ANAR_TXFD | MII_ANAR_10FD | + MII_ANAR_10 | MII_ANAR_SELECT)) + | MII_ANAR_TX; + break; + case 30: /* Interrupt mask */ + s->int_mask = val & 0xff; + lan9118_phy_update_irq(s); + break; + case 17: + case 18: + case 27: + case 31: + qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n", + __func__, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n", + __func__, reg); + break; + } +} + +void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down) +{ + s->link_down = link_down; + + /* Autonegotiation status mirrors link status. */ + if (link_down) { + trace_lan9118_phy_update_link("down"); + s->status &= ~(MII_BMSR_AN_COMP | MII_BMSR_LINK_ST); + s->ints |= PHY_INT_DOWN; + } else { + trace_lan9118_phy_update_link("up"); + s->status |= MII_BMSR_AN_COMP | MII_BMSR_LINK_ST; + s->ints |= PHY_INT_ENERGYON; + s->ints |= PHY_INT_AUTONEG_COMPLETE; + } + lan9118_phy_update_irq(s); +} + +void lan9118_phy_reset(Lan9118PhyState *s) +{ + trace_lan9118_phy_reset(); + + s->control = MII_BMCR_AUTOEN | MII_BMCR_SPEED100; + s->status = MII_BMSR_100TX_FD + | MII_BMSR_100TX_HD + | MII_BMSR_10T_FD + | MII_BMSR_10T_HD + | MII_BMSR_AUTONEG + | MII_BMSR_EXTCAP; + s->advertise = MII_ANAR_TXFD + | MII_ANAR_TX + | MII_ANAR_10FD + | MII_ANAR_10 + | MII_ANAR_CSMACD; + s->int_mask = 0; + s->ints = 0; + lan9118_phy_update_link(s, s->link_down); +} + +static void lan9118_phy_reset_hold(Object *obj, ResetType type) +{ + Lan9118PhyState *s = LAN9118_PHY(obj); + + lan9118_phy_reset(s); +} + +static void lan9118_phy_init(Object *obj) +{ + Lan9118PhyState *s = LAN9118_PHY(obj); + + qdev_init_gpio_out(DEVICE(s), &s->irq, 1); +} + +static const VMStateDescription vmstate_lan9118_phy = { + .name = "lan9118-phy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT16(status, Lan9118PhyState), + VMSTATE_UINT16(control, Lan9118PhyState), + VMSTATE_UINT16(advertise, Lan9118PhyState), + VMSTATE_UINT16(ints, Lan9118PhyState), + VMSTATE_UINT16(int_mask, Lan9118PhyState), + VMSTATE_BOOL(link_down, Lan9118PhyState), + VMSTATE_END_OF_LIST() + } +}; + +static void lan9118_phy_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + rc->phases.hold = lan9118_phy_reset_hold; + dc->vmsd = &vmstate_lan9118_phy; +} + +static const TypeInfo types[] = { + { + .name = TYPE_LAN9118_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Lan9118PhyState), + .instance_init = lan9118_phy_init, + .class_init = lan9118_phy_class_init, + } +}; + +DEFINE_TYPES(types) diff --git a/hw/net/meson.build b/hw/net/meson.build index 00a9e9d..3bb5d74 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -19,6 +19,7 @@ system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('vmxnet3.c')) system_ss.add(when: 'CONFIG_SMC91C111', if_true: files('smc91c111.c')) system_ss.add(when: 'CONFIG_LAN9118', if_true: files('lan9118.c')) +system_ss.add(when: 'CONFIG_LAN9118_PHY', if_true: files('lan9118_phy.c')) system_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) system_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) system_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) diff --git a/hw/net/trace-events b/hw/net/trace-events index d0f1d8c..6100ec3 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -10,6 +10,12 @@ allwinner_sun8i_emac_set_link(bool active) "Set link: active=%u" allwinner_sun8i_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%" PRIx64 " value=0x%" PRIx64 allwinner_sun8i_emac_write(uint64_t offset, uint64_t val) "MMIO write: offset=0x%" PRIx64 " value=0x%" PRIx64 +# lan9118_phy.c +lan9118_phy_read(uint16_t val, int reg) "[0x%02x] -> 0x%04" PRIx16 +lan9118_phy_write(uint16_t val, int reg) "[0x%02x] <- 0x%04" PRIx16 +lan9118_phy_update_link(const char *s) "%s" +lan9118_phy_reset(void) "" + # lance.c lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64"val=0x%04x" @@ -428,12 +434,8 @@ i82596_set_multicast(uint16_t count) "Added %d multicast entries" i82596_channel_attention(void *s) "%p: Received CHANNEL ATTENTION" # imx_fec.c -imx_phy_read(uint32_t val, int phy, int reg) "0x%04"PRIx32" <= phy[%d].reg[%d]" imx_phy_read_num(int phy, int configured) "read request from unconfigured phy %d (configured %d)" -imx_phy_write(uint32_t val, int phy, int reg) "0x%04"PRIx32" => phy[%d].reg[%d]" imx_phy_write_num(int phy, int configured) "write request to unconfigured phy %d (configured %d)" -imx_phy_update_link(const char *s) "%s" -imx_phy_reset(void) "" imx_fec_read_bd(uint64_t addr, int flags, int len, int data) "tx_bd 0x%"PRIx64" flags 0x%04x len %d data 0x%08x" imx_enet_read_bd(uint64_t addr, int flags, int len, int data, int options, int status) "tx_bd 0x%"PRIx64" flags 0x%04x len %d data 0x%08x option 0x%04x status 0x%04x" imx_eth_tx_bd_busy(void) "tx_bd ran out of descriptors to transmit" diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index 453188d..dceee23 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -81,6 +81,24 @@ static inline void set_float_2nan_prop_rule(Float2NaNPropRule rule, status->float_2nan_prop_rule = rule; } +static inline void set_float_3nan_prop_rule(Float3NaNPropRule rule, + float_status *status) +{ + status->float_3nan_prop_rule = rule; +} + +static inline void set_float_infzeronan_rule(FloatInfZeroNaNRule rule, + float_status *status) +{ + status->float_infzeronan_rule = rule; +} + +static inline void set_float_default_nan_pattern(uint8_t dnan_pattern, + float_status *status) +{ + status->default_nan_pattern = dnan_pattern; +} + static inline void set_flush_to_zero(bool val, float_status *status) { status->flush_to_zero = val; @@ -101,11 +119,6 @@ static inline void set_snan_bit_is_one(bool val, float_status *status) status->snan_bit_is_one = val; } -static inline void set_use_first_nan(bool val, float_status *status) -{ - status->use_first_nan = val; -} - static inline void set_no_signaling_nans(bool val, float_status *status) { status->no_signaling_nans = val; @@ -137,6 +150,21 @@ static inline Float2NaNPropRule get_float_2nan_prop_rule(float_status *status) return status->float_2nan_prop_rule; } +static inline Float3NaNPropRule get_float_3nan_prop_rule(float_status *status) +{ + return status->float_3nan_prop_rule; +} + +static inline FloatInfZeroNaNRule get_float_infzeronan_rule(float_status *status) +{ + return status->float_infzeronan_rule; +} + +static inline uint8_t get_float_default_nan_pattern(float_status *status) +{ + return status->default_nan_pattern; +} + static inline bool get_flush_to_zero(float_status *status) { return status->flush_to_zero; diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 8f39691..79ca44d 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -80,6 +80,8 @@ this code that are retained. #ifndef SOFTFLOAT_TYPES_H #define SOFTFLOAT_TYPES_H +#include "hw/registerfields.h" + /* * Software IEC/IEEE floating-point types. */ @@ -208,6 +210,80 @@ typedef enum __attribute__((__packed__)) { } Float2NaNPropRule; /* + * 3-input NaN propagation rule, for fused multiply-add. Individual + * architectures have different rules for which input NaN is + * propagated to the output when there is more than one NaN on the + * input. + * + * If default_nan_mode is enabled then it is valid not to set a NaN + * propagation rule, because the softfloat code guarantees not to try + * to pick a NaN to propagate in default NaN mode. When not in + * default-NaN mode, it is an error for the target not to set the rule + * in float_status if it uses a muladd, and we will assert if we need + * to handle an input NaN and no rule was selected. + * + * The naming scheme for Float3NaNPropRule values is: + * float_3nan_prop_s_abc: + * = "Prefer SNaN over QNaN, then operand A over B over C" + * float_3nan_prop_abc: + * = "Prefer A over B over C regardless of SNaN vs QNAN" + * + * For QEMU, the multiply-add operation is A * B + C. + */ + +/* + * We set the Float3NaNPropRule enum values up so we can select the + * right value in pickNaNMulAdd in a data driven way. + */ +FIELD(3NAN, 1ST, 0, 2) /* which operand is most preferred ? */ +FIELD(3NAN, 2ND, 2, 2) /* which operand is next most preferred ? */ +FIELD(3NAN, 3RD, 4, 2) /* which operand is least preferred ? */ +FIELD(3NAN, SNAN, 6, 1) /* do we prefer SNaN over QNaN ? */ + +#define PROPRULE(X, Y, Z) \ + ((X << R_3NAN_1ST_SHIFT) | (Y << R_3NAN_2ND_SHIFT) | (Z << R_3NAN_3RD_SHIFT)) + +typedef enum __attribute__((__packed__)) { + float_3nan_prop_none = 0, /* No propagation rule specified */ + float_3nan_prop_abc = PROPRULE(0, 1, 2), + float_3nan_prop_acb = PROPRULE(0, 2, 1), + float_3nan_prop_bac = PROPRULE(1, 0, 2), + float_3nan_prop_bca = PROPRULE(1, 2, 0), + float_3nan_prop_cab = PROPRULE(2, 0, 1), + float_3nan_prop_cba = PROPRULE(2, 1, 0), + float_3nan_prop_s_abc = float_3nan_prop_abc | R_3NAN_SNAN_MASK, + float_3nan_prop_s_acb = float_3nan_prop_acb | R_3NAN_SNAN_MASK, + float_3nan_prop_s_bac = float_3nan_prop_bac | R_3NAN_SNAN_MASK, + float_3nan_prop_s_bca = float_3nan_prop_bca | R_3NAN_SNAN_MASK, + float_3nan_prop_s_cab = float_3nan_prop_cab | R_3NAN_SNAN_MASK, + float_3nan_prop_s_cba = float_3nan_prop_cba | R_3NAN_SNAN_MASK, +} Float3NaNPropRule; + +#undef PROPRULE + +/* + * Rule for result of fused multiply-add 0 * Inf + NaN. + * This must be a NaN, but implementations differ on whether this + * is the input NaN or the default NaN. + * + * You don't need to set this if default_nan_mode is enabled. + * When not in default-NaN mode, it is an error for the target + * not to set the rule in float_status if it uses muladd, and we + * will assert if we need to handle an input NaN and no rule was + * selected. + */ +typedef enum __attribute__((__packed__)) { + /* No propagation rule specified */ + float_infzeronan_none = 0, + /* Result is never the default NaN (so always the input NaN) */ + float_infzeronan_dnan_never, + /* Result is always the default NaN */ + float_infzeronan_dnan_always, + /* Result is the default NaN if the input NaN is quiet */ + float_infzeronan_dnan_if_qnan, +} FloatInfZeroNaNRule; + +/* * Floating Point Status. Individual architectures may maintain * several versions of float_status for different functions. The * correct status for the operation is then passed by reference to @@ -219,6 +295,8 @@ typedef struct float_status { FloatRoundMode float_rounding_mode; FloatX80RoundPrec floatx80_rounding_precision; Float2NaNPropRule float_2nan_prop_rule; + Float3NaNPropRule float_3nan_prop_rule; + FloatInfZeroNaNRule float_infzeronan_rule; bool tininess_before_rounding; /* should denormalised results go to zero and set the inexact flag? */ bool flush_to_zero; @@ -226,12 +304,21 @@ typedef struct float_status { bool flush_inputs_to_zero; bool default_nan_mode; /* + * The pattern to use for the default NaN. Here the high bit specifies + * the default NaN's sign bit, and bits 6..0 specify the high bits of the + * fractional part. The low bits of the fractional part are copies of bit 0. + * The exponent of the default NaN is (as for any NaN) always all 1s. + * Note that a value of 0 here is not a valid NaN. The target must set + * this to the correct non-zero value, or we will assert when trying to + * create a default NaN. + */ + uint8_t default_nan_pattern; + /* * The flags below are not used on all specializations and may * constant fold away (see snan_bit_is_one()/no_signalling_nans() in * softfloat-specialize.inc.c) */ bool snan_bit_is_one; - bool use_first_nan; bool no_signaling_nans; /* should overflowed results subtract re_bias to its exponent? */ bool rebias_overflow; diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h index 2d13290..83b2163 100644 --- a/include/hw/net/imx_fec.h +++ b/include/hw/net/imx_fec.h @@ -31,6 +31,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(IMXFECState, IMX_FEC) #define TYPE_IMX_ENET "imx.enet" #include "hw/sysbus.h" +#include "hw/net/lan9118_phy.h" +#include "hw/irq.h" #include "net/net.h" #define ENET_EIR 1 @@ -264,11 +266,8 @@ struct IMXFECState { uint32_t tx_descriptor[ENET_TX_RING_NUM]; uint32_t tx_ring_num; - uint32_t phy_status; - uint32_t phy_control; - uint32_t phy_advertise; - uint32_t phy_int; - uint32_t phy_int_mask; + Lan9118PhyState mii; + IRQState mii_irq; uint32_t phy_num; bool phy_connected; struct IMXFECState *phy_consumer; diff --git a/include/hw/net/lan9118_phy.h b/include/hw/net/lan9118_phy.h new file mode 100644 index 0000000..af12fc3 --- /dev/null +++ b/include/hw/net/lan9118_phy.h @@ -0,0 +1,37 @@ +/* + * SMSC LAN9118 PHY emulation + * + * Copyright (c) 2009 CodeSourcery, LLC. + * Written by Paul Brook + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_NET_LAN9118_PHY_H +#define HW_NET_LAN9118_PHY_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +#define TYPE_LAN9118_PHY "lan9118-phy" +OBJECT_DECLARE_SIMPLE_TYPE(Lan9118PhyState, LAN9118_PHY) + +typedef struct Lan9118PhyState { + SysBusDevice parent_obj; + + uint16_t status; + uint16_t control; + uint16_t advertise; + uint16_t ints; + uint16_t int_mask; + qemu_irq irq; + bool link_down; +} Lan9118PhyState; + +void lan9118_phy_update_link(Lan9118PhyState *s, bool link_down); +void lan9118_phy_reset(Lan9118PhyState *s); +uint16_t lan9118_phy_read(Lan9118PhyState *s, int reg); +void lan9118_phy_write(Lan9118PhyState *s, int reg, uint16_t val); + +#endif diff --git a/include/hw/net/mii.h b/include/hw/net/mii.h index f7feddac..55bf7c9 100644 --- a/include/hw/net/mii.h +++ b/include/hw/net/mii.h @@ -71,6 +71,7 @@ #define MII_BMSR_JABBER (1 << 1) /* Jabber detected */ #define MII_BMSR_EXTCAP (1 << 0) /* Ext-reg capability */ +#define MII_ANAR_RFAULT (1 << 13) /* Say we can detect faults */ #define MII_ANAR_PAUSE_ASYM (1 << 11) /* Try for asymmetric pause */ #define MII_ANAR_PAUSE (1 << 10) /* Try for pause */ #define MII_ANAR_TXFD (1 << 8) @@ -78,6 +79,7 @@ #define MII_ANAR_10FD (1 << 6) #define MII_ANAR_10 (1 << 5) #define MII_ANAR_CSMACD (1 << 0) +#define MII_ANAR_SELECT (0x001f) /* Selector bits */ #define MII_ANLPAR_ACK (1 << 14) #define MII_ANLPAR_PAUSEASY (1 << 11) /* can pause asymmetrically */ @@ -112,6 +114,10 @@ #define RTL8201CP_PHYID1 0x0000 #define RTL8201CP_PHYID2 0x8201 +/* SMSC LAN9118 */ +#define SMSCLAN9118_PHYID1 0x0007 +#define SMSCLAN9118_PHYID2 0xc0d1 + /* RealTek 8211E */ #define RTL8211E_PHYID1 0x001c #define RTL8211E_PHYID2 0xc915 diff --git a/linux-user/arm/nwfpe/fpa11.c b/linux-user/arm/nwfpe/fpa11.c index 8356beb..0f1afbd 100644 --- a/linux-user/arm/nwfpe/fpa11.c +++ b/linux-user/arm/nwfpe/fpa11.c @@ -69,6 +69,11 @@ void resetFPA11(void) * this late date. */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &fpa11->fp_status); + /* + * Use the same default NaN value as Arm VFP. This doesn't match + * the Linux kernel's nwfpe emulation, which uses an all-1s value. + */ + set_float_default_nan_pattern(0b01000000, &fpa11->fp_status); } void SetRoundingMode(const unsigned int opcode) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 5d75c94..70f67e6 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -199,6 +199,8 @@ static void alpha_cpu_initfn(Object *obj) * operand in Fa. That is float_2nan_prop_ba. */ set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); + /* Default NaN: sign bit clear, msb frac bit set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); #if defined(CONFIG_USER_ONLY) env->flags = ENV_FLAG_PS_USER | ENV_FLAG_FEN; cpu_alpha_store_fpcr(env, (uint64_t)(FPCR_INVD | FPCR_DZED | FPCR_OVFD diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6938161..4f7e18e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -173,11 +173,21 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, * * tininess-before-rounding * * 2-input NaN propagation prefers SNaN over QNaN, and then * operand A over operand B (see FPProcessNaNs() pseudocode) + * * 3-input NaN propagation prefers SNaN over QNaN, and then + * operand C over A over B (see FPProcessNaNs3() pseudocode, + * but note that for QEMU muladd is a * b + c, whereas for + * the pseudocode function the arguments are in the order c, a, b. + * * 0 * Inf + NaN returns the default NaN if the input NaN is quiet, + * and the input NaN if it is signalling + * * Default NaN has sign bit clear, msb frac bit set */ static void arm_set_default_fp_behaviours(float_status *s) { set_float_detect_tininess(float_tininess_before_rounding, s); set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, s); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s); + set_float_default_nan_pattern(0b01000000, s); } static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index e825d50..ad6f265 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2813,25 +2813,19 @@ bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp) * no effect on AArch32 instructions. */ bool ebf = is_a64(env) && env->vfp.fpcr & FPCR_EBF; - *statusp = (float_status){ - .tininess_before_rounding = float_tininess_before_rounding, - .float_rounding_mode = float_round_to_odd_inf, - .flush_to_zero = true, - .flush_inputs_to_zero = true, - .default_nan_mode = true, - }; - if (ebf) { - float_status *fpst = &env->vfp.fp_status; - set_flush_to_zero(get_flush_to_zero(fpst), statusp); - set_flush_inputs_to_zero(get_flush_inputs_to_zero(fpst), statusp); - set_float_rounding_mode(get_float_rounding_mode(fpst), statusp); + *statusp = env->vfp.fp_status; + set_default_nan_mode(true, statusp); + if (ebf) { /* EBF=1 needs to do a step with round-to-odd semantics */ *oddstatusp = *statusp; set_float_rounding_mode(float_round_to_odd, oddstatusp); + } else { + set_flush_to_zero(true, statusp); + set_flush_inputs_to_zero(true, statusp); + set_float_rounding_mode(float_round_to_odd_inf, statusp); } - return ebf; } diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 020038f..c9aa940 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -286,6 +286,8 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type) set_default_nan_mode(1, &env->fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); + /* Default NaN value: sign bit set, all frac bits set */ + set_float_default_nan_pattern(0b11111111, &env->fp_status); } static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info) diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 0e44074..239c027 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -55,6 +55,18 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) * HPPA does note implement a CPU reset method at all... */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status); + /* + * TODO: The HPPA architecture reference only documents its NaN + * propagation rule for 2-operand operations. Testing on real hardware + * might be necessary to confirm whether this order for muladd is correct. + * Not preferring the SNaN is almost certainly incorrect as it diverges + * from the documented rules for 2-operand operations. + */ + set_float_3nan_prop_rule(float_3nan_prop_abc, &env->fp_status); + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + /* Default NaN: sign bit clear, msb-1 frac bit set */ + set_float_default_nan_pattern(0b00100000, &env->fp_status); } void cpu_hppa_loaded_fr0(CPUHPPAState *env) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 53b49bb..d0a1e2f 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -173,6 +173,18 @@ void cpu_init_fp_statuses(CPUX86State *env) */ set_float_2nan_prop_rule(float_2nan_prop_x87, &env->mmx_status); set_float_2nan_prop_rule(float_2nan_prop_x87, &env->sse_status); + /* + * Only SSE has multiply-add instructions. In the SDM Section 14.5.2 + * "Fused-Multiply-ADD (FMA) Numeric Behavior" the NaN handling is + * specified -- for 0 * inf + NaN the input NaN is selected, and if + * there are multiple input NaNs they are selected in the order a, b, c. + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->sse_status); + set_float_3nan_prop_rule(float_3nan_prop_abc, &env->sse_status); + /* Default NaN: sign bit set, most significant frac bit set */ + set_float_default_nan_pattern(0b11000000, &env->fp_status); + set_float_default_nan_pattern(0b11000000, &env->mmx_status); + set_float_default_nan_pattern(0b11000000, &env->sse_status); } static inline uint8_t save_exception_flags(CPUX86State *env) diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c index 21bc3b0..a83acf6 100644 --- a/target/loongarch/tcg/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -32,6 +32,14 @@ void restore_fp_status(CPULoongArchState *env) &env->fp_status); set_flush_to_zero(0, &env->fp_status); set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status); + /* + * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c' + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &env->fp_status); + /* Default NaN: sign bit clear, msb frac bit set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); } int ieee_ex_to_loongarch(int xcpt) @@ -353,8 +361,7 @@ uint64_t helper_fclass_s(CPULoongArchState *env, uint64_t fj) } else if (float32_is_zero_or_denormal(f)) { return sign ? 1 << 4 : 1 << 8; } else if (float32_is_any_nan(f)) { - float_status s = { }; /* for snan_bit_is_one */ - return float32_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; + return float32_is_quiet_nan(f, &env->fp_status) ? 1 << 1 : 1 << 0; } else { return sign ? 1 << 3 : 1 << 7; } @@ -372,8 +379,7 @@ uint64_t helper_fclass_d(CPULoongArchState *env, uint64_t fj) } else if (float64_is_zero_or_denormal(f)) { return sign ? 1 << 4 : 1 << 8; } else if (float64_is_any_nan(f)) { - float_status s = { }; /* for snan_bit_is_one */ - return float64_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; + return float64_is_quiet_nan(f, &env->fp_status) ? 1 << 1 : 1 << 0; } else { return sign ? 1 << 3 : 1 << 7; } diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 5fe3355..9de8ce6 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -76,7 +76,7 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) CPUState *cs = CPU(obj); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(obj); CPUM68KState *env = cpu_env(cs); - floatx80 nan = floatx80_default_nan(NULL); + floatx80 nan; int i; if (mcc->parent_phases.hold) { @@ -89,10 +89,6 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) #else cpu_m68k_set_sr(env, SR_S | SR_I); #endif - for (i = 0; i < 8; i++) { - env->fregs[i].d = nan; - } - cpu_m68k_set_fpcr(env, 0); /* * M68000 FAMILY PROGRAMMER'S REFERENCE MANUAL * 3.4 FLOATING-POINT INSTRUCTION DETAILS @@ -109,6 +105,14 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) * preceding paragraph for nonsignaling NaNs. */ set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); + /* Default NaN: sign bit clear, all frac bits set */ + set_float_default_nan_pattern(0b01111111, &env->fp_status); + + nan = floatx80_default_nan(&env->fp_status); + for (i = 0; i < 8; i++) { + env->fregs[i].d = nan; + } + cpu_m68k_set_fpcr(env, 0); env->fpsr = 0; /* TODO: We should set PC from the interrupt vector. */ diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index a605162..e3f4a18 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -615,15 +615,13 @@ void HELPER(frem)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) fp_rem = floatx80_rem(val1->d, val0->d, &env->fp_status); if (!floatx80_is_any_nan(fp_rem)) { - float_status fp_status = { }; + /* Use local temporary fp_status to set different rounding mode */ + float_status fp_status = env->fp_status; uint32_t quotient; int sign; /* Calculate quotient directly using round to nearest mode */ - set_float_2nan_prop_rule(float_2nan_prop_ab, &fp_status); set_float_rounding_mode(float_round_nearest_even, &fp_status); - set_floatx80_rounding_precision( - get_floatx80_rounding_precision(&env->fp_status), &fp_status); fp_quot.d = floatx80_div(val1->d, val0->d, &fp_status); sign = extractFloatx80Sign(fp_quot.d); diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 9bfc6ae..beefeb7 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -36,7 +36,8 @@ static int cf_fpu_gdb_get_reg(CPUState *cs, GByteArray *mem_buf, int n) CPUM68KState *env = &cpu->env; if (n < 8) { - float_status s = {}; + /* Use scratch float_status so any exceptions don't change CPU state */ + float_status s = env->fp_status; return gdb_get_reg64(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); } switch (n) { @@ -56,7 +57,8 @@ static int cf_fpu_gdb_set_reg(CPUState *cs, uint8_t *mem_buf, int n) CPUM68KState *env = &cpu->env; if (n < 8) { - float_status s = {}; + /* Use scratch float_status so any exceptions don't change CPU state */ + float_status s = env->fp_status; env->fregs[n].d = float64_to_floatx80(ldq_be_p(mem_buf), &s); return 8; } diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 710eb11..0e1e22d 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -207,6 +207,8 @@ static void mb_cpu_reset_hold(Object *obj, ResetType type) * this architecture. */ set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); + /* Default NaN: sign bit set, most significant frac bit set */ + set_float_default_nan_pattern(0b11000000, &env->fp_status); #if defined(CONFIG_USER_ONLY) /* start in user mode with interrupts enabled. */ diff --git a/target/mips/fpu_helper.h b/target/mips/fpu_helper.h index 7c3c789..6ad1e46 100644 --- a/target/mips/fpu_helper.h +++ b/target/mips/fpu_helper.h @@ -28,6 +28,8 @@ static inline void restore_flush_mode(CPUMIPSState *env) static inline void restore_snan_bit_mode(CPUMIPSState *env) { bool nan2008 = env->active_fpu.fcr31 & (1 << FCR31_NAN2008); + FloatInfZeroNaNRule izn_rule; + Float3NaNPropRule nan3_rule; /* * With nan2008, SNaNs are silenced in the usual way. @@ -35,6 +37,24 @@ static inline void restore_snan_bit_mode(CPUMIPSState *env) */ set_snan_bit_is_one(!nan2008, &env->active_fpu.fp_status); set_default_nan_mode(!nan2008, &env->active_fpu.fp_status); + /* + * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) + * case sets InvalidOp and returns the default NaN. + * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c'. + */ + izn_rule = nan2008 ? float_infzeronan_dnan_never : float_infzeronan_dnan_always; + set_float_infzeronan_rule(izn_rule, &env->active_fpu.fp_status); + nan3_rule = nan2008 ? float_3nan_prop_s_cab : float_3nan_prop_s_abc; + set_float_3nan_prop_rule(nan3_rule, &env->active_fpu.fp_status); + /* + * With nan2008, the default NaN value has the sign bit clear and the + * frac msb set; with the older mode, the sign bit is clear, and all + * frac bits except the msb are set. + */ + set_float_default_nan_pattern(nan2008 ? 0b01000000 : 0b00111111, + &env->active_fpu.fp_status); + } static inline void restore_fp_status(CPUMIPSState *env) diff --git a/target/mips/msa.c b/target/mips/msa.c index 9dffc42..fc77bfc 100644 --- a/target/mips/msa.c +++ b/target/mips/msa.c @@ -66,6 +66,9 @@ void msa_reset(CPUMIPSState *env) set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->active_tc.msa_fp_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, + &env->active_tc.msa_fp_status); + /* clear float_status exception flags */ set_float_exception_flags(0, &env->active_tc.msa_fp_status); @@ -74,4 +77,11 @@ void msa_reset(CPUMIPSState *env) /* set proper signanling bit meaning ("1" means "quiet") */ set_snan_bit_is_one(0, &env->active_tc.msa_fp_status); + + /* Inf * 0 + NaN returns the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, + &env->active_tc.msa_fp_status); + /* Default NaN: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, + &env->active_tc.msa_fp_status); } diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index b96561d..3ccf85e 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -111,6 +111,8 @@ static void openrisc_cpu_reset_hold(Object *obj, ResetType type) */ set_float_2nan_prop_rule(float_2nan_prop_x87, &cpu->env.fp_status); + /* Default NaN: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &cpu->env.fp_status); #ifndef CONFIG_USER_ONLY cpu->env.picmr = 0x00000000; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index efcb80d..1253dbf 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7270,6 +7270,25 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) */ set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); set_float_2nan_prop_rule(float_2nan_prop_ab, &env->vec_status); + /* + * NaN propagation for fused multiply-add: + * if fRA is a NaN return it; otherwise if fRB is a NaN return it; + * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB + * whereas QEMU labels the operands as (a * b) + c. + */ + set_float_3nan_prop_rule(float_3nan_prop_acb, &env->fp_status); + set_float_3nan_prop_rule(float_3nan_prop_acb, &env->vec_status); + /* + * For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer + * to return an input NaN if we have one (ie c) rather than generating + * a default NaN + */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->vec_status); + + /* Default NaN: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); + set_float_default_nan_pattern(0b01000000, &env->vec_status); for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { ppc_spr_t *spr = &env->spr_cb[i]; diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 230466a..d93cfed 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -155,8 +155,7 @@ void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \ } else if (tp##_is_infinity(arg)) { \ fprf = neg ? 0x09 << FPSCR_FPRF : 0x05 << FPSCR_FPRF; \ } else { \ - float_status dummy = { }; /* snan_bit_is_one = 0 */ \ - if (tp##_is_signaling_nan(arg, &dummy)) { \ + if (tp##_is_signaling_nan(arg, &env->fp_status)) { \ fprf = 0x00 << FPSCR_FPRF; \ } else { \ fprf = 0x11 << FPSCR_FPRF; \ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index f219f0c..80b0995 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1022,6 +1022,8 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) cs->exception_index = RISCV_EXCP_NONE; env->load_res = -1; set_default_nan_mode(1, &env->fp_status); + /* Default NaN value: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); env->vill = true; #ifndef CONFIG_USER_ONLY diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 65a74ce..69ec0bc 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -100,6 +100,8 @@ static void rx_cpu_reset_hold(Object *obj, ResetType type) * then prefer dest over source", which is float_2nan_prop_s_ab. */ set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); + /* Default NaN value: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); } static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 514c70f..adb2750 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -206,6 +206,11 @@ static void s390_cpu_reset_hold(Object *obj, ResetType type) set_float_detect_tininess(float_tininess_before_rounding, &env->fpu_status); set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fpu_status); + set_float_3nan_prop_rule(float_3nan_prop_s_abc, &env->fpu_status); + set_float_infzeronan_rule(float_infzeronan_dnan_always, + &env->fpu_status); + /* Default NaN value: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &env->fpu_status); /* fall through */ case RESET_TYPE_S390_CPU_NORMAL: env->psw.mask &= ~PSW_MASK_RI; diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 8f07261..d5008859 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -127,6 +127,8 @@ static void superh_cpu_reset_hold(Object *obj, ResetType type) set_flush_to_zero(1, &env->fp_status); #endif set_default_nan_mode(1, &env->fp_status); + /* sign bit clear, set all frac bits other than msb */ + set_float_default_nan_pattern(0b00111111, &env->fp_status); } static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index dd7af86..6b66ecb 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -814,6 +814,12 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) * the CPU state struct so it won't get zeroed on reset. */ set_float_2nan_prop_rule(float_2nan_prop_s_ba, &env->fp_status); + /* For fused-multiply add, prefer SNaN over QNaN, then C->B->A */ + set_float_3nan_prop_rule(float_3nan_prop_s_cba, &env->fp_status); + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); + /* Default NaN value: sign bit clear, all frac bits set */ + set_float_default_nan_pattern(0b01111111, &env->fp_status); cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index 6f9ccc0..236d27b 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -490,13 +490,13 @@ uint32_t helper_fcmpeq(CPUSPARCState *env, Int128 src1, Int128 src2) return finish_fcmp(env, r, GETPC()); } -uint32_t helper_flcmps(float32 src1, float32 src2) +uint32_t helper_flcmps(CPUSPARCState *env, float32 src1, float32 src2) { /* * FLCMP never raises an exception nor modifies any FSR fields. * Perform the comparison with a dummy fp environment. */ - float_status discard = { }; + float_status discard = env->fp_status; FloatRelation r; set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard); @@ -518,9 +518,9 @@ uint32_t helper_flcmps(float32 src1, float32 src2) g_assert_not_reached(); } -uint32_t helper_flcmpd(float64 src1, float64 src2) +uint32_t helper_flcmpd(CPUSPARCState *env, float64 src1, float64 src2) { - float_status discard = { }; + float_status discard = env->fp_status; FloatRelation r; set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard); diff --git a/target/sparc/helper.h b/target/sparc/helper.h index 134e519..1ae3f0c 100644 --- a/target/sparc/helper.h +++ b/target/sparc/helper.h @@ -51,8 +51,8 @@ DEF_HELPER_FLAGS_3(fcmpd, TCG_CALL_NO_WG, i32, env, f64, f64) DEF_HELPER_FLAGS_3(fcmped, TCG_CALL_NO_WG, i32, env, f64, f64) DEF_HELPER_FLAGS_3(fcmpq, TCG_CALL_NO_WG, i32, env, i128, i128) DEF_HELPER_FLAGS_3(fcmpeq, TCG_CALL_NO_WG, i32, env, i128, i128) -DEF_HELPER_FLAGS_2(flcmps, TCG_CALL_NO_RWG_SE, i32, f32, f32) -DEF_HELPER_FLAGS_2(flcmpd, TCG_CALL_NO_RWG_SE, i32, f64, f64) +DEF_HELPER_FLAGS_3(flcmps, TCG_CALL_NO_RWG_SE, i32, env, f32, f32) +DEF_HELPER_FLAGS_3(flcmpd, TCG_CALL_NO_RWG_SE, i32, env, f64, f64) DEF_HELPER_2(raise_exception, noreturn, env, int) DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_WG, f64, env, f64, f64) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index cdd0a95..322319a 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -5584,7 +5584,7 @@ static bool trans_FLCMPs(DisasContext *dc, arg_FLCMPs *a) src1 = gen_load_fpr_F(dc, a->rs1); src2 = gen_load_fpr_F(dc, a->rs2); - gen_helper_flcmps(cpu_fcc[a->cc], src1, src2); + gen_helper_flcmps(cpu_fcc[a->cc], tcg_env, src1, src2); return advance_pc(dc); } @@ -5601,7 +5601,7 @@ static bool trans_FLCMPd(DisasContext *dc, arg_FLCMPd *a) src1 = gen_load_fpr_D(dc, a->rs1); src2 = gen_load_fpr_D(dc, a->rs2); - gen_helper_flcmpd(cpu_fcc[a->cc], src1, src2); + gen_helper_flcmpd(cpu_fcc[a->cc], tcg_env, src1, src2); return advance_pc(dc); } diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 7014255..e8b0ec5 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -117,6 +117,8 @@ void fpu_set_state(CPUTriCoreState *env) set_flush_to_zero(1, &env->fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); set_default_nan_mode(1, &env->fp_status); + /* Default NaN pattern: sign bit clear, frac msb set */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); } uint32_t psw_read(CPUTriCoreState *env) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 6f9039a..0d4d79b 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -133,7 +133,11 @@ static void xtensa_cpu_reset_hold(Object *obj, ResetType type) reset_mmu(env); cs->halted = env->runstall; #endif + /* For inf * 0 + NaN, return the input NaN */ + set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); set_no_signaling_nans(!dfpu, &env->fp_status); + /* Default NaN value: sign bit clear, set frac msb */ + set_float_default_nan_pattern(0b01000000, &env->fp_status); xtensa_use_first_nan(env, !dfpu); } diff --git a/target/xtensa/fpu_helper.c b/target/xtensa/fpu_helper.c index f2d212d..53fc7cf 100644 --- a/target/xtensa/fpu_helper.c +++ b/target/xtensa/fpu_helper.c @@ -59,9 +59,10 @@ static const struct { void xtensa_use_first_nan(CPUXtensaState *env, bool use_first) { - set_use_first_nan(use_first, &env->fp_status); set_float_2nan_prop_rule(use_first ? float_2nan_prop_ab : float_2nan_prop_ba, &env->fp_status); + set_float_3nan_prop_rule(use_first ? float_3nan_prop_abc : float_3nan_prop_cba, + &env->fp_status); } void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v) diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index 75c07d5..eacb39b 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -488,7 +488,14 @@ static void run_bench(void) { bench_func_t f; + /* + * These implementation-defined choices for various things IEEE + * doesn't specify match those used by the Arm architecture. + */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &soft_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &soft_status); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &soft_status); + set_float_default_nan_pattern(0b01000000, &soft_status); f = bench_funcs[operation][precision]; g_assert(f); diff --git a/tests/fp/fp-test-log2.c b/tests/fp/fp-test-log2.c index de702c4..79f619c 100644 --- a/tests/fp/fp-test-log2.c +++ b/tests/fp/fp-test-log2.c @@ -71,6 +71,7 @@ int main(int ac, char **av) int i; set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + set_float_default_nan_pattern(0b01000000, &qsf); set_float_rounding_mode(float_round_nearest_even, &qsf); test.d = 0.0; diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 5f6f25c..c619e5d 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -935,7 +935,14 @@ void run_test(void) { unsigned int i; + /* + * These implementation-defined choices for various things IEEE + * doesn't specify match those used by the Arm architecture. + */ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &qsf); + set_float_default_nan_pattern(0b01000000, &qsf); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &qsf); genCases_setLevel(test_level); verCases_maxErrorCount = n_max_errors; |