Fail* directories reorganized, Code-cleanup (-> coding-style), Typos+comments fixed.
git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1321 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
509
simulators/ovp/armmModel/armmUtils.c
Normal file
509
simulators/ovp/armmModel/armmUtils.c
Normal file
@ -0,0 +1,509 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// VMI header files
|
||||
#include "vmi/vmiCxt.h"
|
||||
#include "vmi/vmiRt.h"
|
||||
#include "vmi/vmiMessage.h"
|
||||
|
||||
// model header files
|
||||
#include "armDecode.h"
|
||||
#include "armExceptions.h"
|
||||
#include "armFunctions.h"
|
||||
#include "armStructure.h"
|
||||
#include "armSysRegisters.h"
|
||||
#include "armUtils.h"
|
||||
#include "armVM.h"
|
||||
|
||||
//
|
||||
// Return the version of the instruction set implemented by the processor
|
||||
//
|
||||
inline static Uns32 getInstructionVersion(armP arm) {
|
||||
return ARM_INSTRUCTION_VERSION(arm->configInfo.arch);
|
||||
}
|
||||
|
||||
//
|
||||
// Return endianness of the processor, public routine
|
||||
//
|
||||
VMI_ENDIAN_FN(armGetEndian) {
|
||||
|
||||
armP arm = (armP)processor;
|
||||
|
||||
if(isFetch) {
|
||||
return arm->instructionEndian;
|
||||
} else if(SCS_FIELD(arm, AIRCR, ENDIANNESS)) {
|
||||
return MEM_ENDIAN_BIG;
|
||||
} else {
|
||||
return MEM_ENDIAN_LITTLE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Set the initial endianness for the model
|
||||
//
|
||||
void armSetInitialEndian(armP arm, Bool isBigEndian) {
|
||||
SCS_FIELD(arm, AIRCR, ENDIANNESS) = isBigEndian;
|
||||
}
|
||||
|
||||
//
|
||||
// Return the next instruction address after 'thisPC'.
|
||||
//
|
||||
VMI_NEXT_PC_FN(armNextInstruction) {
|
||||
return (Uns32)(thisPC + armGetInstructionSize((armP)processor, thisPC));
|
||||
}
|
||||
|
||||
//
|
||||
// Return the name of a GPR
|
||||
//
|
||||
const char *armGetGPRName(armP arm, Uns32 index) {
|
||||
|
||||
static const char *gprNames[] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc"
|
||||
};
|
||||
|
||||
return gprNames[index];
|
||||
}
|
||||
|
||||
//
|
||||
// Return the name of a CPR
|
||||
//
|
||||
const char *armGetCPRName(armP arm, Uns32 index) {
|
||||
|
||||
static const char *cprNames[] = {
|
||||
"cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
|
||||
"cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15"
|
||||
};
|
||||
|
||||
return cprNames[index];
|
||||
}
|
||||
|
||||
#define SWAP_REG(_R1, _R2) {Uns32 _V = _R1; _R1= _R2; _R2 = _V;}
|
||||
#define SWAP_GPR(_N, _SET) SWAP_REG(arm->regs[_N], arm->bank.R##_N##_##_SET)
|
||||
|
||||
//
|
||||
// Switch banked registers on switch to the passed mode
|
||||
//
|
||||
inline static void switchRegs(armP arm, Bool useSPProcess) {
|
||||
if(useSPProcess) {
|
||||
SWAP_GPR(13, process);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Switch banked registers on switch between the passed modes
|
||||
//
|
||||
void armSwitchRegs(armP arm, Bool oldUseSPProcess, Bool newUseSPProcess) {
|
||||
switchRegs(arm, oldUseSPProcess);
|
||||
switchRegs(arm, newUseSPProcess);
|
||||
}
|
||||
|
||||
//
|
||||
// Return current processor block mask
|
||||
//
|
||||
static armBlockMask getBlockMask(armP arm) {
|
||||
|
||||
armBlockMask blockMask = 0;
|
||||
|
||||
// get blockmask component selecting register bank
|
||||
if(USE_SP_PROCESS(arm)) {
|
||||
blockMask |= ARM_BM_USE_SP_PROCESS;
|
||||
}
|
||||
|
||||
// include component for endianness
|
||||
if(SCS_FIELD(arm, AIRCR, ENDIANNESS)) {
|
||||
blockMask |= ARM_BM_BIG_ENDIAN;
|
||||
}
|
||||
|
||||
// include component for alignment checking
|
||||
if(DO_UNALIGNED(arm)) {
|
||||
blockMask |= ARM_BM_UNALIGNED;
|
||||
}
|
||||
|
||||
// include component for cp10 (and cp11) enable checking
|
||||
// NOTE: arm documentation specifies behavior is undefined if cp10 enable
|
||||
// differs from cp11 enable, so only cp10 enable is checked here
|
||||
Uns32 cp10Enable = SCS_FIELD(arm, CPACR, cp10);
|
||||
if(IN_USER_MODE(arm)) {
|
||||
if(cp10Enable&2) {blockMask |= ARM_BM_CP10;}
|
||||
} else {
|
||||
if(cp10Enable&1) {blockMask |= ARM_BM_CP10;}
|
||||
}
|
||||
|
||||
// include CONTROL.FPCA bit
|
||||
if(CONTROL_FIELD(arm, FPCA)) {
|
||||
blockMask |= ARM_BM_FPCA;
|
||||
}
|
||||
|
||||
// include FPCCR.ASPEN bit
|
||||
if(SCS_FIELD(arm, FPCCR, ASPEN)) {
|
||||
blockMask |= ARM_BM_ASPEN;
|
||||
}
|
||||
|
||||
// include FPCCR.LSPACT bit
|
||||
if(SCS_FIELD(arm, FPCCR, LSPACT)) {
|
||||
blockMask |= ARM_BM_LSPACT;
|
||||
}
|
||||
|
||||
return blockMask;
|
||||
}
|
||||
|
||||
//
|
||||
// Update processor block mask
|
||||
//
|
||||
void armSetBlockMask(armP arm) {
|
||||
vmirtSetBlockMask((vmiProcessorP)arm, getBlockMask(arm));
|
||||
}
|
||||
|
||||
//
|
||||
// Update processor mode when system state that affects it has been written
|
||||
//
|
||||
static void updateMode(armP arm) {
|
||||
|
||||
// update block mask
|
||||
armSetBlockMask(arm);
|
||||
|
||||
// switch mode if required
|
||||
armSwitchMode(arm);
|
||||
}
|
||||
|
||||
//
|
||||
// Write CONTROL.SP_PROCESS field
|
||||
//
|
||||
void armWriteSPProcess(armP arm, Bool newUseSPProcess) {
|
||||
|
||||
Bool oldUseSPProcess = USE_SP_PROCESS(arm);
|
||||
|
||||
if(oldUseSPProcess!=newUseSPProcess) {
|
||||
|
||||
// update CONTROL.SP_PROCESS
|
||||
USE_SP_PROCESS(arm) = newUseSPProcess;
|
||||
|
||||
// switch banked registers
|
||||
armSwitchRegs(arm, oldUseSPProcess, newUseSPProcess);
|
||||
|
||||
// update block mask
|
||||
armSetBlockMask(arm);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Read CPSR register
|
||||
//
|
||||
Uns32 armReadCPSR(armP arm) {
|
||||
|
||||
// seed flag fields from flag structure
|
||||
PSR_FIELD(arm, Z) = arm->aflags.ZF;
|
||||
PSR_FIELD(arm, N) = arm->aflags.NF;
|
||||
PSR_FIELD(arm, C) = arm->aflags.CF;
|
||||
PSR_FIELD(arm, V) = arm->aflags.VF;
|
||||
PSR_FIELD(arm, Q) = arm->oflags.QF;
|
||||
|
||||
// seed if-then state fields
|
||||
PSR_FIELD(arm, IT10) = arm->itStateRT & 0x3;
|
||||
PSR_FIELD(arm, IT72) = arm->itStateRT >> 2;
|
||||
|
||||
// return derived value
|
||||
return arm->sregs.PSR.reg;
|
||||
}
|
||||
|
||||
//
|
||||
// Read CONTROL register
|
||||
//
|
||||
Uns32 armReadCONTROL(armP arm) {
|
||||
|
||||
// return value
|
||||
return arm->sregs.CONTROL.reg;
|
||||
}
|
||||
|
||||
//
|
||||
// Write CPSR register
|
||||
//
|
||||
void armWriteCPSR(armP arm, Uns32 value, Uns32 mask) {
|
||||
|
||||
// save current processor mode
|
||||
Bool oldHandlerMode = IN_HANDLER_MODE(arm);
|
||||
Bool oldCPSRT = PSR_FIELD(arm, T);
|
||||
|
||||
if (!DSP_PRESENT(arm)) {
|
||||
// GE fields reserved when DSP not implemented
|
||||
mask &= ~PSR_GE;
|
||||
}
|
||||
|
||||
// set new register value (writable bits only)
|
||||
arm->sregs.PSR.reg = (value & mask) | (arm->sregs.PSR.reg & ~mask);
|
||||
|
||||
// get new processor mode
|
||||
Bool newHandlerMode = IN_HANDLER_MODE(arm);
|
||||
Bool newCPSRT = PSR_FIELD(arm, T);
|
||||
|
||||
// update arithmetic flag fields if required
|
||||
if(mask & PSR_FLAGS) {
|
||||
arm->aflags.ZF = PSR_FIELD(arm, Z);
|
||||
arm->aflags.NF = PSR_FIELD(arm, N);
|
||||
arm->aflags.CF = PSR_FIELD(arm, C);
|
||||
arm->aflags.VF = PSR_FIELD(arm, V);
|
||||
arm->oflags.QF = PSR_FIELD(arm, Q);
|
||||
}
|
||||
|
||||
// update if-then state if required
|
||||
if(mask & PSR_IT) {
|
||||
arm->itStateRT = (PSR_FIELD(arm, IT10) | (PSR_FIELD(arm, IT72<<2)));
|
||||
}
|
||||
|
||||
// update processor mode if required
|
||||
if((oldHandlerMode!=newHandlerMode) || (oldCPSRT!=newCPSRT)) {
|
||||
updateMode(arm);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Write PRIMASK register
|
||||
//
|
||||
void armWritePRIMASK(armP arm, Uns32 value) {
|
||||
|
||||
value &= PRIMASK_MASK;
|
||||
|
||||
if(arm->sregs.PRIMASK != value) {
|
||||
arm->sregs.PRIMASK = value;
|
||||
armRefreshExecutionPriority(arm);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Write BASEPRI register
|
||||
//
|
||||
void armWriteBASEPRI(armP arm, Uns32 value) {
|
||||
|
||||
value &= (BASEPRI_MASK & arm->priorityMask);
|
||||
|
||||
if(arm->sregs.BASEPRI != value) {
|
||||
arm->sregs.BASEPRI = value;
|
||||
armRefreshExecutionPriority(arm);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Write BASEPRI register (using BASEPRI_MAX semantics)
|
||||
//
|
||||
void armWriteBASEPRI_MAX(armP arm, Uns32 value) {
|
||||
|
||||
value &= (BASEPRI_MASK & arm->priorityMask);
|
||||
|
||||
if(value && ((value<arm->sregs.BASEPRI) || !arm->sregs.BASEPRI)) {
|
||||
arm->sregs.BASEPRI = value;
|
||||
armRefreshExecutionPriority(arm);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Write FAULTMASK register
|
||||
//
|
||||
void armWriteFAULTMASK(armP arm, Uns32 value) {
|
||||
|
||||
value &= FAULTMASK_MASK;
|
||||
|
||||
if((arm->executionPriority>-1) && (value!=arm->sregs.FAULTMASK)) {
|
||||
arm->sregs.FAULTMASK = value;
|
||||
armRefreshExecutionPriority(arm);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// update the CONTROL.FPCA bit to the value. If it changes then set the block mask
|
||||
//
|
||||
void armUpdateFPCA(armP arm, Bool value) {
|
||||
|
||||
// if CONTROL.FPCA changes set new value and set the block mask
|
||||
if (CONTROL_FIELD(arm, FPCA) != value) {
|
||||
|
||||
CONTROL_FIELD(arm, FPCA) = value;
|
||||
armSetBlockMask(arm);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// update the FPCCR.LSPACT bit to the value. If it changes then set the block mask
|
||||
//
|
||||
void armUpdateLSPACT(armP arm, Bool value) {
|
||||
|
||||
// if FPCCR.LSPACT changes set new value and set the block mask
|
||||
if (SCS_FIELD(arm, FPCCR, LSPACT) != value) {
|
||||
|
||||
SCS_FIELD(arm, FPCCR, LSPACT) = value;
|
||||
armSetBlockMask(arm);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Write CONTROL register
|
||||
//
|
||||
void armWriteCONTROL(armP arm, Uns32 value) {
|
||||
|
||||
armCONTROL u = {reg:value};
|
||||
|
||||
// update CONTROL.FPCA (FPCA is only implemented if FPU is present)
|
||||
if (FPU_PRESENT(arm)) {
|
||||
armUpdateFPCA(arm, u.fields.FPCA);
|
||||
}
|
||||
|
||||
// update CONTROL.threadUnpriv
|
||||
Bool oldUserMode = IN_USER_MODE(arm);
|
||||
CONTROL_FIELD(arm, threadUnpriv) = u.fields.threadUnpriv;
|
||||
Bool newUserMode = IN_USER_MODE(arm);
|
||||
|
||||
// handle any switch to user mode
|
||||
if(oldUserMode!=newUserMode) {
|
||||
updateMode(arm);
|
||||
}
|
||||
|
||||
// CONTROL.useSP_Process may only be modified in Thread mode
|
||||
if(!IN_HANDLER_MODE(arm)) {
|
||||
armWriteSPProcess(arm, u.fields.useSP_Process);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Write SP register
|
||||
//
|
||||
void armWriteSP(armP arm, Uns32 value) {
|
||||
arm->regs[ARM_REG_SP] = value & ~3;
|
||||
}
|
||||
|
||||
//
|
||||
// Switch processor mode if required
|
||||
//
|
||||
void armSwitchMode(armP arm) {
|
||||
|
||||
armMode mode;
|
||||
|
||||
if(!IN_THUMB_MODE(arm)) {
|
||||
|
||||
// invalid mode
|
||||
mode = ARM_MODE_ARM;
|
||||
SCS_FIELD(arm, CFSR, INVSTATE) = 1;
|
||||
|
||||
} else {
|
||||
|
||||
mode = 0;
|
||||
|
||||
// user/kernel mode mask
|
||||
if(IN_USER_MODE(arm)) {
|
||||
mode |= ARM_MODE_U;
|
||||
}
|
||||
|
||||
// add MPU enable mask
|
||||
if(!MPU_ENABLED(arm)) {
|
||||
// no action
|
||||
} else if(arm->executionPriority>=0) {
|
||||
mode |= ARM_MODE_MPU;
|
||||
} else if(SCS_FIELD(arm, MPU_CONTROL, HFNMIENA)) {
|
||||
mode |= ARM_MODE_MPU;
|
||||
}
|
||||
}
|
||||
|
||||
// switch mode if required
|
||||
if(mode != arm->mode) {
|
||||
vmirtSetMode((vmiProcessorP)arm, mode);
|
||||
arm->mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PROCESSOR RUN STATE TRANSITION HANDLING
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// If this memory access callback is triggered, abort any active load linked
|
||||
//
|
||||
static VMI_MEM_WATCH_FN(abortEA) {
|
||||
armAbortExclusiveAccess((armP)userData);
|
||||
}
|
||||
|
||||
//
|
||||
// Install or remove the exclusive access monitor callback
|
||||
//
|
||||
static void updateExclusiveAccessCallback(armP arm, Bool install) {
|
||||
|
||||
memDomainP domain = vmirtGetProcessorDataDomain((vmiProcessorP)arm);
|
||||
Uns32 simLow = arm->exclusiveTag;
|
||||
Uns32 simHigh = simLow + ~arm->exclusiveTagMask;
|
||||
|
||||
// install or remove a watchpoint on the current exclusive access address
|
||||
if(install) {
|
||||
vmirtAddWriteCallback(domain, simLow, simHigh, abortEA, arm);
|
||||
} else {
|
||||
vmirtRemoveWriteCallback(domain, simLow, simHigh, abortEA, arm);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Abort any active exclusive access
|
||||
//
|
||||
void armAbortExclusiveAccess(armP arm) {
|
||||
|
||||
if(arm->exclusiveTag != ARM_NO_TAG) {
|
||||
|
||||
// remove callback on exclusive access monitor region
|
||||
updateExclusiveAccessCallback(arm, False);
|
||||
|
||||
// clear exclusive tag (AFTER updateExclusiveAccessCallback)
|
||||
arm->exclusiveTag = ARM_NO_TAG;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This is called on simulator context switch (when this processor is either
|
||||
// about to start or about to stop simulation)
|
||||
//
|
||||
VMI_IASSWITCH_FN(armContextSwitchCB) {
|
||||
|
||||
armP arm = (armP)processor;
|
||||
|
||||
// establish a watchpoint on a pending exclusive address to detect if that
|
||||
// address is written elsewhere.
|
||||
if(arm->exclusiveTag != ARM_NO_TAG) {
|
||||
updateExclusiveAccessCallback(arm, state==RS_SUSPEND);
|
||||
}
|
||||
|
||||
// if a processor is about to be run, make state consistent with any changes
|
||||
// that may have occurred while the processor was suspended
|
||||
if(state==RS_RUN) {
|
||||
armSetBlockMask(arm);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user