/* * 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 // 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_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<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<=((1<=(1<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; icurrentCW.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); } }