git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1321 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
877 lines
24 KiB
C
877 lines
24 KiB
C
/*
|
|
* Copyright (c) 2005-2011 Imperas Software Ltd., www.imperas.com
|
|
*
|
|
* YOUR ACCESS TO THE INFORMATION IN THIS MODEL IS CONDITIONAL
|
|
* UPON YOUR ACCEPTANCE THAT YOU WILL NOT USE OR PERMIT OTHERS
|
|
* TO USE THE INFORMATION FOR THE PURPOSES OF DETERMINING WHETHER
|
|
* IMPLEMENTATIONS OF THE ARM ARCHITECTURE INFRINGE ANY THIRD
|
|
* PARTY PATENTS.
|
|
*
|
|
* THE LICENSE BELOW EXTENDS ONLY TO USE OF THE SOFTWARE FOR
|
|
* MODELING PURPOSES AND SHALL NOT BE CONSTRUED AS GRANTING
|
|
* A LICENSE TO CREATE A HARDWARE IMPLEMENTATION OF THE
|
|
* FUNCTIONALITY OF THE SOFTWARE LICENSED HEREUNDER.
|
|
* YOU MAY USE THE SOFTWARE SUBJECT TO THE LICENSE TERMS BELOW
|
|
* PROVIDED THAT YOU ENSURE THAT THIS NOTICE IS REPLICATED UNMODIFIED
|
|
* AND IN ITS ENTIRETY IN ALL DISTRIBUTIONS OF THE SOFTWARE,
|
|
* MODIFIED OR UNMODIFIED, IN SOURCE CODE OR IN BINARY FORM.
|
|
*
|
|
* Licensed under an Imperas Modfied Apache License, Version 2.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://www.ovpworld.org/licenses/OVP_MODIFIED_1.0_APACHE_OPEN_SOURCE_LICENSE_2.0.pdf
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
* either express or implied.
|
|
*
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
#include <math.h>
|
|
|
|
// VMI header files
|
|
#include "vmi/vmiRt.h"
|
|
#include "vmi/vmiMessage.h"
|
|
|
|
// model header files
|
|
#include "armFPConstants.h"
|
|
#include "armMessage.h"
|
|
#include "armVFP.h"
|
|
#include "armSysRegisters.h"
|
|
#include "armStructure.h"
|
|
#include "armUtils.h"
|
|
|
|
//
|
|
// Prefix for messages from this module
|
|
//
|
|
#define CPU_PREFIX "ARMM_VFP"
|
|
|
|
//
|
|
// Return current program counter
|
|
//
|
|
inline static Uns32 getPC(armP arm) {
|
|
return vmirtGetPC((vmiProcessorP)arm);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// FLOATING POINT CONTROL PREDICATES
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Return the current rounding control
|
|
//
|
|
static vmiFPRC getRoundingControl(armP arm) {
|
|
|
|
// map ARM rounding mode to VMI rounding mode
|
|
static const vmiFPRC modeMap[] = {
|
|
[0] = vmi_FPR_NEAREST,
|
|
[1] = vmi_FPR_POS_INF,
|
|
[2] = vmi_FPR_NEG_INF,
|
|
[3] = vmi_FPR_ZERO
|
|
};
|
|
|
|
if(SCS_USE_CPUID(arm) && !SCS_FIELD(arm, MVFR0, VFP_RoundingModes)) {
|
|
// if rounding modes are not supported, use vmi_FPR_NEAREST
|
|
return vmi_FPR_NEAREST;
|
|
} else {
|
|
// return the mapped rounding mode
|
|
return modeMap[FPSCR_FIELD(arm, RMode)];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Should the default NaN be returned in any calculation that produces a NaN?
|
|
//
|
|
inline static Bool returnDefaultNaN(armP arm) {
|
|
return (
|
|
// in VFP mode, return the default NaN if FPSCR.DN is set
|
|
FPSCR_FIELD(arm, DN) ||
|
|
// MVFR1.DefaultNaNMode=0 indicates hardware supports only the default
|
|
// NaN mode
|
|
(SCS_USE_CPUID(arm) && !SCS_FIELD(arm, MVFR1, DefaultNaNMode))
|
|
);
|
|
}
|
|
|
|
//
|
|
// Is flush-to-zero mode enabled?
|
|
//
|
|
inline static Bool inFTZMode(armP arm) {
|
|
return (
|
|
// flush-to-zero is enabled if FPSCR.FZ is set
|
|
FPSCR_FIELD(arm, FZ) ||
|
|
// MVFR1.FlushToZeroMode=0 indicates hardware supports only
|
|
// flush-to-zero mode
|
|
(SCS_USE_CPUID(arm) && !SCS_FIELD(arm, MVFR1, FlushToZeroMode))
|
|
);
|
|
}
|
|
|
|
//
|
|
// Is alternative half precision mode enabled?
|
|
//
|
|
inline static Bool inAHPMode(armP arm) {
|
|
return FPSCR_FIELD(arm, AHP);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// FLOATING REGISTER ACCESS
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Set control word
|
|
//
|
|
inline static void setControlWord(armP arm, armFPCW new) {
|
|
if(arm->currentCW.u32 != new.u32) {
|
|
arm->currentCW = new;
|
|
vmirtSetFPControlWord((vmiProcessorP)arm, new.cw);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update VFP control word to make it consistent with the FPSCR
|
|
//
|
|
static void updateVFPControlWord(armP arm) {
|
|
|
|
// Mask off all exceptions */
|
|
armFPCW newFPCW = {.cw.IM=1, .cw.DM=1, .cw.ZM=1, .cw.OM=1, .cw.UM=1, .cw.PM=1};
|
|
|
|
// set rounding control, denormals-are-zero and flush-to-zero
|
|
newFPCW.cw.RC = getRoundingControl(arm);
|
|
newFPCW.cw.DAZ = newFPCW.cw.FZ = inFTZMode(arm);
|
|
|
|
// Set the new control word
|
|
setControlWord(arm, newFPCW);
|
|
|
|
}
|
|
|
|
//
|
|
// Write VFP FPSCR register
|
|
//
|
|
void armWriteFPSCR(armP arm, Uns32 newValue, Uns32 writeMask) {
|
|
|
|
Uns32 oldValue = FPSCR_REG(arm);
|
|
|
|
// update raw register
|
|
FPSCR_REG(arm) = ((oldValue & ~writeMask) | (newValue & writeMask));
|
|
|
|
// set denormal input sticky flag
|
|
arm->denormalInput = FPSCR_FIELD(arm, IDC);
|
|
|
|
// set sticky flags
|
|
vmiFPFlags sticky = {0};
|
|
sticky.f.I = FPSCR_FIELD(arm, IOC); // invalid operation flag
|
|
sticky.f.Z = FPSCR_FIELD(arm, DZC); // divide-by-zero flag
|
|
sticky.f.O = FPSCR_FIELD(arm, OFC); // overflow flag
|
|
sticky.f.U = FPSCR_FIELD(arm, UFC); // underflow flag
|
|
sticky.f.P = FPSCR_FIELD(arm, IXC); // inexact flag
|
|
arm->sdfpSticky = sticky.bits;
|
|
|
|
// set comparison flags
|
|
arm->sdfpAFlags.NF = (FPSCR_FIELD(arm, N)) ? 1 : 0;
|
|
arm->sdfpAFlags.ZF = (FPSCR_FIELD(arm, Z)) ? 1 : 0;
|
|
arm->sdfpAFlags.CF = (FPSCR_FIELD(arm, C)) ? 1 : 0;
|
|
arm->sdfpAFlags.VF = (FPSCR_FIELD(arm, V)) ? 1 : 0;
|
|
|
|
// update simulator floating point control word
|
|
updateVFPControlWord(arm);
|
|
|
|
}
|
|
|
|
//
|
|
// Read VFP FPSCR register
|
|
//
|
|
Uns32 armReadFPSCR(armP arm) {
|
|
|
|
// compose denormal input flag
|
|
FPSCR_FIELD(arm, IDC) = arm->denormalInput;
|
|
|
|
// compose remaining sticky flags
|
|
vmiFPFlags sticky = {arm->sdfpSticky};
|
|
FPSCR_FIELD(arm, IOC) = sticky.f.I; // invalid operation flag
|
|
FPSCR_FIELD(arm, DZC) = sticky.f.Z; // divide-by-zero flag
|
|
FPSCR_FIELD(arm, OFC) = sticky.f.O; // overflow flag
|
|
FPSCR_FIELD(arm, UFC) = sticky.f.U; // underflow flag
|
|
FPSCR_FIELD(arm, IXC) = sticky.f.P; // inexact flag
|
|
|
|
// compose comparison flags
|
|
FPSCR_FIELD(arm, N) = (arm->sdfpAFlags.NF) ? 1 : 0;
|
|
FPSCR_FIELD(arm, Z) = (arm->sdfpAFlags.ZF) ? 1 : 0;
|
|
FPSCR_FIELD(arm, C) = (arm->sdfpAFlags.CF) ? 1 : 0;
|
|
FPSCR_FIELD(arm, V) = (arm->sdfpAFlags.VF) ? 1 : 0;
|
|
|
|
// return composed value
|
|
return FPSCR_REG(arm);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// EXCEPTION FLAG UTILITIES
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Validate that an exception is disabled
|
|
//
|
|
inline static void validateFTZ(armP arm) {
|
|
VMI_ASSERT(
|
|
inFTZMode(arm),
|
|
"expected enabled flush-to-zero mode"
|
|
);
|
|
}
|
|
|
|
//
|
|
// Set the denormal sticky bit (when flush-to-zero mode is enabled)
|
|
//
|
|
static void setDenormalStickyFTZ(armP arm) {
|
|
validateFTZ(arm);
|
|
arm->denormalInput = 1;
|
|
}
|
|
|
|
//
|
|
// Set the underflow sticky bit (when flush-to-zero mode is enabled)
|
|
//
|
|
static void setUnderflowStickyFTZ(armP arm) {
|
|
validateFTZ(arm);
|
|
const static vmiFPFlags underflow = {f:{U:1}};
|
|
arm->sdfpSticky |= underflow.bits;
|
|
}
|
|
|
|
//
|
|
// Raise the denormal exception
|
|
//
|
|
static void raiseDenormal(armP arm) {
|
|
arm->denormalInput = 1;
|
|
}
|
|
|
|
//
|
|
// Raise the underflow exception
|
|
//
|
|
static void raiseUnderflow(armP arm) {
|
|
const static vmiFPFlags underflow = {f:{U:1}};
|
|
arm->sdfpSticky |= underflow.bits;
|
|
}
|
|
|
|
//
|
|
// Raise the overflow exception
|
|
//
|
|
static void raiseOverflow(armP arm) {
|
|
const static vmiFPFlags overflow = {f:{O:1}};
|
|
arm->sdfpSticky |= overflow.bits;
|
|
}
|
|
|
|
//
|
|
// Raise the invalid operation exception
|
|
//
|
|
static void raiseInvalidOperation(armP arm) {
|
|
const static vmiFPFlags invalidOperation = {f:{I:1}};
|
|
arm->sdfpSticky |= invalidOperation.bits;
|
|
}
|
|
|
|
//
|
|
// Raise the inexact exception
|
|
//
|
|
static void raiseInexact(armP arm) {
|
|
const static vmiFPFlags inexact = {f:{P:1}};
|
|
arm->sdfpSticky |= inexact.bits;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// HALF-PRECISION OPERATIONS
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Convert from half-precision to single-precision
|
|
//
|
|
Uns32 armFPHalfToSingle(armP arm, Uns16 half) {
|
|
|
|
Int32 exponent = ARM_FP16_EXPONENT(half);
|
|
Uns32 fraction = ARM_FP16_FRACTION(half);
|
|
Bool sign = ARM_FP16_SIGN(half);
|
|
Bool expAll1 = (exponent==ARM_FP16_EXP_ONES);
|
|
Bool isZero = (!exponent && !fraction);
|
|
Bool isInfinity = (expAll1 && !fraction);
|
|
Bool isNaN = (expAll1 && !isInfinity);
|
|
Bool useAHP = inAHPMode(arm);
|
|
|
|
if(isInfinity && !useAHP) {
|
|
|
|
// infinities require exponent correction
|
|
exponent = ARM_FP32_EXPONENT(ARM_FP32_PLUS_INFINITY);
|
|
fraction = ARM_FP32_FRACTION(ARM_FP32_PLUS_INFINITY);
|
|
|
|
} else if(isZero) {
|
|
|
|
// zero values require no special processing
|
|
|
|
} else if(isNaN && !useAHP) {
|
|
|
|
// argument is an SNaN if MSB of fraction is clear
|
|
Bool isSNaN = !(fraction & (1 << (ARM_FP16_EXP_SHIFT-1)));
|
|
|
|
// raise Invalid Operation exception if an SNaN
|
|
if(isSNaN) {
|
|
raiseInvalidOperation(arm);
|
|
}
|
|
|
|
if(returnDefaultNaN(arm)) {
|
|
|
|
// default QNaN is required
|
|
exponent = ARM_FP32_EXPONENT(ARM_QNAN_DEFAULT_32);
|
|
fraction = ARM_FP32_FRACTION(ARM_QNAN_DEFAULT_32);
|
|
sign = ARM_FP32_SIGN(ARM_QNAN_DEFAULT_32);
|
|
|
|
} else {
|
|
|
|
// NaNs require exponent correction
|
|
exponent = ARM_FP32_EXP_ONES;
|
|
|
|
// NaNs require fraction correction
|
|
fraction = fraction << (ARM_FP32_EXP_SHIFT - ARM_FP16_EXP_SHIFT);
|
|
fraction |= ARM_QNAN_MASK_32;
|
|
}
|
|
|
|
} else {
|
|
|
|
// normalize a denormal value if required
|
|
if(!exponent) {
|
|
|
|
// shift up until implicit MSB is 1
|
|
do {
|
|
fraction <<= 1;
|
|
exponent--;
|
|
} while(!(fraction & (1<<ARM_FP16_EXP_SHIFT)));
|
|
|
|
// correct exponent and mask off implicit MSB
|
|
exponent++;
|
|
fraction &= ~(1<<ARM_FP16_EXP_SHIFT);
|
|
}
|
|
|
|
// rebase exponent and fraction
|
|
exponent += (ARM_FP32_EXP_BIAS - ARM_FP16_EXP_BIAS );
|
|
fraction <<= (ARM_FP32_EXP_SHIFT - ARM_FP16_EXP_SHIFT);
|
|
}
|
|
|
|
// compose single-precision result
|
|
return (
|
|
(sign << ARM_FP32_SIGN_SHIFT) |
|
|
(exponent << ARM_FP32_EXP_SHIFT ) |
|
|
fraction
|
|
);
|
|
}
|
|
|
|
//
|
|
// Convert from single-precision to half-precision
|
|
//
|
|
Uns16 armFPSingleToHalf(armP arm, Uns32 single) {
|
|
|
|
Int32 exponent = ARM_FP32_EXPONENT(single);
|
|
Uns32 fraction = ARM_FP32_FRACTION(single);
|
|
Bool sign = ARM_FP32_SIGN(single);
|
|
Bool expAll1 = (exponent==ARM_FP32_EXP_ONES);
|
|
Bool isZero = (!exponent && !fraction);
|
|
Bool isDenormal = (!exponent && fraction);
|
|
Bool isInfinity = (expAll1 && !fraction);
|
|
Bool isNaN = (expAll1 && !isInfinity);
|
|
Bool useAHP = inAHPMode(arm);
|
|
|
|
if(isInfinity) {
|
|
|
|
if(useAHP) {
|
|
|
|
// alternative half precision has a different infinity format
|
|
exponent = ARM_FP16_EXPONENT(ARM_FP16_AHP_INFINITY);
|
|
fraction = ARM_FP16_FRACTION(ARM_FP16_AHP_INFINITY);
|
|
|
|
raiseInvalidOperation(arm);
|
|
|
|
} else {
|
|
|
|
// IEEE infinities require exponent correction
|
|
exponent = ARM_FP16_EXPONENT(ARM_FP16_PLUS_INFINITY);
|
|
fraction = ARM_FP16_FRACTION(ARM_FP16_PLUS_INFINITY);
|
|
}
|
|
|
|
} else if(isZero) {
|
|
|
|
// zero values require no special processing
|
|
|
|
} else if(isNaN) {
|
|
|
|
// argument is an SNaN if MSB of fraction is clear
|
|
Bool isSNaN = !(fraction & (1 << (ARM_FP32_EXP_SHIFT-1)));
|
|
|
|
// raise Invalid Operation exception if using Alternative Half Precision
|
|
// format or argument is an SNaN
|
|
if(isSNaN || useAHP) {
|
|
raiseInvalidOperation(arm);
|
|
}
|
|
|
|
if(useAHP) {
|
|
|
|
// if using Alternative Half Precision format, NaNs go to zero
|
|
exponent = 0;
|
|
fraction = 0;
|
|
|
|
} else if(returnDefaultNaN(arm)) {
|
|
|
|
// default QNaN is required
|
|
exponent = ARM_FP16_EXPONENT(ARM_QNAN_DEFAULT_16);
|
|
fraction = ARM_FP16_FRACTION(ARM_QNAN_DEFAULT_16);
|
|
sign = ARM_FP16_SIGN(ARM_QNAN_DEFAULT_16);
|
|
|
|
} else {
|
|
|
|
// NaNs require exponent correction
|
|
exponent = ARM_FP16_EXP_ONES;
|
|
|
|
// NaNs require fraction correction
|
|
fraction = fraction >> (ARM_FP32_EXP_SHIFT - ARM_FP16_EXP_SHIFT);
|
|
fraction |= ARM_QNAN_MASK_16;
|
|
}
|
|
|
|
} else if(isDenormal && inFTZMode(arm)) {
|
|
|
|
// flush denormals to zero if required
|
|
setDenormalStickyFTZ(arm);
|
|
exponent = 0;
|
|
fraction = 0;
|
|
|
|
} else {
|
|
|
|
// split value into sign, unrounded mantissa and exponent
|
|
union {Uns32 u32; Flt32 f32;} u = {single};
|
|
Flt32 result = u.f32;
|
|
Flt32 mantissa = sign ? -result : result;
|
|
exponent = 0;
|
|
while(mantissa<1.0) {
|
|
mantissa *= 2.0; exponent--;
|
|
}
|
|
while(mantissa>=2.0) {
|
|
mantissa /= 2.0; exponent++;
|
|
}
|
|
|
|
// bias the exponent so that the minimum exponent becomes 1, lower
|
|
// values 0 (indicating possible underflow)
|
|
exponent -= ARM_FP16_EXP_MIN;
|
|
while(exponent<0) {
|
|
exponent++;
|
|
mantissa /= 2.0;
|
|
}
|
|
|
|
// get the unrounded mantissa as an integer and the "units in last
|
|
// place" rounding error
|
|
Flt32 mantissaExp2F = mantissa*(1<<ARM_FP16_EXP_SHIFT);
|
|
fraction = mantissaExp2F;
|
|
Flt32 error = mantissaExp2F - fraction;
|
|
|
|
// there is an underflow exception if the exponent is too small before
|
|
// rounding and the result is inexact
|
|
if(!exponent && error) {
|
|
raiseUnderflow(arm);
|
|
}
|
|
|
|
Bool roundUp;
|
|
Bool overflowToInf;
|
|
|
|
// now round using the required rounding mode
|
|
switch(getRoundingControl(arm)) {
|
|
|
|
case vmi_FPR_NEAREST:
|
|
roundUp = ((error>0.5) || ((error==0.5) && (fraction&1)));
|
|
overflowToInf = True;
|
|
break;
|
|
|
|
case vmi_FPR_POS_INF:
|
|
roundUp = (error && !sign);
|
|
overflowToInf = !sign;
|
|
break;
|
|
|
|
case vmi_FPR_NEG_INF:
|
|
roundUp = (error && sign);
|
|
overflowToInf = sign;
|
|
break;
|
|
|
|
default:
|
|
roundUp = False;
|
|
overflowToInf = False;
|
|
break;
|
|
}
|
|
|
|
if(roundUp) {
|
|
|
|
fraction++;
|
|
|
|
// check for round up from denormalized to normalized
|
|
if(fraction==(1<<ARM_FP16_EXP_SHIFT)) {
|
|
exponent = 1;
|
|
}
|
|
|
|
// check for round up to next exponent
|
|
if(fraction==(1<<(ARM_FP16_EXP_SHIFT+1))) {
|
|
exponent++;
|
|
fraction /= 2;
|
|
}
|
|
}
|
|
|
|
// deal with overflow and generate result
|
|
if(!useAHP) {
|
|
|
|
// IEEE format result required
|
|
if(exponent>=((1<<ARM_FP16_EXP_BITS)-1)) {
|
|
|
|
if(overflowToInf) {
|
|
exponent = ARM_FP16_EXPONENT(ARM_FP16_PLUS_INFINITY);
|
|
fraction = ARM_FP16_FRACTION(ARM_FP16_PLUS_INFINITY);
|
|
} else {
|
|
exponent = ARM_FP16_EXPONENT(ARM_FP16_MAX_NORMAL);
|
|
fraction = ARM_FP16_FRACTION(ARM_FP16_MAX_NORMAL);
|
|
}
|
|
|
|
raiseOverflow(arm);
|
|
|
|
// Note this is not in the psuedo code but is needed to match
|
|
// behavior of reference simulator
|
|
raiseInexact(arm);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// alternative half precision result required
|
|
if(exponent>=(1<<ARM_FP16_EXP_BITS)) {
|
|
|
|
exponent = ARM_FP16_EXPONENT(ARM_FP16_AHP_INFINITY);
|
|
fraction = ARM_FP16_FRACTION(ARM_FP16_AHP_INFINITY);
|
|
|
|
raiseInvalidOperation(arm);
|
|
|
|
// avoid an inexact exception
|
|
error = 0.0;
|
|
}
|
|
}
|
|
|
|
// deal with inexact exception
|
|
if(error) {
|
|
raiseInexact(arm);
|
|
}
|
|
}
|
|
|
|
// compose half-precision result
|
|
return (
|
|
(sign << ARM_FP16_SIGN_SHIFT) |
|
|
(exponent << ARM_FP16_EXP_SHIFT ) |
|
|
fraction
|
|
);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// INFINITY/ZERO CHECK
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Return True if the single-precision floating point values op1 and op2 are 0
|
|
// and infinity (in either order), setting the denormal sticky bit if so
|
|
//
|
|
Bool armFPInfinityAndZero(armP arm, Uns32 op1, Uns32 op2) {
|
|
|
|
Bool op1Zero = ((op1 & 0x7f800000) == 0);
|
|
Bool op2Zero = ((op2 & 0x7f800000) == 0);
|
|
Bool op1Inf = ((op1 & 0x7fffffff) == 0x7f800000);
|
|
Bool op2Inf = ((op2 & 0x7fffffff) == 0x7f800000);
|
|
|
|
if((op1Zero && op2Inf) || (op2Zero && op1Inf)) {
|
|
|
|
// check if an op is denormal number
|
|
if ((op1Zero && (op1 & 0x007fffff) != 0) ||
|
|
(op2Zero && (op2 & 0x007fffff) != 0))
|
|
{
|
|
// raise denormal exception
|
|
raiseDenormal(arm);
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
return False;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// QNAN/TINY HANDLING
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Is the argument a 32-bit QNaN?
|
|
//
|
|
inline static Bool is32BitQNaN(vmiFPNaNArgP arg) {
|
|
return arg->isFlt32 && (arg->u32 & ARM_QNAN_MASK_32);
|
|
}
|
|
|
|
//
|
|
// Is the argument a 64-bit QNaN?
|
|
//
|
|
inline static Bool is64BitQNaN(vmiFPNaNArgP arg) {
|
|
return !arg->isFlt32 && (arg->u64 & ARM_QNAN_MASK_64);
|
|
}
|
|
|
|
//
|
|
// Is the argument a 32-bit SNaN?
|
|
//
|
|
inline static Bool is32BitSNaN(vmiFPNaNArgP arg) {
|
|
return arg->isFlt32 && !(arg->u32 & ARM_QNAN_MASK_32);
|
|
}
|
|
|
|
//
|
|
// Is the argument a 64-bit SNaN?
|
|
//
|
|
inline static Bool is64BitSNaN(vmiFPNaNArgP arg) {
|
|
return !arg->isFlt32 && !(arg->u64 & ARM_QNAN_MASK_64);
|
|
}
|
|
|
|
//
|
|
// Handle 32-bit QNaN result
|
|
//
|
|
static VMI_FP_QNAN32_RESULT_FN(handleQNaN32) {
|
|
|
|
armP arm = (armP)processor;
|
|
|
|
if(!NaNArgNum || returnDefaultNaN(arm)) {
|
|
|
|
return ARM_QNAN_DEFAULT_32;
|
|
|
|
} else {
|
|
|
|
Uns32 i;
|
|
|
|
// PASS 1: if any argument is an 32-bit SNaN, return that (as a QNaN)
|
|
for(i=0; i<NaNArgNum; i++) {
|
|
if(is32BitSNaN(&NaNArgs[i])) {
|
|
return NaNArgs[i].u32 | ARM_QNAN_MASK_32;
|
|
}
|
|
}
|
|
|
|
// PASS 2: if any argument is a 32-bit QNaN, return that
|
|
for(i=0; i<NaNArgNum; i++) {
|
|
if(is32BitQNaN(&NaNArgs[i])) {
|
|
return NaNArgs[i].u32;
|
|
}
|
|
}
|
|
|
|
// otherwise, if no argument is a 32-bit NaN (e.g. this is a conversion
|
|
// from 64-bit) return the calculated result
|
|
return QNaN32;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle 64-bit QNaN result
|
|
//
|
|
static VMI_FP_QNAN64_RESULT_FN(handleQNaN64) {
|
|
|
|
armP arm = (armP)processor;
|
|
|
|
if(!NaNArgNum || returnDefaultNaN(arm)) {
|
|
|
|
return ARM_QNAN_DEFAULT_64;
|
|
|
|
} else {
|
|
|
|
Uns32 i;
|
|
|
|
// PASS 1: if any argument is an 64-bit SNaN, return that (as a QNaN)
|
|
for(i=0; i<NaNArgNum; i++) {
|
|
if(is64BitSNaN(&NaNArgs[i])) {
|
|
return NaNArgs[i].u64 | ARM_QNAN_MASK_64;
|
|
}
|
|
}
|
|
|
|
// PASS 2: if any argument is a 64-bit QNaN, return that
|
|
for(i=0; i<NaNArgNum; i++) {
|
|
if(is64BitQNaN(&NaNArgs[i])) {
|
|
return NaNArgs[i].u64;
|
|
}
|
|
}
|
|
|
|
// otherwise, if no argument is a 64-bit NaN (e.g. this is a conversion
|
|
// from 32-bit) return the calculated result
|
|
return QNaN64;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Is the passed value a QNaN or SNaN?
|
|
//
|
|
static Bool isNaN80(vmiFP80Arg value) {
|
|
|
|
// decompose vmiFP80Arg into mantissa and sign
|
|
union {vmiFP80Arg fp80; struct {Uns64 mantissa; Uns16 expSign;};} u = {
|
|
value
|
|
};
|
|
|
|
if((u.expSign & 0x7fff)!=0x7fff) {
|
|
// exponent most be all 1's
|
|
return False;
|
|
} else if(u.mantissa==0x8000000000000000ULL) {
|
|
// +infinity, -infinity
|
|
return False;
|
|
} else {
|
|
// NaNs have some non-zero bit in the mantissa
|
|
return u.mantissa;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle 16-bit indeterminate result
|
|
//
|
|
static VMI_FP_IND16_RESULT_FN(handleIndeterminate16) {
|
|
|
|
Uns32 result;
|
|
|
|
if(isNaN80(value)) {
|
|
result = 0;
|
|
} else if(value.bytes[VMI_FP_80_BYTES-1] & 0x80) {
|
|
result = isSigned ? ARM_MIN_INT16 : ARM_MIN_UNS16;
|
|
} else {
|
|
result = isSigned ? ARM_MAX_INT16 : ARM_MAX_UNS16;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Handle 32-bit indeterminate result
|
|
//
|
|
static VMI_FP_IND32_RESULT_FN(handleIndeterminate32) {
|
|
|
|
Uns32 result;
|
|
|
|
if(isNaN80(value)) {
|
|
result = 0;
|
|
} else if(value.bytes[VMI_FP_80_BYTES-1] & 0x80) {
|
|
result = isSigned ? ARM_MIN_INT32 : ARM_MIN_UNS32;
|
|
} else {
|
|
result = isSigned ? ARM_MAX_INT32 : ARM_MAX_UNS32;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Enumeration of tiny values that might be returned by flushing
|
|
//
|
|
typedef enum tinyValueE {
|
|
TV_PLUS_0,
|
|
TV_MINUS_0,
|
|
TV_LAST
|
|
} tinyValue;
|
|
|
|
//
|
|
// These contain tiny values
|
|
//
|
|
static vmiFP80Arg tinyValues32[TV_LAST];
|
|
|
|
//
|
|
// Initialize constant tiny 32-bit values
|
|
//
|
|
static void initTinyValue32(tinyValue tv, Uns32 pattern) {
|
|
union {Uns32 u32; Flt32 f32;} u = {pattern};
|
|
tinyValues32[tv].f80 = u.f32;
|
|
}
|
|
|
|
//
|
|
// Initialize constant tiny values
|
|
//
|
|
static void initTinyValues(void) {
|
|
initTinyValue32(TV_PLUS_0, 0x00000000);
|
|
initTinyValue32(TV_MINUS_0, 0x80000000);
|
|
}
|
|
|
|
//
|
|
// Handle 32-bit tiny result
|
|
//
|
|
static VMI_FP_TINY_RESULT_FN(handleTinyResult) {
|
|
|
|
// set underflow sticky bit
|
|
setUnderflowStickyFTZ((armP)processor);
|
|
|
|
// return appropriately-signed zero
|
|
Bool isNegative = value.bytes[VMI_FP_80_BYTES-1] & 0x80;
|
|
return tinyValues32[isNegative ? TV_MINUS_0 : TV_PLUS_0];
|
|
}
|
|
|
|
//
|
|
// Handle 64-bit tiny argument
|
|
//
|
|
static VMI_FP_TINY_ARGUMENT_FN(handleTinyArgument) {
|
|
|
|
// set denormal sticky bit
|
|
setDenormalStickyFTZ((armP)processor);
|
|
|
|
// return appropriately-signed zero
|
|
Bool isNegative = value.bytes[VMI_FP_80_BYTES-1] & 0x80;
|
|
return tinyValues32[isNegative ? TV_MINUS_0 : TV_PLUS_0];
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// RESET AND INITIALIZATION
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Call on initialization
|
|
//
|
|
void armFPInitialize(armP arm) {
|
|
|
|
// initialize current control word to an empty value
|
|
arm->currentCW.u32 = -1;
|
|
|
|
// Initialize FP registers if present (AFTER armSysInitialize)
|
|
if (SCS_USE_CPUID(arm) && FPU_PRESENT(arm)) {
|
|
|
|
// initialize constant tiny values
|
|
initTinyValues();
|
|
|
|
// set up QNaN values and handlers
|
|
vmirtConfigureFPU(
|
|
(vmiProcessorP)arm,
|
|
ARM_QNAN_DEFAULT_32,
|
|
ARM_QNAN_DEFAULT_64,
|
|
0, // indeterminate value not used
|
|
0, // indeterminate value not used
|
|
0, // indeterminate value not used
|
|
handleQNaN32,
|
|
handleQNaN64,
|
|
handleIndeterminate16,
|
|
handleIndeterminate32,
|
|
0, // 64-bit indeterminate value handler not required
|
|
handleTinyResult,
|
|
handleTinyArgument,
|
|
True
|
|
);
|
|
|
|
// Set the write mask for CPACR to allow updating field cp10 and cp11
|
|
SCS_MASK_FIELD(arm, CPACR, cp10) = 3;
|
|
SCS_MASK_FIELD(arm, CPACR, cp11) = 3;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call on reset
|
|
//
|
|
void armFPReset(armP arm) {
|
|
|
|
// no action unless floating point is implemented
|
|
if(SCS_USE_CPUID(arm) && FPU_PRESENT(arm)) {
|
|
armWriteFPSCR(arm, 0, FPSCR_MASK);
|
|
}
|
|
}
|
|
|
|
|
|
|