diff options
Diffstat (limited to 'cpu')
-rw-r--r-- | cpu/SConscript | 7 | ||||
-rw-r--r-- | cpu/base.cc | 10 | ||||
-rw-r--r-- | cpu/base.hh | 2 | ||||
-rw-r--r-- | cpu/checker/cpu.cc | 857 | ||||
-rw-r--r-- | cpu/checker/cpu.hh | 336 | ||||
-rw-r--r-- | cpu/checker/cpu_builder.cc | 126 | ||||
-rw-r--r-- | cpu/checker/exec_context.hh | 225 | ||||
-rw-r--r-- | cpu/checker/o3_cpu_builder.cc | 126 | ||||
-rw-r--r-- | cpu/cpu_models.py | 3 |
9 files changed, 1689 insertions, 3 deletions
diff --git a/cpu/SConscript b/cpu/SConscript index c34971acf..5d727bd25 100644 --- a/cpu/SConscript +++ b/cpu/SConscript @@ -150,6 +150,13 @@ if 'OzoneCPU' in env['CPU_MODELS']: ozone/lw_lsq.cc ''') +if 'CheckerCPU' in env['CPU_MODELS']: + sources += Split(''' + checker/cpu.cc + checker/cpu_builder.cc + checker/o3_cpu_builder.cc + ''') + # FullCPU sources are included from m5/SConscript since they're not # below this point in the file hierarchy. diff --git a/cpu/base.cc b/cpu/base.cc index 2eb5f7fd3..74b679d5d 100644 --- a/cpu/base.cc +++ b/cpu/base.cc @@ -164,6 +164,7 @@ BaseCPU::Params::Params() #if FULL_SYSTEM profile = false; #endif + checker = NULL; } void @@ -229,15 +230,18 @@ BaseCPU::registerExecContexts() { for (int i = 0; i < execContexts.size(); ++i) { ExecContext *xc = execContexts[i]; + + if (xc->status() == ExecContext::Suspended) { #if FULL_SYSTEM - int id = params->cpu_id; - if (id != -1) - id += i; + int id = params->cpu_id; + if (id != -1) + id += i; xc->setCpuId(system->registerExecContext(xc, id)); #else xc->setCpuId(xc->getProcessPtr()->registerExecContext(xc)); #endif + } } } diff --git a/cpu/base.hh b/cpu/base.hh index d9d5d2b88..20166d7ee 100644 --- a/cpu/base.hh +++ b/cpu/base.hh @@ -44,6 +44,7 @@ namespace Kernel { class Statistics; } #endif class BranchPred; +class CheckerCPU; class ExecContext; class BaseCPU : public SimObject @@ -128,6 +129,7 @@ class BaseCPU : public SimObject int cpu_id; Tick profile; #endif + BaseCPU *checker; Params(); }; diff --git a/cpu/checker/cpu.cc b/cpu/checker/cpu.cc new file mode 100644 index 000000000..f1b43f601 --- /dev/null +++ b/cpu/checker/cpu.cc @@ -0,0 +1,857 @@ +/* + * Copyright (c) 2002-2005 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. + */ + +//#include <cmath> +#include <cstdio> +//#include <cstdlib> +#include <iostream> +#include <iomanip> +#include <list> +//#include <sstream> +#include <string> + +//#include "base/cprintf.hh" +//#include "base/inifile.hh" +//#include "base/loader/symtab.hh" +#include "base/misc.hh" +//#include "base/pollevent.hh" +//#include "base/range.hh" +#include "base/refcnt.hh" +//#include "base/stats/events.hh" +#include "cpu/base.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/cpu_exec_context.hh" +#include "cpu/exec_context.hh" +//#include "cpu/exetrace.hh" +//#include "cpu/profile.hh" +#include "cpu/sampler/sampler.hh" +//#include "cpu/smt.hh" +#include "cpu/static_inst.hh" +//#include "kern/kernel_stats.hh" +#include "mem/base_mem.hh" +#include "mem/mem_interface.hh" +#include "sim/byteswap.hh" +#include "sim/builder.hh" +//#include "sim/debug.hh" +//#include "sim/host.hh" +//#include "sim/sim_events.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/alpha_impl.hh" + +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" + +#if FULL_SYSTEM +#include "base/remote_gdb.hh" +#include "mem/functional/memory_control.hh" +#include "mem/functional/physical.hh" +#include "sim/system.hh" +#include "arch/tlb.hh" +#include "arch/stacktrace.hh" +#include "arch/vtophys.hh" +#else // !FULL_SYSTEM +#include "mem/functional/functional.hh" +#endif // FULL_SYSTEM + +using namespace std; +//The CheckerCPU does alpha only +using namespace AlphaISA; + +void +CheckerCPU::init() +{ +/* + BaseCPU::init(); +#if FULL_SYSTEM + for (int i = 0; i < execContexts.size(); ++i) { + ExecContext *xc = execContexts[i]; + + // initialize CPU, including PC + TheISA::initCPU(xc, xc->readCpuId()); + } +#endif +*/ +} + +CheckerCPU::CheckerCPU(Params *p) + : BaseCPU(p), cpuXC(NULL), xcProxy(NULL) +{ + memReq = new MemReq(); + memReq->xc = xcProxy; + memReq->asid = 0; + memReq->data = new uint8_t[64]; + + numInst = 0; + startNumInst = 0; + numLoad = 0; + startNumLoad = 0; + youngestSN = 0; + + changedPC = willChangePC = changedNextPC = false; + + exitOnError = p->exitOnError; +#if FULL_SYSTEM + itb = p->itb; + dtb = p->dtb; + systemPtr = NULL; + memPtr = NULL; +#endif +} + +CheckerCPU::~CheckerCPU() +{ +} + +void +CheckerCPU::setMemory(FunctionalMemory *mem) +{ + memPtr = mem; +#if !FULL_SYSTEM + cpuXC = new CPUExecContext(this, /* thread_num */ 0, mem, + /* asid */ 0); + + cpuXC->setStatus(ExecContext::Suspended); + xcProxy = cpuXC->getProxy(); + execContexts.push_back(xcProxy); +#else + if (systemPtr) { + cpuXC = new CPUExecContext(this, 0, systemPtr, itb, dtb, memPtr); + + cpuXC->setStatus(ExecContext::Suspended); + xcProxy = cpuXC->getProxy(); + execContexts.push_back(xcProxy); + memReq->xc = xcProxy; + } +#endif +} + +#if FULL_SYSTEM +void +CheckerCPU::setSystem(System *system) +{ + systemPtr = system; + + if (memPtr) { + cpuXC = new CPUExecContext(this, 0, systemPtr, itb, dtb, memPtr); + + cpuXC->setStatus(ExecContext::Suspended); + xcProxy = cpuXC->getProxy(); + execContexts.push_back(xcProxy); + memReq->xc = xcProxy; + } +} +#endif + +void +CheckerCPU::serialize(ostream &os) +{ +/* + BaseCPU::serialize(os); + SERIALIZE_SCALAR(inst); + nameOut(os, csprintf("%s.xc", name())); + cpuXC->serialize(os); + cacheCompletionEvent.serialize(os); +*/ +} + +void +CheckerCPU::unserialize(Checkpoint *cp, const string §ion) +{ +/* + BaseCPU::unserialize(cp, section); + UNSERIALIZE_SCALAR(inst); + cpuXC->unserialize(cp, csprintf("%s.xc", section)); +*/ +} + +Fault +CheckerCPU::copySrcTranslate(Addr src) +{ + static bool no_warn = true; + int blk_size = 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + int offset = src & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (src & PageMask) != ((src + blk_size) & PageMask) && + (src >> 40) != 0xfffffc) { + warn("Copied block source spans pages %x.", src); + no_warn = false; + } + + memReq->reset(src & ~(blk_size - 1), blk_size); + + // translate to physical address + Fault fault = cpuXC->translateDataReadReq(memReq); + + if (fault == NoFault) { + cpuXC->copySrcAddr = src; + cpuXC->copySrcPhysAddr = memReq->paddr + offset; + } else { + assert(!fault->isAlignmentFault()); + + cpuXC->copySrcAddr = 0; + cpuXC->copySrcPhysAddr = 0; + } + return fault; +} + +Fault +CheckerCPU::copy(Addr dest) +{ + static bool no_warn = true; + int blk_size = 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + uint8_t data[blk_size]; + //assert(cpuXC->copySrcAddr); + int offset = dest & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (dest & PageMask) != ((dest + blk_size) & PageMask) && + (dest >> 40) != 0xfffffc) { + no_warn = false; + warn("Copied block destination spans pages %x. ", dest); + } + + memReq->reset(dest & ~(blk_size -1), blk_size); + // translate to physical address + Fault fault = cpuXC->translateDataWriteReq(memReq); + + if (fault == NoFault) { + Addr dest_addr = memReq->paddr + offset; + // Need to read straight from memory since we have more than 8 bytes. + memReq->paddr = cpuXC->copySrcPhysAddr; + cpuXC->mem->read(memReq, data); + memReq->paddr = dest_addr; + cpuXC->mem->write(memReq, data); + memReq->cmd = Copy; + memReq->completionEvent = NULL; + memReq->paddr = cpuXC->copySrcPhysAddr; + memReq->dest = dest_addr; + memReq->size = 64; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + } + else + assert(!fault->isAlignmentFault()); + + return fault; +} + +// precise architected memory state accessor macros +template <class T> +Fault +CheckerCPU::read(Addr addr, T &data, unsigned flags) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + // Should I probe the DTB? Or should I just take the physical address + // and assume correct translation? + translateDataReadReq(memReq); + + // if we have a cache, do cache access too + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + + if (!(memReq->flags & UNCACHEABLE)) { + cpuXC->read(memReq, data); + } else { + // Assume the data is correct if it's an uncached access + memcpy(&data, &unverifiedResult.integer, sizeof(T)); + } + + return NoFault; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Fault +CheckerCPU::read(Addr addr, uint64_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint32_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint16_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint8_t &data, unsigned flags); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +CheckerCPU::read(Addr addr, double &data, unsigned flags) +{ + return read(addr, *(uint64_t*)&data, flags); +} + +template<> +Fault +CheckerCPU::read(Addr addr, float &data, unsigned flags) +{ + return read(addr, *(uint32_t*)&data, flags); +} + +template<> +Fault +CheckerCPU::read(Addr addr, int32_t &data, unsigned flags) +{ + return read(addr, (uint32_t&)data, flags); +} + +template <class T> +Fault +CheckerCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + cpuXC->translateDataWriteReq(memReq); + + if ((!(unverifiedReq->flags & LOCKED) || + ((unverifiedReq->flags & LOCKED) && + unverifiedReq->result == 1)) && + !(unverifiedReq->flags & UNCACHEABLE)) { + // do functional access +// cpuXC->read(memReq, data); + + memReq->cmd = Write; +// memcpy(memReq->data,(uint8_t *)&data,memReq->size); + T inst_data; + memcpy(&inst_data, unverifiedReq->data, sizeof(T)); + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + + // Hard to verify this as the data writes back after the + // instruction commits. May only be able to check that the + // value produced from execute() matches the value produced + // from the instruction's first execution. + if (data != inst_data) { + warn("Store value does not match value in memory! " + "Instruction: %#x, memory: %#x", + inst_data, data); + handleError(); + } + } + + // Assume the result was the same as the one passed in. This checker + // doesn't check if the SC should succeed or fail, it just checks the + // value. + if (res) + *res = unverifiedReq->result; + + return NoFault; +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +template +Fault +CheckerCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +CheckerCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint64_t*)&data, addr, flags, res); +} + +template<> +Fault +CheckerCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint32_t*)&data, addr, flags, res); +} + +template<> +Fault +CheckerCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) +{ + return write((uint32_t)data, addr, flags, res); +} + + +#if FULL_SYSTEM +Addr +CheckerCPU::dbg_vtophys(Addr addr) +{ + return vtophys(xcProxy, addr); +} +#endif // FULL_SYSTEM + +#if FULL_SYSTEM +void +CheckerCPU::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (cpuXC->status() == ExecContext::Suspended) { + DPRINTF(IPI,"Suspended Processor awoke\n"); + cpuXC->activate(); + } +} +#endif // FULL_SYSTEM + +bool +CheckerCPU::translateInstReq(MemReqPtr &req) +{ +#if FULL_SYSTEM + return (cpuXC->translateInstReq(req) == NoFault); +#else + cpuXC->translateInstReq(req); + return true; +#endif +} + +void +CheckerCPU::translateDataReadReq(MemReqPtr &req) +{ + cpuXC->translateDataReadReq(req); + + if (req->vaddr != unverifiedReq->vaddr) { + warn("Request virtual addresses do not match! Inst: %#x, checker:" + " %#x", + unverifiedReq->vaddr, req->vaddr); + } + req->paddr = unverifiedReq->paddr; + + if (checkFlags(req)) { + warn("Request flags do not match! Inst: %#x, checker: %#x", + unverifiedReq->flags, req->flags); + handleError(); + } +} + +void +CheckerCPU::translateDataWriteReq(MemReqPtr &req) +{ + cpuXC->translateDataWriteReq(req); + + if (req->vaddr != unverifiedReq->vaddr) { + warn("Request virtual addresses do not match! Inst: %#x, checker:" + " %#x", + unverifiedReq->vaddr, req->vaddr); + } + req->paddr = unverifiedReq->paddr; + + if (checkFlags(req)) { + warn("Request flags do not match! Inst: %#x, checker: %#x", + unverifiedReq->flags, req->flags); + handleError(); + } +} + +bool +CheckerCPU::checkFlags(MemReqPtr &req) +{ + // Remove any dynamic flags that don't have to do with the request itself. + unsigned flags = unverifiedReq->flags; + unsigned mask = LOCKED | PHYSICAL | VPTE | ALTMODE | UNCACHEABLE | NO_FAULT; + flags = flags & (mask); + if (flags == req->flags) { + return false; + } else { + return true; + } +} + +/* start simulation, program loaded, processor precise state initialized */ +template <class DynInstPtr> +void +Checker<DynInstPtr>::tick(DynInstPtr &completed_inst) +{ + DynInstPtr inst; + + if (!instList.empty()) { + if (youngestSN < completed_inst->seqNum) { + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", + completed_inst->seqNum, completed_inst->readPC()); + instList.push_back(completed_inst); + youngestSN = completed_inst->seqNum; + } + + if (!instList.front()->isCompleted()) { + return; + } else { + inst = instList.front(); + instList.pop_front(); + } + } else { + if (!completed_inst->isCompleted()) { + if (youngestSN < completed_inst->seqNum) { + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", + completed_inst->seqNum, completed_inst->readPC()); + instList.push_back(completed_inst); + youngestSN = completed_inst->seqNum; + } + return; + } else { + if (youngestSN < completed_inst->seqNum) { + inst = completed_inst; + youngestSN = completed_inst->seqNum; + } else { +// panic("SN already seen yet the list is empty!"); + return; + } + } + } + + while (1) { + DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%#x.\n", + inst->seqNum, inst->readPC()); +// verifyInst = completed_inst; + unverifiedResult.integer = inst->readIntResult(); + unverifiedReq = inst->req; + numCycles++; + + Fault fault = NoFault; + + // maintain $r0 semantics + cpuXC->setIntReg(ZeroReg, 0); +#ifdef TARGET_ALPHA + cpuXC->setFloatRegDouble(ZeroReg, 0.0); +#endif // TARGET_ALPHA + + // Try to fetch an instruction + + // set up memory request for instruction fetch +#if FULL_SYSTEM +#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 +#else +#define IFETCH_FLAGS(pc) 0 +#endif + + if (changedPC) { + DPRINTF(Checker, "Changed PC recently to %#x\n", + cpuXC->readPC()); + if (willChangePC) { + if (newPC == cpuXC->readPC()) { + DPRINTF(Checker, "Changed PC matches expected PC\n"); + } else { + warn("Changed PC does not match expected PC, changed: %#x, " + "expected: %#x", + cpuXC->readPC(), newPC); + handleError(); + } + willChangePC = false; + } + changedPC = false; + } + if (changedNextPC) { + DPRINTF(Checker, "Changed NextPC recently to %#x\n", + cpuXC->readNextPC()); + changedNextPC = false; + } + + memReq->cmd = Read; + memReq->reset(cpuXC->readPC() & ~3, sizeof(uint32_t), + IFETCH_FLAGS(cpuXC->readPC())); + + bool succeeded = translateInstReq(memReq); + + if (!succeeded) { + warn("Instruction PC %#x was not found in the ITB!", + cpuXC->readPC()); + handleError(); + + // go to the next instruction + cpuXC->setPC(cpuXC->readNextPC()); + cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst)); + + return; + } + +// if (fault == NoFault) +// fault = cpuXC->mem->read(memReq, machInst); + cpuXC->mem->read(memReq, machInst); + + // If we've got a valid instruction (i.e., no fault on instruction + // fetch), then execute it. + + // keep an instruction count + numInst++; +// numInsts++; + + // decode the instruction + machInst = gtoh(machInst); + // Checks that the instruction matches what we expected it to be. + // Checks both the machine instruction and the PC. + validateInst(inst); + + curStaticInst = StaticInst::decode(makeExtMI(machInst, cpuXC->readPC())); + +#if FULL_SYSTEM + cpuXC->setInst(machInst); +#endif // FULL_SYSTEM + + fault = inst->getFault(); + + // Either the instruction was a fault and we should process the fault, + // or we should just go ahead execute the instruction. This assumes + // that the instruction is properly marked as a fault. + if (fault == NoFault) { + + cpuXC->func_exe_inst++; + + fault = curStaticInst->execute(this, NULL); + + // Checks to make sure instrution results are correct. + validateExecution(inst); + +// if (curStaticInst->isMemRef()) { +// numMemRefs++; +// } + + if (curStaticInst->isLoad()) { + ++numLoad; + } + } + + if (fault != NoFault) { +#if FULL_SYSTEM + fault->invoke(xcProxy); + willChangePC = true; + newPC = cpuXC->readPC(); + DPRINTF(Checker, "Fault, PC is now %#x\n", newPC); +#else // !FULL_SYSTEM + fatal("fault (%d) detected @ PC 0x%08p", fault, cpuXC->readPC()); +#endif // FULL_SYSTEM + } else { +#if THE_ISA != MIPS_ISA + // go to the next instruction + cpuXC->setPC(cpuXC->readNextPC()); + cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst)); +#else + // go to the next instruction + cpuXC->setPC(cpuXC->readNextPC()); + cpuXC->setNextPC(cpuXC->readNextNPC()); + cpuXC->setNextNPC(cpuXC->readNextNPC() + sizeof(MachInst)); +#endif + + } + +#if FULL_SYSTEM + Addr oldpc; + int count = 0; + do { + oldpc = cpuXC->readPC(); + system->pcEventQueue.service(xcProxy); + count++; + } while (oldpc != cpuXC->readPC()); + if (count > 1) { + willChangePC = true; + newPC = cpuXC->readPC(); + DPRINTF(Checker, "PC Event, PC is now %#x\n", newPC); + } +#endif + + // Checks PC, next PC. Optionally can check all registers. (Or just those + // that have been modified). + validateState(); + + if (instList.empty()) { + break; + } else if (instList.front()->isCompleted()) { + inst = instList.front(); + instList.pop_front(); + } else { + break; + } + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::switchOut(Sampler *s) +{ + sampler = s; + instList.clear(); +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::takeOverFrom(BaseCPU *oldCPU) +{ +// BaseCPU::takeOverFrom(oldCPU); + + // if any of this CPU's ExecContexts are active, mark the CPU as + // running and schedule its tick event. +/* + for (int i = 0; i < execContexts.size(); ++i) { + ExecContext *xc = execContexts[i]; + } +*/ +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateInst(DynInstPtr &inst) +{ + if (inst->readPC() != cpuXC->readPC()) { + warn("PCs do not match! Inst: %#x, checker: %#x", + inst->readPC(), cpuXC->readPC()); + if (changedPC) { + warn("Changed PCs recently, may not be an error"); + } else { + handleError(); + } + } + + if (static_cast<MachInst>(inst->staticInst->machInst) != + machInst) { + warn("Binary instructions do not match! Inst: %#x, checker: %#x", + static_cast<MachInst>(inst->staticInst->machInst), + machInst); + handleError(); + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateExecution(DynInstPtr &inst) +{ + if (inst->numDestRegs()) { + if (inst->isUnverifiable()) { + // @todo: Support more destination registers. + // Grab the result from the instruction and write it to the + // register. + RegIndex idx = inst->destRegIdx(0); + if (idx < TheISA::FP_Base_DepTag) { + cpuXC->setIntReg(idx, inst->readIntResult()); + } else if (idx < TheISA::Fpcr_DepTag) { + cpuXC->setFloatRegInt(idx, inst->readIntResult()); + } else { + cpuXC->setMiscReg(idx, inst->readIntResult()); + } + } else if (result.integer != inst->readIntResult()) { + warn("Instruction results do not match! (May not be integer results) " + "Inst: %#x, checker: %#x", + inst->readIntResult(), result.integer); + handleError(); + } + } + + if (inst->readNextPC() != cpuXC->readNextPC()) { + warn("Instruction next PCs do not match! Inst: %#x, checker: %#x", + inst->readNextPC(), cpuXC->readNextPC()); + handleError(); + } + + // Checking side effect registers can be difficult if they are not + // checked simultaneously with the execution of the instruction. + // This is because other valid instructions may have modified + // these registers in the meantime, and their values are not + // stored within the DynInst. + while (!miscRegIdxs.empty()) { + int misc_reg_idx = miscRegIdxs.front(); + miscRegIdxs.pop(); + + if (inst->xcBase()->readMiscReg(misc_reg_idx) != + cpuXC->readMiscReg(misc_reg_idx)) { + warn("Misc reg idx %i (side effect) does not match! Inst: %#x, " + "checker: %#x", + misc_reg_idx, inst->xcBase()->readMiscReg(misc_reg_idx), + cpuXC->readMiscReg(misc_reg_idx)); + handleError(); + } + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateState() +{ +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::dumpInsts() +{ + int num = 0; + + InstListIt inst_list_it = --(instList.end()); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Completed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isCompleted()); + + cprintf("\n"); + + inst_list_it--; + ++num; + } + +} + +template +class Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > >; + +template +class Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >; diff --git a/cpu/checker/cpu.hh b/cpu/checker/cpu.hh new file mode 100644 index 000000000..678e888df --- /dev/null +++ b/cpu/checker/cpu.hh @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2002-2005 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. + */ + +#ifndef __CPU_CHECKER_CPU_HH__ +#define __CPU_CHECKER_CPU_HH__ + +#include <list> +#include <queue> +#include <map> + +#include "base/statistics.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/cpu_exec_context.hh" +#include "cpu/pc_event.hh" +#include "cpu/sampler/sampler.hh" +#include "cpu/static_inst.hh" +#include "sim/eventq.hh" + +// forward declarations +#if FULL_SYSTEM +class Processor; +class AlphaITB; +class AlphaDTB; +class PhysicalMemory; + +class RemoteGDB; +class GDBListener; + +#else + +class Process; + +#endif // FULL_SYSTEM +template <class> +class BaseDynInst; +class ExecContext; +class MemInterface; +class Checkpoint; + +class CheckerCPU : public BaseCPU +{ + protected: + typedef TheISA::MachInst MachInst; + typedef TheISA::MiscReg MiscReg; + public: + // main simulation loop (one cycle) + virtual void init(); + + struct Params : public BaseCPU::Params + { +#if FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; + FunctionalMemory *mem; +#else + Process *process; +#endif + bool exitOnError; + }; + + public: + void post_interrupt(int int_num, int index); + + CheckerCPU(Params *p); + virtual ~CheckerCPU(); + + void setMemory(FunctionalMemory *mem); + + FunctionalMemory *memPtr; + +#if FULL_SYSTEM + void setSystem(System *system); + + System *systemPtr; +#endif + public: + // execution context + CPUExecContext *cpuXC; + + ExecContext *xcProxy; + + AlphaITB *itb; + AlphaDTB *dtb; + +#if FULL_SYSTEM + Addr dbg_vtophys(Addr addr); + + bool interval_stats; +#endif + + union Result { + uint64_t integer; + float fp; + double dbl; + }; + + Result result; + + // current instruction + MachInst machInst; + + // Refcounted pointer to the one memory request. + MemReqPtr memReq; + + // Pointer to the sampler that is telling us to switchover. + // Used to signal the completion of the pipe drain and schedule + // the next switchover + Sampler *sampler; + + StaticInstPtr curStaticInst; + + // number of simulated instructions + Counter numInst; + Counter startNumInst; + + std::queue<int> miscRegIdxs; + + virtual Counter totalInstructions() const + { + return numInst - startNumInst; + } + + // number of simulated loads + Counter numLoad; + Counter startNumLoad; + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + // These functions are only used in CPU models that split + // effective address computation from the actual memory access. + void setEA(Addr EA) { panic("SimpleCPU::setEA() not implemented\n"); } + Addr getEA() { panic("SimpleCPU::getEA() not implemented\n"); } + + void prefetch(Addr addr, unsigned flags) + { + // need to do this... + } + + void writeHint(Addr addr, int size, unsigned flags) + { + // need to do this... + } + + Fault copySrcTranslate(Addr src); + + Fault copy(Addr dest); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return cpuXC->readIntReg(si->srcRegIdx(idx)); + } + + float readFloatRegSingle(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return cpuXC->readFloatRegSingle(reg_idx); + } + + double readFloatRegDouble(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return cpuXC->readFloatRegDouble(reg_idx); + } + + uint64_t readFloatRegInt(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return cpuXC->readFloatRegInt(reg_idx); + } + + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + cpuXC->setIntReg(si->destRegIdx(idx), val); + result.integer = val; + } + + void setFloatRegSingle(const StaticInst *si, int idx, float val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + cpuXC->setFloatRegSingle(reg_idx, val); + result.fp = val; + } + + void setFloatRegDouble(const StaticInst *si, int idx, double val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + cpuXC->setFloatRegDouble(reg_idx, val); + result.dbl = val; + } + + void setFloatRegInt(const StaticInst *si, int idx, uint64_t val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + cpuXC->setFloatRegInt(reg_idx, val); + result.integer = val; + } + + uint64_t readPC() { return cpuXC->readPC(); } + void setNextPC(uint64_t val) { + cpuXC->setNextPC(val); + } + + MiscReg readMiscReg(int misc_reg) + { + return cpuXC->readMiscReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return cpuXC->readMiscRegWithEffect(misc_reg, fault); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + result.integer = val; + miscRegIdxs.push(misc_reg); + return cpuXC->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + miscRegIdxs.push(misc_reg); + return cpuXC->setMiscRegWithEffect(misc_reg, val); + } + + void recordPCChange(uint64_t val) { changedPC = true; } + void recordNextPCChange(uint64_t val) { changedNextPC = true; } + + bool translateInstReq(MemReqPtr &req); + void translateDataWriteReq(MemReqPtr &req); + void translateDataReadReq(MemReqPtr &req); + +#if FULL_SYSTEM + Fault hwrei() { return cpuXC->hwrei(); } + int readIntrFlag() { return cpuXC->readIntrFlag(); } + void setIntrFlag(int val) { cpuXC->setIntrFlag(val); } + bool inPalMode() { return cpuXC->inPalMode(); } + void ev5_trap(Fault fault) { fault->invoke(xcProxy); } + bool simPalCheck(int palFunc) { return cpuXC->simPalCheck(palFunc); } +#else + // Assume that the normal CPU's call to syscall was successful. + void syscall() { } +#endif + + void handleError() + { + if (exitOnError) + panic("Checker found error!"); + } + bool checkFlags(MemReqPtr &req); + + ExecContext *xcBase() { return xcProxy; } + CPUExecContext *cpuXCBase() { return cpuXC; } + + Result unverifiedResult; + MemReqPtr unverifiedReq; + + bool changedPC; + bool willChangePC; + uint64_t newPC; + bool changedNextPC; + bool exitOnError; + + InstSeqNum youngestSN; +// std::map<Addr, uint64_t> storeBuff; +// typedef std::map<Addr, uint64_t>::iterator map_it; +}; + +template <class DynInstPtr> +class Checker : public CheckerCPU +{ + public: + Checker(Params *p) + : CheckerCPU(p) + { } + + void switchOut(Sampler *s); + void takeOverFrom(BaseCPU *oldCPU); + + void tick(DynInstPtr &inst); + + void validateInst(DynInstPtr &inst); + void validateExecution(DynInstPtr &inst); + void validateState(); + + std::list<DynInstPtr> instList; + typedef typename std::list<DynInstPtr>::iterator InstListIt; + void dumpInsts(); +}; + +#endif // __CPU_CHECKER_CPU_HH__ diff --git a/cpu/checker/cpu_builder.cc b/cpu/checker/cpu_builder.cc new file mode 100644 index 000000000..397ccab14 --- /dev/null +++ b/cpu/checker/cpu_builder.cc @@ -0,0 +1,126 @@ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "mem/base_mem.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class OzoneChecker : public Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > > +{ + public: + OzoneChecker(Params *p) + : Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > >(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// CheckerCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(OzoneChecker) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<FunctionalMemory *> mem; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + Param<int> clock; + SimObjectParam<BaseMem *> icache; + SimObjectParam<BaseMem *> dcache; + + Param<bool> defer_registration; + Param<bool> exitOnError; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(OzoneChecker) + +BEGIN_INIT_SIM_OBJECT_PARAMS(OzoneChecker) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(mem, "memory"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(icache, "L1 instruction cache object"), + INIT_PARAM(dcache, "L1 data cache object"), + + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(exitOnError, "exit on error"), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(OzoneChecker) + + +CREATE_SIM_OBJECT(OzoneChecker) +{ + OzoneChecker::Params *params = new OzoneChecker::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->exitOnError = exitOnError; + params->deferRegistration = defer_registration; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + BaseMem *cache = icache; + cache = dcache; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->mem = mem; + params->system = system; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + OzoneChecker *cpu = new OzoneChecker(params); + return cpu; +} + +REGISTER_SIM_OBJECT("OzoneChecker", OzoneChecker) diff --git a/cpu/checker/exec_context.hh b/cpu/checker/exec_context.hh new file mode 100644 index 000000000..4843d1cf0 --- /dev/null +++ b/cpu/checker/exec_context.hh @@ -0,0 +1,225 @@ +#ifndef __CPU_CHECKER_EXEC_CONTEXT_HH__ +#define __CPU_CHECKER_EXEC_CONTEXT_HH__ + +#include "cpu/checker/cpu.hh" +#include "cpu/cpu_exec_context.hh" +#include "cpu/exec_context.hh" + +class EndQuiesceEvent; + +template <class XC> +class CheckerExecContext : public ExecContext +{ + public: + CheckerExecContext(XC *actual_xc, + CheckerCPU *checker_cpu) + : actualXC(actual_xc), checkerXC(checker_cpu->cpuXC), checkerCPU(checker_cpu) + { } + + private: + XC *actualXC; + CPUExecContext *checkerXC; + CheckerCPU *checkerCPU; + + public: + + BaseCPU *getCpuPtr() { return actualXC->getCpuPtr(); } + + void setCpuId(int id) + { + actualXC->setCpuId(id); + checkerXC->setCpuId(id); + } + + int readCpuId() { return actualXC->readCpuId(); } + + FunctionalMemory *getMemPtr() { return actualXC->getMemPtr(); } + +#if FULL_SYSTEM + System *getSystemPtr() { return actualXC->getSystemPtr(); } + + PhysicalMemory *getPhysMemPtr() { return actualXC->getPhysMemPtr(); } + + AlphaITB *getITBPtr() { return actualXC->getITBPtr(); } + + AlphaDTB *getDTBPtr() { return actualXC->getDTBPtr(); } +#else + Process *getProcessPtr() { return actualXC->getProcessPtr(); } +#endif + + Status status() const { return actualXC->status(); } + + void setStatus(Status new_status) + { actualXC->setStatus(new_status); + checkerXC->setStatus(new_status); } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1) { actualXC->activate(delay); } + + /// Set the status to Suspended. + void suspend() { actualXC->suspend(); } + + /// Set the status to Unallocated. + void deallocate() { actualXC->deallocate(); } + + /// Set the status to Halted. + void halt() { actualXC->halt(); } + +#if FULL_SYSTEM + void dumpFuncProfile() { actualXC->dumpFuncProfile(); } +#endif + + void takeOverFrom(ExecContext *oldContext) + { + actualXC->takeOverFrom(oldContext); + checkerXC->takeOverFrom(oldContext); + } + + void regStats(const std::string &name) { actualXC->regStats(name); } + + void serialize(std::ostream &os) { actualXC->serialize(os); } + void unserialize(Checkpoint *cp, const std::string §ion) + { actualXC->unserialize(cp, section); } + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent() { return actualXC->getQuiesceEvent(); } + + Tick readLastActivate() { return actualXC->readLastActivate(); } + Tick readLastSuspend() { return actualXC->readLastSuspend(); } + + void profileClear() { return actualXC->profileClear(); } + void profileSample() { return actualXC->profileSample(); } +#endif + + int getThreadNum() { return actualXC->getThreadNum(); } + + // @todo: Do I need this? + MachInst getInst() { return actualXC->getInst(); } + + // @todo: Do I need this? + void copyArchRegs(ExecContext *xc) + { + actualXC->copyArchRegs(xc); + checkerXC->copyArchRegs(xc); + } + + void clearArchRegs() + { + actualXC->clearArchRegs(); + checkerXC->clearArchRegs(); + } + + // + // New accessors for new decoder. + // + uint64_t readIntReg(int reg_idx) + { return actualXC->readIntReg(reg_idx); } + + float readFloatRegSingle(int reg_idx) + { return actualXC->readFloatRegSingle(reg_idx); } + + double readFloatRegDouble(int reg_idx) + { return actualXC->readFloatRegDouble(reg_idx); } + + uint64_t readFloatRegInt(int reg_idx) + { return actualXC->readFloatRegInt(reg_idx); } + + void setIntReg(int reg_idx, uint64_t val) + { + actualXC->setIntReg(reg_idx, val); + checkerXC->setIntReg(reg_idx, val); + } + + void setFloatRegSingle(int reg_idx, float val) + { + actualXC->setFloatRegSingle(reg_idx, val); + checkerXC->setFloatRegSingle(reg_idx, val); + } + + void setFloatRegDouble(int reg_idx, double val) + { + actualXC->setFloatRegDouble(reg_idx, val); + checkerXC->setFloatRegSingle(reg_idx, val); + } + + void setFloatRegInt(int reg_idx, uint64_t val) + { + actualXC->setFloatRegInt(reg_idx, val); + checkerXC->setFloatRegInt(reg_idx, val); + } + + uint64_t readPC() { return actualXC->readPC(); } + + void setPC(uint64_t val) + { + actualXC->setPC(val); + checkerXC->setPC(val); + checkerCPU->recordPCChange(val); + } + + uint64_t readNextPC() { return actualXC->readNextPC(); } + + void setNextPC(uint64_t val) + { + actualXC->setNextPC(val); + checkerXC->setNextPC(val); + checkerCPU->recordNextPCChange(val); + } + + MiscReg readMiscReg(int misc_reg) + { return actualXC->readMiscReg(misc_reg); } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return actualXC->readMiscRegWithEffect(misc_reg, fault); } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + checkerXC->setMiscReg(misc_reg, val); + return actualXC->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + checkerXC->setMiscRegWithEffect(misc_reg, val); + return actualXC->setMiscRegWithEffect(misc_reg, val); + } + + unsigned readStCondFailures() + { return actualXC->readStCondFailures(); } + + void setStCondFailures(unsigned sc_failures) + { + checkerXC->setStCondFailures(sc_failures); + actualXC->setStCondFailures(sc_failures); + } +#if FULL_SYSTEM + bool inPalMode() { return actualXC->inPalMode(); } +#endif + + // @todo: Fix this! + bool misspeculating() { return actualXC->misspeculating(); } + +#if !FULL_SYSTEM + IntReg getSyscallArg(int i) { return actualXC->getSyscallArg(i); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, IntReg val) + { + checkerXC->setSyscallArg(i, val); + actualXC->setSyscallArg(i, val); + } + + void setSyscallReturn(SyscallReturn return_value) + { + checkerXC->setSyscallReturn(return_value); + actualXC->setSyscallReturn(return_value); + } + +// void syscall() { actualXC->syscall(); } + + Counter readFuncExeInst() { return actualXC->readFuncExeInst(); } +#endif +}; + +#endif // __CPU_CHECKER_EXEC_CONTEXT_HH__ diff --git a/cpu/checker/o3_cpu_builder.cc b/cpu/checker/o3_cpu_builder.cc new file mode 100644 index 000000000..125bfa398 --- /dev/null +++ b/cpu/checker/o3_cpu_builder.cc @@ -0,0 +1,126 @@ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/alpha_dyn_inst.hh" +#include "cpu/o3/alpha_impl.hh" +#include "mem/base_mem.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class O3Checker : public Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > > +{ + public: + O3Checker(Params *p) + : Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// CheckerCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(O3Checker) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<FunctionalMemory *> mem; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + Param<int> clock; + SimObjectParam<BaseMem *> icache; + SimObjectParam<BaseMem *> dcache; + + Param<bool> defer_registration; + Param<bool> exitOnError; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(O3Checker) + +BEGIN_INIT_SIM_OBJECT_PARAMS(O3Checker) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(mem, "memory"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(icache, "L1 instruction cache object"), + INIT_PARAM(dcache, "L1 data cache object"), + + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(exitOnError, "exit on error"), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(O3Checker) + + +CREATE_SIM_OBJECT(O3Checker) +{ + O3Checker::Params *params = new O3Checker::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->exitOnError = exitOnError; + params->deferRegistration = defer_registration; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + BaseMem *cache = icache; + cache = dcache; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->mem = mem; + params->system = system; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + O3Checker *cpu = new O3Checker(params); + return cpu; +} + +REGISTER_SIM_OBJECT("O3Checker", O3Checker) diff --git a/cpu/cpu_models.py b/cpu/cpu_models.py index 8912673f7..2b1ae6277 100644 --- a/cpu/cpu_models.py +++ b/cpu/cpu_models.py @@ -74,4 +74,7 @@ CpuModel('OzoneSimpleCPU', 'ozone_simple_exec.cc', CpuModel('OzoneCPU', 'ozone_exec.cc', '#include "cpu/ozone/dyn_inst.hh"', { 'CPU_exec_context': 'OzoneDynInst<OzoneImpl>' }) +CpuModel('CheckerCPU', 'checker_cpu_exec.cc', + '#include "cpu/checker/cpu.hh"', + { 'CPU_exec_context': 'CheckerCPU' }) |