a2d246b6b8
This patch takes quite a large step in transitioning from the ad-hoc RefCountingPtr to the c++11 shared_ptr by adopting its use for all Faults. There are no changes in behaviour, and the code modifications are mostly just replacing "new" with "make_shared".
367 lines
12 KiB
Plaintext
367 lines
12 KiB
Plaintext
// Copyright (c) 2006-2007 The Regents of The University of Michigan
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met: redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer;
|
|
// redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution;
|
|
// neither the name of the copyright holders nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// Authors: Ali Saidi
|
|
// Gabe Black
|
|
// Steve Reinhardt
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Mem utility templates and functions
|
|
//
|
|
|
|
output header {{
|
|
/**
|
|
* Base class for memory operations.
|
|
*/
|
|
class Mem : public SparcStaticInst
|
|
{
|
|
protected:
|
|
|
|
// Constructor
|
|
Mem(const char *mnem, ExtMachInst _machInst, OpClass __opClass) :
|
|
SparcStaticInst(mnem, _machInst, __opClass)
|
|
{
|
|
}
|
|
|
|
std::string generateDisassembly(Addr pc,
|
|
const SymbolTable *symtab) const;
|
|
};
|
|
|
|
/**
|
|
* Class for memory operations which use an immediate offset.
|
|
*/
|
|
class MemImm : public Mem
|
|
{
|
|
protected:
|
|
|
|
// Constructor
|
|
MemImm(const char *mnem, ExtMachInst _machInst, OpClass __opClass) :
|
|
Mem(mnem, _machInst, __opClass), imm(sext<13>(SIMM13))
|
|
{}
|
|
|
|
std::string generateDisassembly(Addr pc,
|
|
const SymbolTable *symtab) const;
|
|
|
|
const int32_t imm;
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
std::string Mem::generateDisassembly(Addr pc,
|
|
const SymbolTable *symtab) const
|
|
{
|
|
std::stringstream response;
|
|
bool load = flags[IsLoad];
|
|
bool store = flags[IsStore];
|
|
|
|
printMnemonic(response, mnemonic);
|
|
if (store) {
|
|
printReg(response, _srcRegIdx[0]);
|
|
ccprintf(response, ", ");
|
|
}
|
|
ccprintf(response, "[");
|
|
if (_srcRegIdx[!store ? 0 : 1] != 0) {
|
|
printSrcReg(response, !store ? 0 : 1);
|
|
ccprintf(response, " + ");
|
|
}
|
|
printSrcReg(response, !store ? 1 : 2);
|
|
ccprintf(response, "]");
|
|
if (load) {
|
|
ccprintf(response, ", ");
|
|
printReg(response, _destRegIdx[0]);
|
|
}
|
|
|
|
return response.str();
|
|
}
|
|
|
|
std::string MemImm::generateDisassembly(Addr pc,
|
|
const SymbolTable *symtab) const
|
|
{
|
|
std::stringstream response;
|
|
bool load = flags[IsLoad];
|
|
bool save = flags[IsStore];
|
|
|
|
printMnemonic(response, mnemonic);
|
|
if (save) {
|
|
printReg(response, _srcRegIdx[0]);
|
|
ccprintf(response, ", ");
|
|
}
|
|
ccprintf(response, "[");
|
|
if (_srcRegIdx[!save ? 0 : 1] != 0) {
|
|
printReg(response, _srcRegIdx[!save ? 0 : 1]);
|
|
ccprintf(response, " + ");
|
|
}
|
|
if (imm >= 0)
|
|
ccprintf(response, "0x%x]", imm);
|
|
else
|
|
ccprintf(response, "-0x%x]", -imm);
|
|
if (load) {
|
|
ccprintf(response, ", ");
|
|
printReg(response, _destRegIdx[0]);
|
|
}
|
|
|
|
return response.str();
|
|
}
|
|
}};
|
|
|
|
// This template provides the execute functions for a load
|
|
def template LoadExecute {{
|
|
Fault %(class_name)s::execute(CPU_EXEC_CONTEXT *xc,
|
|
Trace::InstRecord *traceData) const
|
|
{
|
|
Fault fault = NoFault;
|
|
Addr EA;
|
|
%(fp_enable_check)s;
|
|
%(op_decl)s;
|
|
%(op_rd)s;
|
|
%(ea_code)s;
|
|
DPRINTF(Sparc, "%s: The address is 0x%x\n", mnemonic, EA);
|
|
%(fault_check)s;
|
|
if (fault == NoFault) {
|
|
%(EA_trunc)s
|
|
fault = readMemAtomic(xc, traceData, EA, Mem, %(asi_val)s);
|
|
}
|
|
if (fault == NoFault) {
|
|
%(code)s;
|
|
}
|
|
if (fault == NoFault) {
|
|
// Write the resulting state to the execution context
|
|
%(op_wb)s;
|
|
}
|
|
|
|
return fault;
|
|
}
|
|
}};
|
|
|
|
def template LoadInitiateAcc {{
|
|
Fault %(class_name)s::initiateAcc(CPU_EXEC_CONTEXT * xc,
|
|
Trace::InstRecord * traceData) const
|
|
{
|
|
Fault fault = NoFault;
|
|
Addr EA;
|
|
%(fp_enable_check)s;
|
|
%(op_decl)s;
|
|
%(op_rd)s;
|
|
%(ea_code)s;
|
|
DPRINTF(Sparc, "%s: The address is 0x%x\n", mnemonic, EA);
|
|
%(fault_check)s;
|
|
if (fault == NoFault) {
|
|
%(EA_trunc)s
|
|
fault = readMemTiming(xc, traceData, EA, Mem, %(asi_val)s);
|
|
}
|
|
return fault;
|
|
}
|
|
}};
|
|
|
|
def template LoadCompleteAcc {{
|
|
Fault %(class_name)s::completeAcc(PacketPtr pkt, CPU_EXEC_CONTEXT * xc,
|
|
Trace::InstRecord * traceData) const
|
|
{
|
|
Fault fault = NoFault;
|
|
%(op_decl)s;
|
|
%(op_rd)s;
|
|
getMem(pkt, Mem, traceData);
|
|
%(code)s;
|
|
if (fault == NoFault) {
|
|
%(op_wb)s;
|
|
}
|
|
return fault;
|
|
}
|
|
}};
|
|
|
|
// This template provides the execute functions for a store
|
|
def template StoreExecute {{
|
|
Fault %(class_name)s::execute(CPU_EXEC_CONTEXT *xc,
|
|
Trace::InstRecord *traceData) const
|
|
{
|
|
Fault fault = NoFault;
|
|
// This is to support the conditional store in cas instructions.
|
|
// It should be optomized out in all the others
|
|
bool storeCond = true;
|
|
Addr EA;
|
|
%(fp_enable_check)s;
|
|
%(op_decl)s;
|
|
%(op_rd)s;
|
|
%(ea_code)s;
|
|
DPRINTF(Sparc, "%s: The address is 0x%x\n", mnemonic, EA);
|
|
%(fault_check)s;
|
|
if (fault == NoFault) {
|
|
%(code)s;
|
|
}
|
|
if (storeCond && fault == NoFault) {
|
|
%(EA_trunc)s
|
|
fault = writeMemAtomic(xc, traceData, Mem, EA, %(asi_val)s, 0);
|
|
}
|
|
if (fault == NoFault) {
|
|
// Write the resulting state to the execution context
|
|
%(op_wb)s;
|
|
}
|
|
|
|
return fault;
|
|
}
|
|
}};
|
|
|
|
def template StoreInitiateAcc {{
|
|
Fault %(class_name)s::initiateAcc(CPU_EXEC_CONTEXT * xc,
|
|
Trace::InstRecord * traceData) const
|
|
{
|
|
Fault fault = NoFault;
|
|
bool storeCond = true;
|
|
Addr EA;
|
|
%(fp_enable_check)s;
|
|
%(op_decl)s;
|
|
|
|
%(op_rd)s;
|
|
%(ea_code)s;
|
|
DPRINTF(Sparc, "%s: The address is 0x%x\n", mnemonic, EA);
|
|
%(fault_check)s;
|
|
if (fault == NoFault) {
|
|
%(code)s;
|
|
}
|
|
if (storeCond && fault == NoFault) {
|
|
%(EA_trunc)s
|
|
fault = writeMemTiming(xc, traceData, Mem, EA, %(asi_val)s, 0);
|
|
}
|
|
return fault;
|
|
}
|
|
}};
|
|
|
|
def template StoreCompleteAcc {{
|
|
Fault %(class_name)s::completeAcc(PacketPtr, CPU_EXEC_CONTEXT * xc,
|
|
Trace::InstRecord * traceData) const
|
|
{
|
|
return NoFault;
|
|
}
|
|
}};
|
|
|
|
def template EACompExecute {{
|
|
Fault
|
|
%(class_name)s::eaComp(CPU_EXEC_CONTEXT *xc,
|
|
Trace::InstRecord *traceData) const
|
|
{
|
|
Addr EA;
|
|
Fault fault = NoFault;
|
|
%(op_decl)s;
|
|
%(op_rd)s;
|
|
%(ea_code)s;
|
|
%(fault_check)s;
|
|
|
|
// NOTE: Trace Data is written using execute or completeAcc templates
|
|
if (fault == NoFault) {
|
|
%(EA_trunc)s
|
|
xc->setEA(EA);
|
|
}
|
|
|
|
return fault;
|
|
}
|
|
}};
|
|
|
|
def template EACompDeclare {{
|
|
Fault eaComp(%(CPU_exec_context)s *, Trace::InstRecord *) const;
|
|
}};
|
|
|
|
// This delcares the initiateAcc function in memory operations
|
|
def template InitiateAccDeclare {{
|
|
Fault initiateAcc(%(CPU_exec_context)s *, Trace::InstRecord *) const;
|
|
}};
|
|
|
|
// This declares the completeAcc function in memory operations
|
|
def template CompleteAccDeclare {{
|
|
Fault completeAcc(PacketPtr, %(CPU_exec_context)s *, Trace::InstRecord *) const;
|
|
}};
|
|
|
|
// Here are some code snippets which check for various fault conditions
|
|
let {{
|
|
LoadFuncs = [LoadExecute, LoadInitiateAcc, LoadCompleteAcc]
|
|
StoreFuncs = [StoreExecute, StoreInitiateAcc, StoreCompleteAcc]
|
|
|
|
# The LSB can be zero, since it's really the MSB in doubles and quads
|
|
# and we're dealing with doubles
|
|
BlockAlignmentFaultCheck = '''
|
|
if (RD & 0xe)
|
|
fault = std::make_shared<IllegalInstruction>();
|
|
else if (EA & 0x3f)
|
|
fault = std::make_shared<MemAddressNotAligned>();
|
|
'''
|
|
TwinAlignmentFaultCheck = '''
|
|
if (RD & 0x1)
|
|
fault = std::make_shared<IllegalInstruction>();
|
|
else if (EA & 0xf)
|
|
fault = std::make_shared<MemAddressNotAligned>();
|
|
'''
|
|
# XXX Need to take care of pstate.hpriv as well. The lower ASIs
|
|
# are split into ones that are available in priv and hpriv, and
|
|
# those that are only available in hpriv
|
|
AlternateASIPrivFaultCheck = '''
|
|
if ((!Pstate.priv && !Hpstate.hpriv &&
|
|
!asiIsUnPriv((ASI)EXT_ASI)) ||
|
|
(!Hpstate.hpriv && asiIsHPriv((ASI)EXT_ASI)))
|
|
fault = std::make_shared<PrivilegedAction>();
|
|
else if (asiIsAsIfUser((ASI)EXT_ASI) && !Pstate.priv)
|
|
fault = std::make_shared<PrivilegedAction>();
|
|
'''
|
|
|
|
TruncateEA = '''
|
|
if (!FullSystem)
|
|
EA = Pstate.am ? EA<31:0> : EA;
|
|
'''
|
|
}};
|
|
|
|
// A simple function to generate the name of the macro op of a certain
|
|
// instruction at a certain micropc
|
|
let {{
|
|
def makeMicroName(name, microPc):
|
|
return name + "::" + name + "_" + str(microPc)
|
|
}};
|
|
|
|
// This function properly generates the execute functions for one of the
|
|
// templates above. This is needed because in one case, ea computation,
|
|
// fault checks and the actual code all occur in the same function,
|
|
// and in the other they're distributed across two. Also note that for
|
|
// execute functions, the name of the base class doesn't matter.
|
|
let {{
|
|
def doSplitExecute(execute, name, Name, asi, opt_flags, microParam):
|
|
microParam["asi_val"] = asi;
|
|
iop = InstObjParams(name, Name, '', microParam, opt_flags)
|
|
(execf, initf, compf) = execute
|
|
return execf.subst(iop) + initf.subst(iop) + compf.subst(iop)
|
|
|
|
|
|
def doDualSplitExecute(code, postacc_code, eaRegCode, eaImmCode, execute,
|
|
faultCode, nameReg, nameImm, NameReg, NameImm, asi, opt_flags):
|
|
executeCode = ''
|
|
for (eaCode, name, Name) in (
|
|
(eaRegCode, nameReg, NameReg),
|
|
(eaImmCode, nameImm, NameImm)):
|
|
microParams = {"code": code, "postacc_code" : postacc_code,
|
|
"ea_code": eaCode, "fault_check": faultCode,
|
|
"EA_trunc" : TruncateEA}
|
|
executeCode += doSplitExecute(execute, name, Name,
|
|
asi, opt_flags, microParams)
|
|
return executeCode
|
|
}};
|