Mercurial > flash_v2
diff packages/hal/mips/arch/current/src/mipsfp.c @ 26:4741a3c3496a ecos-sw-1999-07-23
Merge from eCos master repository on 1999-07-23-05:47:06-BST
| author | jlarmour |
|---|---|
| date | Fri, 23 Jul 1999 15:20:15 +0000 |
| parents | |
| children | 641b639be825 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/hal/mips/arch/current/src/mipsfp.c @@ -0,0 +1,529 @@ +//========================================================================== +// +// mipsfp.c +// +// HAL miscellaneous functions +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Cygnus eCos Public License +// Version 1.0 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://sourceware.cygnus.com/ecos +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Cygnus Operating System, released +// September 30, 1998. +// +// The Initial Developer of the Original Code is Cygnus. Portions created +// by Cygnus are Copyright (C) 1998,1999 Cygnus Solutions. All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): jlarmour +// Contributors: jlarmour +// Date: 1999-07-13 +// Purpose: Emulate unimplemented FP operations on MIPS architectures +// Description: This catches the unimplemented operation excetion only, +// and if possible deals with it so processing can continue +// as if the MIPS had a proper IEEE FPU +// +// +//####DESCRIPTIONEND#### +// +//========================================================================*/ + +// CONFIGURATION + +#include <pkgconf/hal.h> + +#ifdef CYGHWR_HAL_MIPS_FPU + +// INCLUDES + +#include <cyg/infra/cyg_type.h> // Standard eCos types +#include <cyg/infra/cyg_ass.h> // Standard eCos assertion support +#include <cyg/infra/cyg_trac.h> // Standard eCos tracing support +#include <cyg/hal/hal_intr.h> // HAL interrupt vectors +#include <cyg/hal/hal_arch.h> // Architecture types such as + // HAL_SavedRegisters +#define CYGARC_HAL_COMMON_EXPORT_CPU_MACROS +#include <cyg/hal/mips-regs.h> // MIPS register and bitmask definitions + +// TYPES + +// The following types were taken from <sys/ieeefp.h> from libm. + +#if (CYG_BYTEORDER == CYG_MSBFIRST) // Big endian + +// Note: there do not seem to be any current machines which are Big Endian but +// have a mixed up double layout. + +typedef union +{ + cyg_int32 asi32[2]; + + cyg_int64 asi64; + + double value; + + struct + { + unsigned int sign : 1; + unsigned int exponent: 11; + unsigned int fraction0:4; + unsigned int fraction1:16; + unsigned int fraction2:16; + unsigned int fraction3:16; + + } number; + + struct + { + unsigned int sign : 1; + unsigned int exponent: 11; + unsigned int quiet:1; + unsigned int function0:3; + unsigned int function1:16; + unsigned int function2:16; + unsigned int function3:16; + } nan; + + struct + { + cyg_uint32 msw; + cyg_uint32 lsw; + } parts; + + +} Cyg_libm_ieee_double_shape_type; + + +typedef union +{ + cyg_int32 asi32; + + float value; + + struct + { + unsigned int sign : 1; + unsigned int exponent: 8; + unsigned int fraction0: 7; + unsigned int fraction1: 16; + } number; + + struct + { + unsigned int sign:1; + unsigned int exponent:8; + unsigned int quiet:1; + unsigned int function0:6; + unsigned int function1:16; + } nan; + +} Cyg_libm_ieee_float_shape_type; + + +#else // Little endian + +typedef union +{ + cyg_int32 asi32[2]; + + cyg_int64 asi64; + + double value; + + struct + { +#if (CYG_DOUBLE_BYTEORDER == CYG_MSBFIRST) // Big endian + unsigned int fraction1:16; + unsigned int fraction0: 4; + unsigned int exponent :11; + unsigned int sign : 1; + unsigned int fraction3:16; + unsigned int fraction2:16; +#else + unsigned int fraction3:16; + unsigned int fraction2:16; + unsigned int fraction1:16; + unsigned int fraction0: 4; + unsigned int exponent :11; + unsigned int sign : 1; +#endif + } number; + + struct + { +#if (CYG_DOUBLE_BYTEORDER == CYG_MSBFIRST) // Big endian + unsigned int function1:16; + unsigned int function0:3; + unsigned int quiet:1; + unsigned int exponent: 11; + unsigned int sign : 1; + unsigned int function3:16; + unsigned int function2:16; +#else + unsigned int function3:16; + unsigned int function2:16; + unsigned int function1:16; + unsigned int function0:3; + unsigned int quiet:1; + unsigned int exponent: 11; + unsigned int sign : 1; +#endif + } nan; + + struct + { +#if (CYG_DOUBLE_BYTEORDER == CYG_MSBFIRST) // Big endian + cyg_uint32 msw; + cyg_uint32 lsw; +#else + cyg_uint32 lsw; + cyg_uint32 msw; +#endif + } parts; + +} Cyg_libm_ieee_double_shape_type; + + +typedef union +{ + cyg_int32 asi32; + + float value; + + struct + { + unsigned int fraction0: 7; + unsigned int fraction1: 16; + unsigned int exponent: 8; + unsigned int sign : 1; + } number; + + struct + { + unsigned int function1:16; + unsigned int function0:6; + unsigned int quiet:1; + unsigned int exponent:8; + unsigned int sign:1; + } nan; + +} Cyg_libm_ieee_float_shape_type; + +#endif // little-endian + +typedef enum { + ADD_INSN=0, // 0 + SUB_INSN, + MUL_INSN, + DIV_INSN, + SQRT_INSN, + ABS_INSN, // 5 + MOV_INSN, + NEG_INSN, + ROUNDL_INSN, + TRUNCL_INSN, + CEILL_INSN, // 10 + FLOORL_INSN, + ROUNDW_INSN, + TRUNCW_INSN, + CEILW_INSN, + FLOORW_INSN, // 15 + // ... + CVTS_INSN=32, + CVTD_INSN, + // ... + CVTW_INSN=36, + CVTL_INSN, + // ... + // + // 48-63 are floating point compare - treated separately + +} fp_operation; + +typedef enum { + S_FORMAT=16, + D_FORMAT=17, + W_FORMAT=20, + L_FORMAT=21 +} fp_format; + +// FUNCTIONS + +#define issubnormal(_x_) ((_x_)->number.exponent == 0) + +// This function returns non-zero if the exception has been handled +// successfully. + +// FIXME: Arguably we should raise underflow exceptions in some of the cases +// below e.g. sqrt(subnormal). And perhaps we should round appropriately to +// +/- 2^^Emin if round to +/- infinity is enabled, as per the FS bit. Not sure. + +externC cyg_uint8 +cyg_hal_mips_process_fpe( HAL_SavedRegisters *regs ) +{ + CYG_WORD insn; + CYG_HAL_FPU_REG *srcreg1, *srcreg2, *dstreg; + cyg_uint8 handled=0; + fp_format format; + cyg_uint8 fp64bit=0; // true if format is 64bit, false if 32bit + cyg_uint8 fixedpoint=0; // true if format is fixed point, false if + // floating point + cyg_uint8 computational_insn=1; // computational FP instruction + cyg_bool delay_slot; // did it happen in a delay slot + + CYG_CHECK_DATA_PTR( regs, + "cyg_hal_mips_process_fpe() called with invalid regs ptr"); + + CYG_PRECONDITION( (regs->vector>>2) == CYGNUM_HAL_VECTOR_FPE, + "Asked to process non-FPE exception"); + + CYG_REPORT_FUNCNAMETYPE("cyg_hal_mips_process_fpe", "returning %d"); + + // First of all, we only handle the unimplemented operation exception + // here, so if we don't have that, we just exit + if ((regs->fcr31 & FCR31_CAUSE_E) == 0) { + CYG_REPORT_RETVAL(0); + return 0; + } + + // Get the contents of the instruction that caused the exception. This + // may have been in a branch delay slot however, so we have to check + // the BD bit in the cause register first. + if (regs->cause & CAUSE_BD) { + insn = *(((CYG_WORD *) regs->pc) + 1); + delay_slot = true; + } else { + insn = *(CYG_WORD *) regs->pc; + delay_slot = false; + } + + CYG_TRACE2(true, "exception at pc %08x containing %08x", regs->pc, insn); + + CYG_ASSERT( (insn>>26) == 0x11, + "Instruction at pc doesn't have expected opcode COP1"); + + // Determine the format + format = (insn >> 21) & 0x1f; + + switch (format) + { + case S_FORMAT: + break; + case D_FORMAT: + fp64bit++; + break; + case W_FORMAT: + fixedpoint++; + break; + case L_FORMAT: + fixedpoint++; + fp64bit++; + break; + default: + computational_insn=0; + break; + } // switch + + // This module only emulates computational floating point instructions + if (computational_insn && !fixedpoint) { + + // Decode the registers used + dstreg = ®s->f[ (insn >> 6) & 0x1f ]; + srcreg1 = ®s->f[ (insn >> 11) & 0x1f ]; + srcreg2 = ®s->f[ (insn >> 16) & 0x1f ]; + + // Determine the operation requested + switch (insn & 0x3f) + { + case ADD_INSN: + case SUB_INSN: + case MUL_INSN: + case DIV_INSN: + + if (fp64bit) { + Cyg_libm_ieee_double_shape_type *s1, *s2; + + s1 = (Cyg_libm_ieee_double_shape_type *)srcreg1; + s2 = (Cyg_libm_ieee_double_shape_type *)srcreg2; + + if (issubnormal(s1)) { // flush to 0 and restart + s1->value=0.0; + handled++; + } + + // We could try flushing both to 0 at the same time, but + // that's inadvisable if both numbers are very small. + // Particularly if this is DIV_INSN, when we could therefore + // get 0/0 even when the program explicitly checked for + // denominator != 0. That's also why we check s1 first. + + else if (issubnormal(s2)) { // flush to 0 and restart + s2->value=0.0; + handled++; + } + } else { // 32-bit + Cyg_libm_ieee_float_shape_type *s1, *s2; + + s1 = (Cyg_libm_ieee_float_shape_type *)srcreg1; + s2 = (Cyg_libm_ieee_float_shape_type *)srcreg2; + + if (issubnormal(s1)) { // flush to 0 and restart + s1->value=0.0; + handled++; + } + else if (issubnormal(s2)) { // flush to 0 and restart + s2->value=0.0; + handled++; + } + } + break; + + case SQRT_INSN: + if (fp64bit) { + Cyg_libm_ieee_double_shape_type *d, *s; + + d = (Cyg_libm_ieee_double_shape_type *)dstreg; + s = (Cyg_libm_ieee_double_shape_type *)srcreg1; + + if (issubnormal(s)) { // Sqrt of something tiny is 0 + // if this is a delay slot, we can't restart properly + // so clear the source register instead + if (delay_slot) + s->value=0.0; + else { + d->value=0.0; + regs->pc += 4; // We've dealt with this so move on + } + handled++; + } + + } else { // 32-bit + Cyg_libm_ieee_float_shape_type *d, *s; + + d = (Cyg_libm_ieee_float_shape_type *)dstreg; + s = (Cyg_libm_ieee_float_shape_type *)srcreg1; + + if (issubnormal(s)) { // Sqrt of something tiny is 0 + // if this is a delay slot, we can't restart properly + // so clear the source register instead + if (delay_slot) + s->value=0.0; + else { + d->value=0.0; + regs->pc += 4; // We've dealt with this so move on + } + handled++; + } + } + break; + + case ABS_INSN: + // We may as well do this right if we can + if (fp64bit) { + Cyg_libm_ieee_double_shape_type *d, *s; + + d = (Cyg_libm_ieee_double_shape_type *)dstreg; + s = (Cyg_libm_ieee_double_shape_type *)srcreg1; + + // if this is a delay slot, we can't restart properly + // so clear the source register instead + if (delay_slot) { + s->value=0.0; + } else { + d->asi64 = s->asi64; + d->number.sign = 0; + regs->pc += 4; + } + } else { // 32-bit + Cyg_libm_ieee_float_shape_type *d, *s; + + d = (Cyg_libm_ieee_float_shape_type *)dstreg; + s = (Cyg_libm_ieee_float_shape_type *)srcreg1; + + // if this is a delay slot, we can't restart properly + // so clear the source register instead + if (delay_slot) { + s->value=0.0; + } else { + d->asi32 = s->asi32; + d->number.sign = 0; + regs->pc += 4; + } + } + handled++; + break; + + case NEG_INSN: + // We may as well do this right if we can + if (fp64bit) { + Cyg_libm_ieee_double_shape_type *d, *s; + + d = (Cyg_libm_ieee_double_shape_type *)dstreg; + s = (Cyg_libm_ieee_double_shape_type *)srcreg1; + + // if this is a delay slot, we can't restart properly + // so clear the source register instead + if (delay_slot) { + s->value=0.0; + } else { + d->asi64 = s->asi64; + d->number.sign = s->number.sign ? 0 : 1; + regs->pc += 4; + } + } else { // 32-bit + Cyg_libm_ieee_float_shape_type *d, *s; + + d = (Cyg_libm_ieee_float_shape_type *)dstreg; + s = (Cyg_libm_ieee_float_shape_type *)srcreg1; + + // if this is a delay slot, we can't restart properly + // so clear the source register instead + if (delay_slot) { + s->value=0.0; + } else { + d->asi32 = s->asi32; + d->number.sign = s->number.sign ? 0 : 1; + regs->pc += 4; + } + } + handled++; + break; + + default: + break; + } // switch + + // As well as all the other opcodes in the enum, there are also all + // the floating point compare operations between 48 and 63 + } + + if (handled) { + // We must clear the cause and flag bits before restoring FPCR31 + regs->fcr31 &= ~(FCR31_CAUSE_E | FCR31_CAUSE_V | FCR31_CAUSE_Z | + FCR31_CAUSE_O | FCR31_CAUSE_U | FCR31_CAUSE_I | + FCR31_FLAGS_V | FCR31_FLAGS_Z | + FCR31_FLAGS_O | FCR31_FLAGS_U | FCR31_FLAGS_I); + + } + + CYG_REPORT_RETVAL(handled); + return handled; +} // cyg_hal_mips_process_fpe() + +#endif // ifdef CYGHWR_HAL_MIPS_FPU + +// EOF mipsfp.c
