diff options
Diffstat (limited to 'cpu')
-rw-r--r-- | cpu/base_cpu.cc | 157 | ||||
-rw-r--r-- | cpu/base_cpu.hh | 122 | ||||
-rw-r--r-- | cpu/exec_context.cc | 106 | ||||
-rw-r--r-- | cpu/exec_context.hh | 359 | ||||
-rw-r--r-- | cpu/exetrace.cc | 190 | ||||
-rw-r--r-- | cpu/exetrace.hh | 197 | ||||
-rw-r--r-- | cpu/full_cpu/op_class.hh | 64 | ||||
-rw-r--r-- | cpu/full_cpu/smt.hh | 64 | ||||
-rw-r--r-- | cpu/inst_seq.hh | 40 | ||||
-rw-r--r-- | cpu/intr_control.cc | 59 | ||||
-rw-r--r-- | cpu/intr_control.hh | 61 | ||||
-rw-r--r-- | cpu/memtest/memtest.cc | 324 | ||||
-rw-r--r-- | cpu/memtest/memtest.hh | 140 | ||||
-rw-r--r-- | cpu/pc_event.cc | 231 | ||||
-rw-r--r-- | cpu/pc_event.hh | 215 | ||||
-rw-r--r-- | cpu/simple_cpu/simple_cpu.cc | 746 | ||||
-rw-r--r-- | cpu/simple_cpu/simple_cpu.hh | 280 | ||||
-rw-r--r-- | cpu/static_inst.cc | 88 | ||||
-rw-r--r-- | cpu/static_inst.hh | 433 |
19 files changed, 3876 insertions, 0 deletions
diff --git a/cpu/base_cpu.cc b/cpu/base_cpu.cc new file mode 100644 index 000000000..06b2ec65c --- /dev/null +++ b/cpu/base_cpu.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2003 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 <string> +#include <sstream> +#include <iostream> + +#include "base_cpu.hh" +#include "cprintf.hh" +#include "exec_context.hh" +#include "misc.hh" +#include "sim_events.hh" + +using namespace std; + +vector<BaseCPU *> BaseCPU::cpuList; + +// This variable reflects the max number of threads in any CPU. Be +// careful to only use it once all the CPUs that you care about have +// been initialized +int maxThreadsPerCPU = 1; + +#ifdef FULL_SYSTEM +BaseCPU::BaseCPU(const string &_name, int _number_of_threads, + Counter max_insts_any_thread, + Counter max_insts_all_threads, + System *_system, int num, Tick freq) + : SimObject(_name), number(num), frequency(freq), + number_of_threads(_number_of_threads), system(_system) +#else +BaseCPU::BaseCPU(const string &_name, int _number_of_threads, + Counter max_insts_any_thread, + Counter max_insts_all_threads) + : SimObject(_name), number_of_threads(_number_of_threads) +#endif +{ + // add self to global list of CPUs + cpuList.push_back(this); + + if (number_of_threads > maxThreadsPerCPU) + maxThreadsPerCPU = number_of_threads; + + // allocate per-thread instruction-based event queues + comInsnEventQueue = new (EventQueue *)[number_of_threads]; + for (int i = 0; i < number_of_threads; ++i) + comInsnEventQueue[i] = new EventQueue("instruction-based event queue"); + + // + // set up instruction-count-based termination events, if any + // + if (max_insts_any_thread != 0) + for (int i = 0; i < number_of_threads; ++i) + new SimExitEvent(comInsnEventQueue[i], max_insts_any_thread, + "a thread reached the max instruction count"); + + if (max_insts_all_threads != 0) { + // allocate & initialize shared downcounter: each event will + // decrement this when triggered; simulation will terminate + // when counter reaches 0 + int *counter = new int; + *counter = number_of_threads; + for (int i = 0; i < number_of_threads; ++i) + new CountedExitEvent(comInsnEventQueue[i], + "all threads reached the max instruction count", + max_insts_all_threads, *counter); + } + +#ifdef FULL_SYSTEM + memset(interrupts, 0, sizeof(interrupts)); + intstatus = 0; +#endif +} + +void +BaseCPU::regStats() +{ + int size = contexts.size(); + if (size > 1) { + for (int i = 0; i < size; ++i) { + stringstream namestr; + ccprintf(namestr, "%s.ctx%d", name(), i); + contexts[i]->regStats(namestr.str()); + } + } else if (size == 1) + contexts[0]->regStats(name()); +} + +#ifdef FULL_SYSTEM +void +BaseCPU::post_interrupt(int int_num, int index) +{ + DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index); + + if (int_num < 0 || int_num >= NumInterruptLevels) + panic("int_num out of bounds\n"); + + if (index < 0 || index >= sizeof(uint8_t) * 8) + panic("int_num out of bounds\n"); + + AlphaISA::check_interrupts = 1; + interrupts[int_num] |= 1 << index; + intstatus |= (ULL(1) << int_num); +} + +void +BaseCPU::clear_interrupt(int int_num, int index) +{ + DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index); + + if (int_num < 0 || int_num >= NumInterruptLevels) + panic("int_num out of bounds\n"); + + if (index < 0 || index >= sizeof(uint8_t) * 8) + panic("int_num out of bounds\n"); + + interrupts[int_num] &= ~(1 << index); + if (interrupts[int_num] == 0) + intstatus &= ~(ULL(1) << int_num); +} + +void +BaseCPU::clear_interrupts() +{ + DPRINTF(Interrupt, "Interrupts all cleared\n"); + + memset(interrupts, 0, sizeof(interrupts)); + intstatus = 0; +} + +#endif // FULL_SYSTEM + +DEFINE_SIM_OBJECT_CLASS_NAME("BaseCPU", BaseCPU) diff --git a/cpu/base_cpu.hh b/cpu/base_cpu.hh new file mode 100644 index 000000000..745220d85 --- /dev/null +++ b/cpu/base_cpu.hh @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2003 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 __BASE_CPU_HH__ +#define __BASE_CPU_HH__ + +#include <vector> + +#include "eventq.hh" +#include "sim_object.hh" + +#include "isa_traits.hh" // for Addr + +#ifdef FULL_SYSTEM +class System; +#endif + +class BranchPred; +class ExecContext; + +class BaseCPU : public SimObject +{ +#ifdef FULL_SYSTEM + protected: + int number; + Tick frequency; + uint8_t interrupts[NumInterruptLevels]; + uint64_t intstatus; + + public: + virtual void post_interrupt(int int_num, int index); + virtual void clear_interrupt(int int_num, int index); + virtual void clear_interrupts(); + + bool check_interrupt(int int_num) const { + if (int_num > NumInterruptLevels) + panic("int_num out of bounds\n"); + + return interrupts[int_num] != 0; + } + + bool check_interrupts() const { return intstatus != 0; } + uint64_t intr_status() const { return intstatus; } + + Tick getFreq() const { return frequency; } +#endif + + protected: + std::vector<ExecContext *> contexts; + + public: + virtual void execCtxStatusChg() {} + + public: + +#ifdef FULL_SYSTEM + BaseCPU(const std::string &_name, int _number_of_threads, + Counter max_insts_any_thread, Counter max_insts_all_threads, + System *_system, + int num, Tick freq); +#else + BaseCPU(const std::string &_name, int _number_of_threads, + Counter max_insts_any_thread = 0, + Counter max_insts_all_threads = 0); +#endif + + virtual ~BaseCPU() {} + + virtual void regStats(); + + /// Number of threads we're actually simulating (<= SMT_MAX_THREADS). + /// This is a constant for the duration of the simulation. + int number_of_threads; + + /// Vector of per-thread instruction-based event queues. Used for + /// scheduling events based on number of instructions committed by + /// a particular thread. + EventQueue **comInsnEventQueue; + +#ifdef FULL_SYSTEM + System *system; +#endif + + virtual bool filterThisInstructionPrefetch(int thread_number, + short asid, Addr prefetchTarget) const { return true; } + + /// Return pointer to CPU's branch predictor (NULL if none). + virtual BranchPred *getBranchPred() { return NULL; }; + + private: + static std::vector<BaseCPU *> cpuList; //!< Static global cpu list + + public: + static int numSimulatedCPUs() { return cpuList.size(); } +}; + +#endif // __BASE_CPU_HH__ diff --git a/cpu/exec_context.cc b/cpu/exec_context.cc new file mode 100644 index 000000000..c81d172a8 --- /dev/null +++ b/cpu/exec_context.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2003 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 <string> + +#include "base_cpu.hh" +#include "exec_context.hh" + +#ifdef FULL_SYSTEM +#include "system.hh" +#else +#include "prog.hh" +#endif + +using namespace std; + +// constructor +#ifdef FULL_SYSTEM +ExecContext::ExecContext(BaseCPU *_cpu, int _thread_num, System *_sys, + AlphaItb *_itb, AlphaDtb *_dtb, + FunctionalMemory *_mem, int _cpu_id) + : kernelStats(this, _cpu), cpu(_cpu), thread_num(_thread_num), mem(_mem), + itb(_itb), dtb(_dtb), cpu_id(_cpu_id), system(_sys), + memCtrl(_sys->memCtrl), physmem(_sys->physmem) +{ + memset(®s, 0, sizeof(RegFile)); + _status = Active; + func_exe_insn = 0; + storeCondFailures = 0; + system->registerExecContext(this); +} +#else +ExecContext::ExecContext(BaseCPU *_cpu, int _thread_num, + Process *_process, int _asid) + : cpu(_cpu), thread_num(_thread_num), process(_process), asid (_asid) +{ + + // Register with process object. Our 'active' will be set by the + // process iff we're the initial context. Others are reserved for + // dynamically created threads. + process->registerExecContext(this); + + mem = process->getMemory(); + + func_exe_insn = 0; + storeCondFailures = 0; +} + +ExecContext::ExecContext(BaseCPU *_cpu, int _thread_num, + FunctionalMemory *_mem, int _asid) + : cpu(_cpu), thread_num(_thread_num), process(NULL), mem(_mem), + asid(_asid) +{ +} +#endif + +void +ExecContext::setStatus(Status new_status) +{ +#ifdef FULL_SYSTEM + if (status() == new_status) + return; + + // Don't change the status from active if there are pending interrupts + if (new_status == Suspended && cpu->check_interrupts()) { + assert(status() == Active); + return; + } +#endif + + _status = new_status; + cpu->execCtxStatusChg(); +} + +void +ExecContext::regStats(const string &name) +{ +#ifdef FULL_SYSTEM + kernelStats.regStats(name + ".kern"); +#endif +} diff --git a/cpu/exec_context.hh b/cpu/exec_context.hh new file mode 100644 index 000000000..988673a0f --- /dev/null +++ b/cpu/exec_context.hh @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2003 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 __EXEC_CONTEXT_HH__ +#define __EXEC_CONTEXT_HH__ + +#include "host.hh" +#include "mem_req.hh" + +// forward declaration: see functional_memory.hh +class FunctionalMemory; +class PhysicalMemory; +class BaseCPU; + +#ifdef FULL_SYSTEM + +#include "alpha_memory.hh" +class MemoryController; + +#include "kernel_stats.hh" +#include "system.hh" + +#else // !FULL_SYSTEM + +#include "prog.hh" + +#endif // FULL_SYSTEM + +// +// The ExecContext object represents a functional context for +// instruction execution. It incorporates everything required for +// architecture-level functional simulation of a single thread. +// + +class ExecContext +{ + public: + enum Status { Unallocated, Active, Suspended, Halted }; + + private: + Status _status; + + public: + Status status() const { return _status; } + void setStatus(Status new_status); + +#ifdef FULL_SYSTEM + public: + KernelStats kernelStats; +#endif + + public: + RegFile regs; // correct-path register context + + // pointer to CPU associated with this context + BaseCPU *cpu; + + // Index of hardware thread context on the CPU that this represents. + int thread_num; + +#ifdef FULL_SYSTEM + + FunctionalMemory *mem; + AlphaItb *itb; + AlphaDtb *dtb; + int cpu_id; + System *system; + + // the following two fields are redundant, since we can always + // look them up through the system pointer, but we'll leave them + // here for now for convenience + MemoryController *memCtrl; + PhysicalMemory *physmem; + +#else + Process *process; + + FunctionalMemory *mem; // functional storage for process address space + + // Address space ID. Note that this is used for TIMING cache + // simulation only; all functional memory accesses should use + // one of the FunctionalMemory pointers above. + short asid; + +#endif + + + /* + * number of executed instructions, for matching with syscall trace + * points in EIO files. + */ + Counter func_exe_insn; + + // + // Count failed store conditionals so we can warn of apparent + // application deadlock situations. + unsigned storeCondFailures; + + // constructor: initialize context from given process structure +#ifdef FULL_SYSTEM + ExecContext(BaseCPU *_cpu, int _thread_num, System *_system, + AlphaItb *_itb, AlphaDtb *_dtb, FunctionalMemory *_dem, + int _cpu_id); +#else + ExecContext(BaseCPU *_cpu, int _thread_num, Process *_process, int _asid); + ExecContext(BaseCPU *_cpu, int _thread_num, FunctionalMemory *_mem, + int _asid); +#endif + virtual ~ExecContext() {} + + void regStats(const std::string &name); + +#ifdef FULL_SYSTEM + bool validInstAddr(Addr addr) { return true; } + bool validDataAddr(Addr addr) { return true; } + int getInstAsid() { return ITB_ASN_ASN(regs.ipr[TheISA::IPR_ITB_ASN]); } + int getDataAsid() { return DTB_ASN_ASN(regs.ipr[TheISA::IPR_DTB_ASN]); } + + Fault translateInstReq(MemReqPtr req) + { + return itb->translate(req); + } + + Fault translateDataReadReq(MemReqPtr req) + { + return dtb->translate(req, false); + } + + Fault translateDataWriteReq(MemReqPtr req) + { + return dtb->translate(req, true); + } + + +#else + bool validInstAddr(Addr addr) + { return process->validInstAddr(addr); } + + bool validDataAddr(Addr addr) + { return process->validDataAddr(addr); } + + int getInstAsid() { return asid; } + int getDataAsid() { return asid; } + + Fault dummyTranslation(MemReqPtr req) + { +#if 0 + assert((req->vaddr >> 48 & 0xffff) == 0); +#endif + + // put the asid in the upper 16 bits of the paddr + req->paddr = req->vaddr & ~((Addr)0xffff << sizeof(Addr) * 8 - 16); + req->paddr = req->paddr | (Addr)req->asid << sizeof(Addr) * 8 - 16; + return No_Fault; + } + Fault translateInstReq(MemReqPtr req) + { + return dummyTranslation(req); + } + Fault translateDataReadReq(MemReqPtr req) + { + return dummyTranslation(req); + } + Fault translateDataWriteReq(MemReqPtr req) + { + return dummyTranslation(req); + } + +#endif + + template <class T> + Fault read(MemReqPtr req, T& data) + { +#if defined(TARGET_ALPHA) && defined(FULL_SYSTEM) + if (req->flags & LOCKED) { + MiscRegFile *cregs = &req->xc->regs.miscRegs; + cregs->lock_addr = req->paddr; + cregs->lock_flag = true; + } +#endif + return mem->read(req, data); + } + + template <class T> + Fault write(MemReqPtr req, T& data) + { +#if defined(TARGET_ALPHA) && defined(FULL_SYSTEM) + + MiscRegFile *cregs; + + // If this is a store conditional, act appropriately + if (req->flags & LOCKED) { + cregs = &req->xc->regs.miscRegs; + + if (req->flags & UNCACHEABLE) { + // Don't update result register (see machine.def) + req->result = 2; + req->xc->storeCondFailures = 0;//Needed? [RGD] + } else { + req->result = cregs->lock_flag; + if (!cregs->lock_flag || + ((cregs->lock_addr & ~0xf) != (req->paddr & ~0xf))) { + cregs->lock_flag = false; + if (((++req->xc->storeCondFailures) % 100000) == 0) { + std::cerr << "Warning: " + << req->xc->storeCondFailures + << " consecutive store conditional failures " + << "on cpu " << req->xc->cpu_id + << std::endl; + } + return No_Fault; + } + else req->xc->storeCondFailures = 0; + } + } + + // Need to clear any locked flags on other proccessors for this + // address + // Only do this for succsful Store Conditionals and all other + // stores (WH64?) + // Unsuccesful Store Conditionals would have returned above, + // and wouldn't fall through + for(int i = 0; i < system->num_cpus; i++){ + cregs = &system->xc_array[i]->regs.miscRegs; + if((cregs->lock_addr & ~0xf) == (req->paddr & ~0xf)) { + cregs->lock_flag = false; + } + } + +#endif + return mem->write(req, data); + } + + virtual bool misspeculating(); + + + // + // New accessors for new decoder. + // + uint64_t readIntReg(int reg_idx) + { + return regs.intRegFile[reg_idx]; + } + + float readFloatRegSingle(int reg_idx) + { + return (float)regs.floatRegFile.d[reg_idx]; + } + + double readFloatRegDouble(int reg_idx) + { + return regs.floatRegFile.d[reg_idx]; + } + + uint64_t readFloatRegInt(int reg_idx) + { + return regs.floatRegFile.q[reg_idx]; + } + + void setIntReg(int reg_idx, uint64_t val) + { + regs.intRegFile[reg_idx] = val; + } + + void setFloatRegSingle(int reg_idx, float val) + { + regs.floatRegFile.d[reg_idx] = (double)val; + } + + void setFloatRegDouble(int reg_idx, double val) + { + regs.floatRegFile.d[reg_idx] = val; + } + + void setFloatRegInt(int reg_idx, uint64_t val) + { + regs.floatRegFile.q[reg_idx] = val; + } + + uint64_t readPC() + { + return regs.pc; + } + + void setNextPC(uint64_t val) + { + regs.npc = val; + } + + uint64_t readUniq() + { + return regs.miscRegs.uniq; + } + + void setUniq(uint64_t val) + { + regs.miscRegs.uniq = val; + } + + uint64_t readFpcr() + { + return regs.miscRegs.fpcr; + } + + void setFpcr(uint64_t val) + { + regs.miscRegs.fpcr = val; + } + +#ifdef FULL_SYSTEM + uint64_t readIpr(int idx, Fault &fault); + Fault setIpr(int idx, uint64_t val); + Fault hwrei(); + void ev5_trap(Fault fault); + bool simPalCheck(int palFunc); +#endif + +#ifndef FULL_SYSTEM + void syscall() + { + process->syscall(this); + } +#endif +}; + + +// for non-speculative execution context, spec_mode is always false +inline bool +ExecContext::misspeculating() +{ + return false; +} + +#endif // __EXEC_CONTEXT_HH__ diff --git a/cpu/exetrace.cc b/cpu/exetrace.cc new file mode 100644 index 000000000..4c5d14893 --- /dev/null +++ b/cpu/exetrace.cc @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2003 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 <fstream> +#include <iomanip> + +#include "dyn_inst.hh" +#include "spec_state.hh" +#include "issue.hh" +#include "exetrace.hh" +#include "exec_context.hh" +#include "symtab.hh" +#include "base_cpu.hh" +#include "static_inst.hh" + +using namespace std; + + +//////////////////////////////////////////////////////////////////////// +// +// Methods for the InstRecord object +// + + +const SymbolTable *debugSymbolTable = NULL; + +void +Trace::InstRecord::dump(ostream &outs) +{ + if (flags[PRINT_CYCLE]) + ccprintf(outs, "%7d: ", cycle); + + outs << cpu->name() << " "; + + if (flags[TRACE_MISSPEC]) + outs << (misspeculating ? "-" : "+") << " "; + + if (flags[PRINT_THREAD_NUM]) + outs << "T" << thread << " : "; + + outs << "0x" << hex << PC << " : "; + + // + // Print decoded instruction + // + +#if defined(__GNUC__) && (__GNUC__ < 3) + // There's a bug in gcc 2.x library that prevents setw() + // from working properly on strings + string mc(staticInst->disassemble(PC, debugSymbolTable)); + while (mc.length() < 25) + mc += " "; + outs << mc; +#else + outs << setw(25) << staticInst->disassemble(PC, debugSymbolTable); +#endif + + outs << " : "; + + if (flags[PRINT_OP_CLASS]) { + outs << opClassStrings[staticInst->opClass()] << " : "; + } + + if (flags[PRINT_RESULT_DATA] && data_status != DataInvalid) { + outs << " D="; +#if 0 + if (data_status == DataDouble) + ccprintf(outs, "%f", data.as_double); + else + ccprintf(outs, "%#018x", data.as_int); +#else + ccprintf(outs, "%#018x", data.as_int); +#endif + } + + if (flags[PRINT_EFF_ADDR] && addr_valid) + outs << " A=0x" << hex << addr; + + if (flags[PRINT_INT_REGS] && regs_valid) { + for (int i = 0; i < 32;) + for (int j = i + 1; i <= j; i++) + ccprintf(outs, "r%02d = %#018x%s", i, iregs->regs[i], + ((i == j) ? "\n" : " ")); + outs << "\n"; + } + + if (flags[PRINT_FETCH_SEQ] && fetch_seq_valid) + outs << " FetchSeq=" << dec << fetch_seq; + + if (flags[PRINT_CP_SEQ] && cp_seq_valid) + outs << " CPSeq=" << dec << cp_seq; + + // + // End of line... + // + outs << endl; + outs.flush(); +} + + +vector<bool> Trace::InstRecord::flags(NUM_BITS); + +//////////////////////////////////////////////////////////////////////// +// +// Parameter space for per-cycle execution address tracing options. +// Derive from ParamContext so we can override checkParams() function. +// +class ExecutionTraceParamContext : public ParamContext +{ + public: + ExecutionTraceParamContext(const string &_iniSection) + : ParamContext(_iniSection) + { + } + + void checkParams(); // defined at bottom of file +}; + +ExecutionTraceParamContext exeTraceParams("exetrace"); + +Param<bool> exe_trace_spec(&exeTraceParams, "speculative", + "capture speculative instructions", false); + +Param<bool> exe_trace_print_cycle(&exeTraceParams, "print_cycle", + "print cycle number", true); +Param<bool> exe_trace_print_opclass(&exeTraceParams, "print_opclass", + "print op class", true); +Param<bool> exe_trace_print_thread(&exeTraceParams, "print_thread", + "print thread number", true); +Param<bool> exe_trace_print_effaddr(&exeTraceParams, "print_effaddr", + "print effective address", true); +Param<bool> exe_trace_print_data(&exeTraceParams, "print_data", + "print result data", true); +Param<bool> exe_trace_print_iregs(&exeTraceParams, "print_iregs", + "print all integer regs", false); +Param<bool> exe_trace_print_fetchseq(&exeTraceParams, "print_fetchseq", + "print fetch sequence number", false); +Param<bool> exe_trace_print_cp_seq(&exeTraceParams, "print_cpseq", + "print correct-path sequence number", false); + +// +// Helper function for ExecutionTraceParamContext::checkParams() just +// to get us into the InstRecord namespace +// +void +Trace::InstRecord::setParams() +{ + flags[TRACE_MISSPEC] = exe_trace_spec; + + flags[PRINT_CYCLE] = exe_trace_print_cycle; + flags[PRINT_OP_CLASS] = exe_trace_print_opclass; + flags[PRINT_THREAD_NUM] = exe_trace_print_thread; + flags[PRINT_RESULT_DATA] = exe_trace_print_effaddr; + flags[PRINT_EFF_ADDR] = exe_trace_print_data; + flags[PRINT_INT_REGS] = exe_trace_print_iregs; + flags[PRINT_FETCH_SEQ] = exe_trace_print_fetchseq; + flags[PRINT_CP_SEQ] = exe_trace_print_cp_seq; +} + +void +ExecutionTraceParamContext::checkParams() +{ + Trace::InstRecord::setParams(); +} + diff --git a/cpu/exetrace.hh b/cpu/exetrace.hh new file mode 100644 index 000000000..2eb7753e5 --- /dev/null +++ b/cpu/exetrace.hh @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2003 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 __EXETRACE_HH__ +#define __EXETRACE_HH__ + +#include <fstream> +#include <vector> + +#include "host.hh" +#include "inst_seq.hh" // for InstSeqNum +#include "trace.hh" +#include "exec_context.hh" +#include "static_inst.hh" + +class BaseCPU; + + +namespace Trace { + +#if 0 + static const FlagVec ALL = ULL(0x1); + static const FlagVec FULL = ULL(0x2); + static const FlagVec SYMBOLS = ULL(0x4); + static const FlagVec EXTENDED = ULL(0x8); + static const FlagVec BRANCH_TAKEN = ULL(0x10); + static const FlagVec BRANCH_NOTTAKEN = ULL(0x20); + static const FlagVec CALLPAL = ULL(0x40); + static const FlagVec SPECULATIVE = ULL(0x100); + static const FlagVec OMIT_COUNT = ULL(0x200); + static const FlagVec INCLUDE_THREAD_NUM = ULL(0x400); +#endif + +class InstRecord : public Record +{ + protected: + + // The following fields are initialized by the constructor and + // thus guaranteed to be valid. + BaseCPU *cpu; + // need to make this ref-counted so it doesn't go away before we + // dump the record + StaticInstPtr<TheISA> staticInst; + Addr PC; + bool misspeculating; + unsigned thread; + + // The remaining fields are only valid for particular instruction + // types (e.g, addresses for memory ops) or when particular + // options are enabled (e.g., tracing full register contents). + // Each data field has an associated valid flag to indicate + // whether the data field is valid. + Addr addr; + bool addr_valid; + + union { + uint64_t as_int; + double as_double; + } data; + enum { + DataInvalid = 0, + DataInt8 = 1, // set to equal number of bytes + DataInt16 = 2, + DataInt32 = 4, + DataInt64 = 8, + DataDouble = 3 + } data_status; + + InstSeqNum fetch_seq; + bool fetch_seq_valid; + + InstSeqNum cp_seq; + bool cp_seq_valid; + + struct iRegFile { + IntRegFile regs; + }; + iRegFile *iregs; + bool regs_valid; + + public: + InstRecord(Tick _cycle, BaseCPU *_cpu, StaticInstPtr<TheISA> _staticInst, + Addr _pc, bool spec, unsigned _thread) + : Record(_cycle), cpu(_cpu), staticInst(_staticInst), PC(_pc), + misspeculating(spec), thread(_thread) + { + data_status = DataInvalid; + addr_valid = false; + regs_valid = false; + + fetch_seq_valid = false; + cp_seq_valid = false; + } + + virtual ~InstRecord() { } + + virtual void dump(std::ostream &outs); + + void setAddr(Addr a) { addr = a; addr_valid = true; } + + void setData(uint64_t d) { data.as_int = d; data_status = DataInt64; } + void setData(uint32_t d) { data.as_int = d; data_status = DataInt32; } + void setData(uint16_t d) { data.as_int = d; data_status = DataInt16; } + void setData(uint8_t d) { data.as_int = d; data_status = DataInt8; } + + void setData(int64_t d) { setData((uint64_t)d); } + void setData(int32_t d) { setData((uint32_t)d); } + void setData(int16_t d) { setData((uint16_t)d); } + void setData(int8_t d) { setData((uint8_t)d); } + + void setData(double d) { data.as_double = d; data_status = DataDouble; } + + void setFetchSeq(InstSeqNum seq) + { fetch_seq = seq; fetch_seq_valid = true; } + + void setCPSeq(InstSeqNum seq) + { cp_seq = seq; cp_seq_valid = true; } + + void setRegs(const IntRegFile ®s); + + void finalize() { theLog.append(this); } + + enum InstExecFlagBits { + TRACE_MISSPEC = 0, + PRINT_CYCLE, + PRINT_OP_CLASS, + PRINT_THREAD_NUM, + PRINT_RESULT_DATA, + PRINT_EFF_ADDR, + PRINT_INT_REGS, + PRINT_FETCH_SEQ, + PRINT_CP_SEQ, + NUM_BITS + }; + + static std::vector<bool> flags; + + static void setParams(); + + static bool traceMisspec() { return flags[TRACE_MISSPEC]; } +}; + + +inline void +InstRecord::setRegs(const IntRegFile ®s) +{ + if (!iregs) + iregs = new iRegFile; + + memcpy(&iregs->regs, regs, sizeof(IntRegFile)); + regs_valid = true; +} + +inline +InstRecord * +getInstRecord(Tick cycle, ExecContext *xc, BaseCPU *cpu, + const StaticInstPtr<TheISA> staticInst, + Addr pc, int thread = 0) +{ + if (DTRACE(InstExec) && + (InstRecord::traceMisspec() || !xc->misspeculating())) { + return new InstRecord(cycle, cpu, staticInst, pc, + xc->misspeculating(), thread); + } + + return NULL; +} + + +} + +#endif // __EXETRACE_HH__ diff --git a/cpu/full_cpu/op_class.hh b/cpu/full_cpu/op_class.hh new file mode 100644 index 000000000..67ccaabad --- /dev/null +++ b/cpu/full_cpu/op_class.hh @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2003 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 __OP_CLASS_HH__ +#define __OP_CLASS_HH__ + +/** + * @file + * Definition of operation classes. + */ + +/** + * Instruction operation classes. These classes are used for + * assigning instructions to functional units. + */ +enum OpClass { + No_OpClass = 0, /* inst does not use a functional unit */ + IntALU, /* integer ALU */ + IntMULT, /* integer multiplier */ + IntDIV, /* integer divider */ + FloatADD, /* floating point adder/subtractor */ + FloatCMP, /* floating point comparator */ + FloatCVT, /* floating point<->integer converter */ + FloatMULT, /* floating point multiplier */ + FloatDIV, /* floating point divider */ + FloatSQRT, /* floating point square root */ + RdPort, /* memory read port */ + WrPort, /* memory write port */ + LvqPort, /* load value queue read port (redundant threading) */ + IPrefPort, + Num_OpClasses /* total functional unit classes */ +}; + +/** + * Array mapping OpClass enum values to strings. + */ +extern const char *opClassStrings[]; + +#endif // __OP_CLASS_HH__ diff --git a/cpu/full_cpu/smt.hh b/cpu/full_cpu/smt.hh new file mode 100644 index 000000000..f9c1e4614 --- /dev/null +++ b/cpu/full_cpu/smt.hh @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2003 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. + */ + +/** + * @file + * Defines SMT_MAX_CPUS and SMT_MAX_THREADS. + */ + +#ifndef __SMT_HH__ +#define __SMT_HH__ + +#ifndef SMT_MAX_CPUS +/** The maximum number of cpus in any one system. */ +#define SMT_MAX_CPUS 4 +#endif + +#ifndef SMT_MAX_THREADS +/** The number of TPUs in any processor. */ +#define SMT_MAX_THREADS 4 +#endif + +/** + * The maximum number of active threads across all cpus. Used to + * initialize per-thread statistics in the cache. + * + * NB: Be careful to only use it once all the CPUs that you care about + * have been initialized + */ +extern int maxThreadsPerCPU; + +/** + * Changes the status and priority of the thread with the given number. + * @param thread_number The thread to change. + * @param activate The new active status. + * @param priority The new priority. + */ +void change_thread_state(int thread_number, int activate, int priority); + +#endif // __SMT_HH__ diff --git a/cpu/inst_seq.hh b/cpu/inst_seq.hh new file mode 100644 index 000000000..9c3898ff7 --- /dev/null +++ b/cpu/inst_seq.hh @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2003 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 __STD_TYPES_HH__ +#define __STD_TYPES_HH__ + +// inst sequence type, used to order instructions in the ready list, +// if this rolls over the ready list order temporarily will get messed +// up, but execution will continue and complete correctly +typedef unsigned long long InstSeqNum; + +// inst tag type, used to tag an operation instance in the IQ +typedef unsigned int InstTag; + +#endif // __STD_TYPES_HH__ diff --git a/cpu/intr_control.cc b/cpu/intr_control.cc new file mode 100644 index 000000000..7ad32a2b9 --- /dev/null +++ b/cpu/intr_control.cc @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2003 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 <string> +#include <vector> + +#include "base_cpu.hh" +#include "intr_control.hh" +#include "sim_object.hh" + +using namespace std; + +IntrControl::IntrControl(const string &name, BaseCPU *c) + : SimObject(name), cpu(c) +{} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IntrControl) + + SimObjectParam<BaseCPU *> cpu; + +END_DECLARE_SIM_OBJECT_PARAMS(IntrControl) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IntrControl) + + INIT_PARAM(cpu, "the processor") + +END_INIT_SIM_OBJECT_PARAMS(IntrControl) + +CREATE_SIM_OBJECT(IntrControl) +{ + return new IntrControl(getInstanceName(), cpu); +} + +REGISTER_SIM_OBJECT("IntrControl", IntrControl) diff --git a/cpu/intr_control.hh b/cpu/intr_control.hh new file mode 100644 index 000000000..660d6d704 --- /dev/null +++ b/cpu/intr_control.hh @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003 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 __INTR_CONTROL_HH__ +#define __INTR_CONTROL_HH__ + +#include "misc.hh" +#include "base_cpu.hh" +#include "sim_object.hh" + +class IntrControl : public SimObject +{ + public: + BaseCPU *cpu; + IntrControl(const std::string &name, BaseCPU *c); + + void clear(int int_num, int index = 0); + void post(int int_num, int index = 0); +}; + +inline void +IntrControl::post(int int_num, int index) +{ cpu->post_interrupt(int_num, index); } + +inline void +IntrControl::clear(int int_num, int index) +{ cpu->clear_interrupt(int_num, index); } + +#endif // __INTR_CONTROL_HH__ + + + + + + + diff --git a/cpu/memtest/memtest.cc b/cpu/memtest/memtest.cc new file mode 100644 index 000000000..70b6fbf13 --- /dev/null +++ b/cpu/memtest/memtest.cc @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2003 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. + */ + +// FIX ME: make trackBlkAddr use blocksize from actual cache, not hard coded + +#include <string> +#include <sstream> +#include <iomanip> +#include <vector> + +#include "memtest.hh" +#include "misc.hh" +#include "sim_events.hh" +#include "main_memory.hh" +#include "base_cache.hh" + +#include "statistics.hh" +#include "sim_stats.hh" + +using namespace std; + +MemTest::MemTest(const string &name, + MemInterface *_cache_interface, + FunctionalMemory *main_mem, + FunctionalMemory *check_mem, + unsigned _memorySize, + unsigned _percentReads, + unsigned _percentUncacheable, + unsigned _maxReads, + unsigned _progressInterval, + Addr _traceAddr) + : BaseCPU(name, 1), + tickEvent(this), + cacheInterface(_cache_interface), + mainMem(main_mem), + checkMem(check_mem), + size(_memorySize), + percentReads(_percentReads), + percentUncacheable(_percentUncacheable), + maxReads(_maxReads), + progressInterval(_progressInterval), + nextProgressMessage(_progressInterval) +{ + vector<string> cmd; + cmd.push_back("/bin/ls"); + vector<string> null_vec; + xc = new ExecContext(this ,0,mainMem,0); + + blockSize = cacheInterface->getBlockSize(); + blockAddrMask = blockSize - 1; + traceBlockAddr = blockAddr(_traceAddr); + + //setup data storage with interesting values + uint8_t *data1 = new uint8_t[size]; + uint8_t *data2 = new uint8_t[size]; + uint8_t *data3 = new uint8_t[size]; + memset(data1, 1, size); + memset(data2, 2, size); + memset(data3, 3, size); + curTick = 0; + + baseAddr1 = 0x100000; + baseAddr2 = 0x400000; + uncacheAddr = 0x800000; + + // set up intial memory contents here + mainMem->prot_write(baseAddr1, data1, size); + checkMem->prot_write(baseAddr1, data1, size); + mainMem->prot_write(baseAddr2, data2, size); + checkMem->prot_write(baseAddr2, data2, size); + mainMem->prot_write(uncacheAddr, data3, size); + checkMem->prot_write(uncacheAddr, data3, size); + + delete [] data1; + delete [] data2; + delete [] data3; + + // set up counters + noResponseCycles = 0; + numReads = 0; + numWrites = 0; + tickEvent.schedule(0); +} + +static void +printData(ostream &os, uint8_t *data, int nbytes) +{ + os << hex << setfill('0'); + // assume little-endian: print bytes from highest address to lowest + for (uint8_t *dp = data + nbytes - 1; dp >= data; --dp) { + os << setw(2) << (unsigned)*dp; + } + os << dec; +} + +void +MemTest::completeRequest(MemReqPtr req, uint8_t *data) +{ + switch (req->cmd) { + case Read: + if (memcmp(req->data, data, req->size) != 0) { + cerr << name() << ": on read of 0x" << hex << req->paddr + << " @ cycle " << dec << curTick + << ", cache returns 0x"; + printData(cerr, req->data, req->size); + cerr << ", expected 0x"; + printData(cerr, data, req->size); + cerr << endl; + fatal(""); + } + + numReads++; + + if (numReads.val() == nextProgressMessage) { + cerr << name() << ": completed " << numReads.val() + << " read accesses @ " << curTick << endl; + nextProgressMessage += progressInterval; + } + + if (numReads.val() == maxReads) { + stringstream stream; + stream << name() << " reached max read count (" << maxReads + << ")" << endl; + + new SimExitEvent(stream.str()); + } + break; + + case Write: + numWrites++; + break; + + + default: + panic("invalid command"); + } + + if (blockAddr(req->paddr) == traceBlockAddr) { + cerr << name() << ": completed " + << (req->cmd.isWrite() ? "write" : "read") << " access of " + << req->size << " bytes at address 0x" + << hex << req->paddr << ", value = 0x"; + printData(cerr, req->data, req->size); + cerr << endl; + } + + noResponseCycles = 0; + delete [] data; +} + + +void +MemTest::regStats() +{ + using namespace Statistics; + + numReads + .name(name() + ".num_reads") + .desc("number of read accesses completed") + ; + + numWrites + .name(name() + ".num_writes") + .desc("number of write accesses completed") + ; + + numCopies + .name(name() + ".num_copies") + .desc("number of copy accesses completed") + ; +} + +void +MemTest::tick() +{ + if (!tickEvent.scheduled()) + tickEvent.schedule(curTick + 1); + + if (++noResponseCycles >= 5000) { + cerr << name() << ": deadlocked at cycle " << curTick << endl; + fatal(""); + } + + if (cacheInterface->isBlocked()) { + return; + } + + //make new request + unsigned cmd = rand() % 100; + unsigned offset1 = random() % size; + unsigned base = random() % 2; + uint64_t data = random(); + unsigned access_size = random() % 4; + unsigned cacheable = rand() % 100; + + MemReqPtr req = new MemReq(); + + if (cacheable < percentUncacheable) { + req->flags |= UNCACHEABLE; + req->paddr = uncacheAddr + offset1; + } else { + req->paddr = ((base) ? baseAddr1 : baseAddr2) + offset1; + } + + req->size = 1 << access_size; + req->data = new uint8_t[req->size]; + req->paddr &= ~(req->size - 1); + req->time = curTick; + req->xc = xc; + + if (cmd < percentReads) { + // read + req->cmd = Read; + uint8_t *result = new uint8_t[8]; + checkMem->access(Read, req->paddr, result, req->size); + if (blockAddr(req->paddr) == traceBlockAddr) { + cerr << name() << ": initiating read of " + << req->size << " bytes from addr 0x" + << hex << req->paddr << " at cycle " + << dec << curTick << endl; + } + + req->completionEvent = new MemCompleteEvent(req, result, this); + cacheInterface->access(req); + } else { + // write + req->cmd = Write; + memcpy(req->data, &data, req->size); + checkMem->access(Write, req->paddr, req->data, req->size); + if (blockAddr(req->paddr) == traceBlockAddr) { + cerr << name() << ": initiating write of " + << req->size << " bytes (value = 0x"; + printData(cerr, req->data, req->size); + cerr << ") to addr 0x" + << hex << req->paddr << " at cycle " + << dec << curTick << endl; + } + req->completionEvent = new MemCompleteEvent(req, NULL, this); + cacheInterface->access(req); + } +} + + +void +MemCompleteEvent::process() +{ + tester->completeRequest(req, data); + delete this; +} + + +const char * +MemCompleteEvent::description() +{ + return "memory access completion"; +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(MemTest) + + SimObjectParam<BaseCache *> cache; + SimObjectParam<FunctionalMemory *> main_mem; + SimObjectParam<FunctionalMemory *> check_mem; + Param<unsigned> memory_size; + Param<unsigned> percent_reads; + Param<unsigned> percent_uncacheable; + Param<unsigned> max_reads; + Param<unsigned> progress_interval; + Param<Addr> trace_addr; + +END_DECLARE_SIM_OBJECT_PARAMS(MemTest) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(MemTest) + + INIT_PARAM(cache, "L1 cache"), + INIT_PARAM(main_mem, "hierarchical memory"), + INIT_PARAM(check_mem, "check memory"), + INIT_PARAM_DFLT(memory_size, "memory size", 65536), + INIT_PARAM_DFLT(percent_reads, "target read percentage", 65), + INIT_PARAM_DFLT(percent_uncacheable, "target uncacheable percentage", 10), + INIT_PARAM_DFLT(max_reads, "number of reads to simulate", 0), + INIT_PARAM_DFLT(progress_interval, + "progress report interval (in accesses)", 1000000), + INIT_PARAM_DFLT(trace_addr, "address to trace", 0) + +END_INIT_SIM_OBJECT_PARAMS(MemTest) + + +CREATE_SIM_OBJECT(MemTest) +{ + return new MemTest(getInstanceName(), cache->getInterface(), main_mem, + check_mem, + memory_size, percent_reads, + percent_uncacheable, max_reads, progress_interval, + trace_addr); +} + +REGISTER_SIM_OBJECT("MemTest", MemTest) diff --git a/cpu/memtest/memtest.hh b/cpu/memtest/memtest.hh new file mode 100644 index 000000000..aa652abbd --- /dev/null +++ b/cpu/memtest/memtest.hh @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2003 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 __MEMTEST_HH__ +#define __MEMTEST_HH__ + +#include "sim_object.hh" +#include "mem_interface.hh" +#include "functional_memory.hh" +#include "base_cpu.hh" +#include "exec_context.hh" + +#include "statistics.hh" +#include "sim_stats.hh" + +class MemTest : public BaseCPU +{ + public: + + MemTest(const std::string &name, + MemInterface *_cache_interface, + FunctionalMemory *main_mem, + FunctionalMemory *check_mem, + unsigned _memorySize, + unsigned _percentReads, + unsigned _percentUncacheable, + unsigned _maxReads, + unsigned _progressInterval, + Addr _traceAddr); + + // register statistics + virtual void regStats(); + // main simulation loop (one cycle) + void tick(); + + protected: + class TickEvent : public Event + { + private: + MemTest *cpu; + public: + TickEvent(MemTest *c) + : Event(&mainEventQueue, 100), cpu(c) {} + void process() {cpu->tick();} + virtual const char *description() { return "tick event"; } + }; + + TickEvent tickEvent; + + MemInterface *cacheInterface; + FunctionalMemory *mainMem; + FunctionalMemory *checkMem; + ExecContext *xc; + + unsigned size; // size of testing memory region + + unsigned percentReads; // target percentage of read accesses + unsigned percentUncacheable; + + Tick maxReads; // max # of reads to perform (then quit) + + unsigned blockSize; + + Addr blockAddrMask; + + Addr blockAddr(Addr addr) + { + return (addr & ~blockAddrMask); + } + + Addr traceBlockAddr; + + Addr baseAddr1; // fix this to option + Addr baseAddr2; // fix this to option + Addr uncacheAddr; + + unsigned progressInterval; // frequency of progress reports + Tick nextProgressMessage; // access # for next progress report + + Tick noResponseCycles; + + Statistics::Scalar<long long int> numReads; + Statistics::Scalar<long long int> numWrites; + Statistics::Scalar<long long int> numCopies; + + // called by MemCompleteEvent::process() + void completeRequest(MemReqPtr req, uint8_t *data); + + friend class MemCompleteEvent; +}; + + +class MemCompleteEvent : public Event +{ + MemReqPtr req; + uint8_t *data; + MemTest *tester; + + public: + + MemCompleteEvent(MemReqPtr _req, uint8_t *_data, MemTest *_tester) + : Event(&mainEventQueue), + req(_req), data(_data), tester(_tester) + { + } + + void process(); + + virtual const char *description(); +}; + +#endif // __MEMTEST_HH__ + + + diff --git a/cpu/pc_event.cc b/cpu/pc_event.cc new file mode 100644 index 000000000..4de425199 --- /dev/null +++ b/cpu/pc_event.cc @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2003 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 <algorithm> +#include <map> +#include <string> +#include <utility> + +#include "debug.hh" +#include "exec_context.hh" +#include "pc_event.hh" +#include "trace.hh" +#include "universe.hh" + +#ifdef FULL_SYSTEM +#include "arguments.hh" +#include "pmap.h" +#include "kernel.hh" +#include "memory_control.hh" +#include "cpu.hh" +#include "system.hh" +#include "bpred.hh" +#endif + +using namespace std; + +PCEventQueue::PCEventQueue() +{} + +PCEventQueue::~PCEventQueue() +{} + +bool +PCEventQueue::remove(PCEvent *event) +{ + int removed = 0; + range_t range = equal_range(event); + for (iterator i = range.first; i != range.second; ++i) { + if (*i == event) { + DPRINTF(PCEvent, "PC based event removed at %#x: %s\n", + event->pc(), event->descr()); + pc_map.erase(i); + ++removed; + } + } + + return removed > 0; +} + +bool +PCEventQueue::schedule(PCEvent *event) +{ + pc_map.push_back(event); + sort(pc_map.begin(), pc_map.end(), MapCompare()); + + DPRINTF(PCEvent, "PC based event scheduled for %#x: %s\n", + event->pc(), event->descr()); + + return true; +} + +bool +PCEventQueue::service(ExecContext *xc) +{ + Addr pc = xc->regs.pc; + int serviced = 0; + range_t range = equal_range(pc); + for (iterator i = range.first; i != range.second; ++i) { + // Make sure that the pc wasn't changed as the side effect of + // another event. This for example, prevents two invocations + // of the SkipFuncEvent. Maybe we should have separate PC + // event queues for each processor? + if (pc != xc->regs.pc) + continue; + + DPRINTF(PCEvent, "PC based event serviced at %#x: %s\n", + (*i)->pc(), (*i)->descr()); + + (*i)->process(xc); + ++serviced; + } + + return serviced > 0; +} + +void +PCEventQueue::dump() const +{ + const_iterator i = pc_map.begin(); + const_iterator e = pc_map.end(); + + for (; i != e; ++i) + cprintf("%d: event at %#x: %s\n", curTick, (*i)->pc(), + (*i)->descr()); +} + +PCEventQueue::range_t +PCEventQueue::equal_range(Addr pc) +{ + return std::equal_range(pc_map.begin(), pc_map.end(), pc, MapCompare()); +} + +#ifdef FULL_SYSTEM +void +SkipFuncEvent::process(ExecContext *xc) +{ + Addr newpc = xc->regs.intRegFile[ReturnAddressReg]; + + DPRINTF(PCEvent, "skipping %s: pc=%x, newpc=%x\n", description, + xc->regs.pc, newpc); + + xc->regs.pc = newpc; + xc->regs.npc = xc->regs.pc + sizeof(MachInst); + + BranchPred *bp = xc->cpu->getBranchPred(); + if (bp != NULL) { + bp->popRAS(xc->thread_num); + } +} + +void +BadAddrEvent::process(ExecContext *xc) +{ + // The following gross hack is the equivalent function to the + // annotation for vmunix::badaddr in: + // simos/simulation/apps/tcl/osf/tlaser.tcl + + uint64_t a0 = xc->regs.intRegFile[ArgumentReg0]; + + if (a0 < ALPHA_K0SEG_BASE || a0 >= ALPHA_K1SEG_BASE || + xc->memCtrl->badaddr(ALPHA_K0SEG_TO_PHYS(a0) & PA_IMPL_MASK)) { + + DPRINTF(BADADDR, "badaddr arg=%#x bad\n", a0); + xc->regs.intRegFile[ReturnValueReg] = 0x1; + SkipFuncEvent::process(xc); + } + else + DPRINTF(BADADDR, "badaddr arg=%#x good\n", a0); +} + +void +PrintfEvent::process(ExecContext *xc) +{ + if (DTRACE(Printf)) { + DebugOut() << curTick << ": " << xc->cpu->name() << ": "; + + AlphaArguments args(xc); + Kernel::Printf(args); + } +} + +void +DebugPrintfEvent::process(ExecContext *xc) +{ + if (DTRACE(DebugPrintf)) { + if (!raw) + DebugOut() << curTick << ": " << xc->cpu->name() << ": "; + + AlphaArguments args(xc); + Kernel::Printf(args); + } +} + +void +DumpMbufEvent::process(ExecContext *xc) +{ + if (DTRACE(DebugPrintf)) { + AlphaArguments args(xc); + Kernel::DumpMbuf(args); + } +} +#endif + +BreakPCEvent::BreakPCEvent(PCEventQueue *q, const std::string &desc, bool del) + : PCEvent(q, desc), remove(del) +{ +} + +void +BreakPCEvent::process(ExecContext *xc) +{ + debug_break(); + if (remove) + delete this; +} + +#ifdef FULL_SYSTEM +extern "C" +void +sched_break_pc_sys(System *sys, Addr addr) +{ + PCEvent *event = new BreakPCEvent(&sys->pcEventQueue, "debug break", true); + event->schedule(addr); +} + +extern "C" +void +sched_break_pc(Addr addr) +{ + for (vector<System *>::iterator sysi = System::systemList.begin(); + sysi != System::systemList.end(); ++sysi) { + sched_break_pc_sys(*sysi, addr); + } + +} +#endif diff --git a/cpu/pc_event.hh b/cpu/pc_event.hh new file mode 100644 index 000000000..24442f5f4 --- /dev/null +++ b/cpu/pc_event.hh @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2003 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 __PC_EVENT_HH__ +#define __PC_EVENT_HH__ + +#include <vector> + +#include "mem_req.hh" + +class ExecContext; +class PCEventQueue; + +class PCEvent +{ + protected: + static const Addr badpc = MemReq::inval_addr; + + protected: + std::string description; + PCEventQueue *queue; + Addr evpc; + + public: + PCEvent() : queue(0), evpc(badpc) { } + + PCEvent(const std::string &desc) + : description(desc), queue(0), evpc(badpc) { } + + PCEvent(PCEventQueue *q, Addr pc = badpc) : queue(q), evpc(pc) { } + + PCEvent(PCEventQueue *q, const std::string &desc, Addr pc = badpc) + : description(desc), queue(q), evpc(pc) { } + + virtual ~PCEvent() { if (queue) remove(); } + + std::string descr() const { return description; } + Addr pc() const { return evpc; } + + bool remove(); + bool schedule(); + bool schedule(Addr pc); + bool schedule(PCEventQueue *q, Addr pc); + virtual void process(ExecContext *xc) = 0; +}; + +class PCEventQueue +{ + protected: + typedef PCEvent * record_t; + class MapCompare { + public: + bool operator()(const record_t &l, const record_t &r) const { + return l->pc() < r->pc(); + } + bool operator()(const record_t &l, Addr pc) const { + return l->pc() < pc; + } + bool operator()(Addr pc, const record_t &r) const { + return pc < r->pc(); + } + }; + typedef std::vector<record_t> map_t; + + public: + typedef map_t::iterator iterator; + typedef map_t::const_iterator const_iterator; + + protected: + typedef std::pair<iterator, iterator> range_t; + typedef std::pair<const_iterator, const_iterator> const_range_t; + + protected: + map_t pc_map; + + public: + PCEventQueue(); + ~PCEventQueue(); + + bool remove(PCEvent *event); + bool schedule(PCEvent *event); + bool service(ExecContext *xc); + + range_t equal_range(Addr pc); + range_t equal_range(PCEvent *event) { return equal_range(event->pc()); } + + void dump() const; +}; + +inline bool +PCEvent::remove() +{ + if (!queue) + panic("cannot remove an uninitialized event;"); + + return queue->remove(this); +} + +inline bool +PCEvent::schedule() +{ + if (!queue || evpc == badpc) + panic("cannot schedule an uninitialized event;"); + + return queue->schedule(this); +} + +inline bool +PCEvent::schedule(Addr pc) +{ + if (evpc != badpc) + panic("cannot switch PC"); + evpc = pc; + + return schedule(); +} + +inline bool +PCEvent::schedule(PCEventQueue *q, Addr pc) +{ + if (queue) + panic("cannot switch event queues"); + + if (evpc != badpc) + panic("cannot switch addresses"); + + queue = q; + evpc = pc; + + return schedule(); +} + + +#ifdef FULL_SYSTEM +class SkipFuncEvent : public PCEvent +{ + public: + SkipFuncEvent(PCEventQueue *q, const std::string &desc) + : PCEvent(q, desc) {} + virtual void process(ExecContext *xc); +}; + +class BadAddrEvent : public SkipFuncEvent +{ + public: + BadAddrEvent(PCEventQueue *q, const std::string &desc) + : SkipFuncEvent(q, desc) {} + virtual void process(ExecContext *xc); +}; + +class PrintfEvent : public PCEvent +{ + public: + PrintfEvent(PCEventQueue *q, const std::string &desc) + : PCEvent(q, desc) {} + virtual void process(ExecContext *xc); +}; + +class DebugPrintfEvent : public PCEvent +{ + private: + bool raw; + + public: + DebugPrintfEvent(PCEventQueue *q, const std::string &desc, bool r = false) + : PCEvent(q, desc), raw(r) {} + virtual void process(ExecContext *xc); +}; + +class DumpMbufEvent : public PCEvent +{ + public: + DumpMbufEvent(PCEventQueue *q, const std::string &desc) + : PCEvent(q, desc) {} + virtual void process(ExecContext *xc); +}; +#endif + +class BreakPCEvent : public PCEvent +{ + protected: + bool remove; + + public: + BreakPCEvent(PCEventQueue *q, const std::string &desc, bool del = false); + virtual void process(ExecContext *xc); +}; + + +#endif // __PC_EVENT_HH__ diff --git a/cpu/simple_cpu/simple_cpu.cc b/cpu/simple_cpu/simple_cpu.cc new file mode 100644 index 000000000..41a612318 --- /dev/null +++ b/cpu/simple_cpu/simple_cpu.cc @@ -0,0 +1,746 @@ +/* + * Copyright (c) 2003 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 <iostream> +#include <iomanip> +#include <list> +#include <sstream> +#include <string> + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +#include "host.hh" +#include "cprintf.hh" +#include "misc.hh" +#include "smt.hh" + +#include "annotation.hh" +#include "exec_context.hh" +#include "base_cpu.hh" +#include "debug.hh" +#include "simple_cpu.hh" +#include "inifile.hh" +#include "mem_interface.hh" +#include "base_mem.hh" +#include "static_inst.hh" + +#ifdef FULL_SYSTEM +#include "memory_control.hh" +#include "physical_memory.hh" +#include "alpha_memory.hh" +#include "system.hh" +#else // !FULL_SYSTEM +#include "functional_memory.hh" +#include "prog.hh" +#include "eio.hh" +#endif // FULL_SYSTEM + +#include "exetrace.hh" +#include "trace.hh" +#include "sim_events.hh" +#include "pollevent.hh" +#include "sim_object.hh" +#include "sim_stats.hh" + +#include "range.hh" +#include "symtab.hh" + +#ifdef FULL_SYSTEM +#include "vtophys.hh" +#include "pciareg.h" +#include "remote_gdb.hh" +#include "alpha_access.h" +#endif + + +using namespace std; + +SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU *_cpu) + : Event(&mainEventQueue), + cpu(_cpu) +{ +} + +void SimpleCPU::CacheCompletionEvent::process() +{ + cpu->processCacheCompletion(); +} + +const char * +SimpleCPU::CacheCompletionEvent::description() +{ + return "cache completion event"; +} + +#ifdef FULL_SYSTEM +SimpleCPU::SimpleCPU(const string &_name, + System *_system, + Counter max_insts_any_thread, + Counter max_insts_all_threads, + AlphaItb *itb, AlphaDtb *dtb, + FunctionalMemory *mem, + MemInterface *icache_interface, + MemInterface *dcache_interface, + int cpu_id, Tick freq) + : BaseCPU(_name, /* number_of_threads */ 1, + max_insts_any_thread, max_insts_all_threads, + _system, cpu_id, freq), +#else +SimpleCPU::SimpleCPU(const string &_name, Process *_process, + Counter max_insts_any_thread, + Counter max_insts_all_threads, + MemInterface *icache_interface, + MemInterface *dcache_interface) + : BaseCPU(_name, /* number_of_threads */ 1, + max_insts_any_thread, max_insts_all_threads), +#endif + tickEvent(this), xc(NULL), cacheCompletionEvent(this) +{ +#ifdef FULL_SYSTEM + xc = new ExecContext(this, 0, system, itb, dtb, mem, cpu_id); + + _status = Running; + if (cpu_id != 0) { + + xc->setStatus(ExecContext::Unallocated); + + //Open a GDB debug session on port (7000 + the cpu_id) + (new GDBListener(new RemoteGDB(system, xc), 7000 + cpu_id))->listen(); + + AlphaISA::init(system->physmem, &xc->regs); + + fault = Reset_Fault; + + IntReg *ipr = xc->regs.ipr; + ipr[TheISA::IPR_MCSR] = 0x6; + + AlphaISA::swap_palshadow(&xc->regs, true); + + xc->regs.pc = + ipr[TheISA::IPR_PAL_BASE] + AlphaISA::fault_addr[fault]; + xc->regs.npc = xc->regs.pc + sizeof(MachInst); + + _status = Idle; + } + else { + system->initBootContext(xc); + + // Reset the system + // + AlphaISA::init(system->physmem, &xc->regs); + + fault = Reset_Fault; + + IntReg *ipr = xc->regs.ipr; + ipr[TheISA::IPR_MCSR] = 0x6; + + AlphaISA::swap_palshadow(&xc->regs, true); + + xc->regs.pc = ipr[TheISA::IPR_PAL_BASE] + AlphaISA::fault_addr[fault]; + xc->regs.npc = xc->regs.pc + sizeof(MachInst); + + _status = Running; + tickEvent.schedule(0); + } + +#else + xc = new ExecContext(this, /* thread_num */ 0, _process, /* asid */ 0); + fault = No_Fault; + if (xc->status() == ExecContext::Active) { + _status = Running; + tickEvent.schedule(0); + } else + _status = Idle; +#endif // !FULL_SYSTEM + + icacheInterface = icache_interface; + dcacheInterface = dcache_interface; + + memReq = new MemReq(); + memReq->xc = xc; + memReq->asid = 0; + + numInst = 0; + last_idle = 0; + lastIcacheStall = 0; + lastDcacheStall = 0; + + contexts.push_back(xc); +} + +SimpleCPU::~SimpleCPU() +{ +} + +void +SimpleCPU::regStats() +{ + BaseCPU::regStats(); + + numInsts + .name(name() + ".num_insts") + .desc("Number of instructions executed") + ; + + numMemRefs + .name(name() + ".num_refs") + .desc("Number of memory references") + ; + + idleCycles + .name(name() + ".idle_cycles") + .desc("Number of idle cycles") + ; + + idleFraction + .name(name() + ".idle_fraction") + .desc("Percentage of idle cycles") + ; + + icacheStallCycles + .name(name() + ".icache_stall_cycles") + .desc("ICache total stall cycles") + .prereq(icacheStallCycles) + ; + + dcacheStallCycles + .name(name() + ".dcache_stall_cycles") + .desc("DCache total stall cycles") + .prereq(dcacheStallCycles) + ; + + idleFraction = idleCycles / simTicks; + + numInsts = Statistics::scalar(numInst); + simInsts += numInsts; +} + +void +SimpleCPU::serialize() +{ + nameOut(); + +#ifdef FULL_SYSTEM +#if 0 + // do we need this anymore?? egh + childOut("itb", xc->itb); + childOut("dtb", xc->dtb); + childOut("physmem", physmem); +#endif +#endif + + for (int i = 0; i < NumIntRegs; i++) { + stringstream buf; + ccprintf(buf, "R%02d", i); + paramOut(buf.str(), xc->regs.intRegFile[i]); + } + for (int i = 0; i < NumFloatRegs; i++) { + stringstream buf; + ccprintf(buf, "F%02d", i); + paramOut(buf.str(), xc->regs.floatRegFile.d[i]); + } + // CPUTraitsType::serializeSpecialRegs(getProxy(), xc->regs); +} + +void +SimpleCPU::unserialize(IniFile &db, const string &category, ConfigNode *node) +{ + string data; + + for (int i = 0; i < NumIntRegs; i++) { + stringstream buf; + ccprintf(buf, "R%02d", i); + db.findDefault(category, buf.str(), data); + to_number(data,xc->regs.intRegFile[i]); + } + for (int i = 0; i < NumFloatRegs; i++) { + stringstream buf; + ccprintf(buf, "F%02d", i); + db.findDefault(category, buf.str(), data); + xc->regs.floatRegFile.d[i] = strtod(data.c_str(),NULL); + } + + // Read in Special registers + + // CPUTraitsType::unserializeSpecialRegs(db,category,node,xc->regs); +} + +void +change_thread_state(int thread_number, int activate, int priority) +{ +} + +// precise architected memory state accessor macros +template <class T> +Fault +SimpleCPU::read(Addr addr, T& data, unsigned flags) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = xc->translateDataReadReq(memReq); + + // do functional access + if (fault == No_Fault) + fault = xc->read(memReq, data); + + if (traceData) { + traceData->setAddr(addr); + if (fault == No_Fault) + traceData->setData(data); + } + + // if we have a cache, do cache access too + if (fault == No_Fault && dcacheInterface) { + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~UNCACHEABLE; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && dcacheInterface->doEvents) { + memReq->completionEvent = &cacheCompletionEvent; + setStatus(DcacheMissStall); + } + } + + return fault; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Fault +SimpleCPU::read(Addr addr, uint64_t& data, unsigned flags); + +template +Fault +SimpleCPU::read(Addr addr, uint32_t& data, unsigned flags); + +template +Fault +SimpleCPU::read(Addr addr, uint16_t& data, unsigned flags); + +template +Fault +SimpleCPU::read(Addr addr, uint8_t& data, unsigned flags); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +SimpleCPU::read(Addr addr, double& data, unsigned flags) +{ + return read(addr, *(uint64_t*)&data, flags); +} + +template<> +Fault +SimpleCPU::read(Addr addr, float& data, unsigned flags) +{ + return read(addr, *(uint32_t*)&data, flags); +} + + +template<> +Fault +SimpleCPU::read(Addr addr, int32_t& data, unsigned flags) +{ + return read(addr, (uint32_t&)data, flags); +} + + +template <class T> +Fault +SimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + if (traceData) { + traceData->setAddr(addr); + traceData->setData(data); + } + + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = xc->translateDataWriteReq(memReq); + + // do functional access + if (fault == No_Fault) + fault = xc->write(memReq, data); + + if (fault == No_Fault && dcacheInterface) { + memReq->cmd = Write; + memReq->data = (uint8_t *)&data; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~UNCACHEABLE; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && dcacheInterface->doEvents) { + memReq->completionEvent = &cacheCompletionEvent; + setStatus(DcacheMissStall); + } + } + + if (res && (fault == No_Fault)) + *res = memReq->result; + + return fault; +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +template +Fault +SimpleCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +SimpleCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +SimpleCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +SimpleCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +SimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint64_t*)&data, addr, flags, res); +} + +template<> +Fault +SimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint32_t*)&data, addr, flags, res); +} + + +template<> +Fault +SimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) +{ + return write((uint32_t)data, addr, flags, res); +} + + +#ifdef FULL_SYSTEM +Addr +SimpleCPU::dbg_vtophys(Addr addr) +{ + return vtophys(xc, addr); +} +#endif // FULL_SYSTEM + +Tick save_cycle = 0; + + +void +SimpleCPU::processCacheCompletion() +{ + switch (status()) { + case IcacheMissStall: + icacheStallCycles += curTick - lastIcacheStall; + setStatus(IcacheMissComplete); + break; + case DcacheMissStall: + dcacheStallCycles += curTick - lastDcacheStall; + setStatus(Running); + break; + default: + panic("SimpleCPU::processCacheCompletion: bad state"); + break; + } +} + +#ifdef FULL_SYSTEM +void +SimpleCPU::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (xc->status() == ExecContext::Suspended) { + DPRINTF(IPI,"Suspended Processor awoke\n"); + xc->setStatus(ExecContext::Active); + Annotate::Resume(xc); + } +} +#endif // FULL_SYSTEM + +/* start simulation, program loaded, processor precise state initialized */ +void +SimpleCPU::tick() +{ + traceData = NULL; + +#ifdef FULL_SYSTEM + if (fault == No_Fault && AlphaISA::check_interrupts && + xc->cpu->check_interrupts() && + !PC_PAL(xc->regs.pc) && + status() != IcacheMissComplete) { + int ipl = 0; + int summary = 0; + AlphaISA::check_interrupts = 0; + IntReg *ipr = xc->regs.ipr; + + if (xc->regs.ipr[TheISA::IPR_SIRR]) { + for (int i = TheISA::INTLEVEL_SOFTWARE_MIN; + i < TheISA::INTLEVEL_SOFTWARE_MAX; i++) { + if (ipr[TheISA::IPR_SIRR] & (ULL(1) << i)) { + // See table 4-19 of 21164 hardware reference + ipl = (i - TheISA::INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = xc->cpu->intr_status(); + for(int i = TheISA::INTLEVEL_EXTERNAL_MIN; + i < TheISA::INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + + if (ipr[TheISA::IPR_ASTRR]) + panic("asynchronous traps not implemented\n"); + + if (ipl && ipl > xc->regs.ipr[TheISA::IPR_IPLR]) { + ipr[TheISA::IPR_ISR] = summary; + ipr[TheISA::IPR_INTID] = ipl; + xc->ev5_trap(Interrupt_Fault); + + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + ipr[TheISA::IPR_IPLR], ipl, summary); + } + } +#endif + + // maintain $r0 semantics + xc->regs.intRegFile[ZeroReg] = 0; +#ifdef TARGET_ALPHA + xc->regs.floatRegFile.d[ZeroReg] = 0.0; +#endif // TARGET_ALPHA + + if (status() == IcacheMissComplete) { + // We've already fetched an instruction and were stalled on an + // I-cache miss. No need to fetch it again. + + setStatus(Running); + } + else { + // Try to fetch an instruction + + // set up memory request for instruction fetch +#ifdef FULL_SYSTEM +#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 +#else +#define IFETCH_FLAGS(pc) 0 +#endif + + memReq->cmd = Read; + memReq->reset(xc->regs.pc & ~3, sizeof(uint32_t), + IFETCH_FLAGS(xc->regs.pc)); + + fault = xc->translateInstReq(memReq); + + if (fault == No_Fault) + fault = xc->mem->read(memReq, inst); + + if (icacheInterface && fault == No_Fault) { + memReq->completionEvent = NULL; + + memReq->time = curTick; + memReq->flags &= ~UNCACHEABLE; + MemAccessResult result = icacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && icacheInterface->doEvents) { + memReq->completionEvent = &cacheCompletionEvent; + setStatus(IcacheMissStall); + return; + } + } + } + + // If we've got a valid instruction (i.e., no fault on instruction + // fetch), then execute it. + if (fault == No_Fault) { + + // keep an instruction count + numInst++; + + // check for instruction-count-based events + comInsnEventQueue[0]->serviceEvents(numInst); + + // decode the instruction + StaticInstPtr<TheISA> si(inst); + + traceData = Trace::getInstRecord(curTick, xc, this, si, + xc->regs.pc); + +#ifdef FULL_SYSTEM + xc->regs.opcode = (inst >> 26) & 0x3f; + xc->regs.ra = (inst >> 21) & 0x1f; +#endif // FULL_SYSTEM + + xc->func_exe_insn++; + + fault = si->execute(this, xc, traceData); + + if (si->isMemRef()) { + numMemRefs++; + } + + if (traceData) + traceData->finalize(); + + } // if (fault == No_Fault) + + if (fault != No_Fault) { +#ifdef FULL_SYSTEM + xc->ev5_trap(fault); +#else // !FULL_SYSTEM + fatal("fault (%d) detected @ PC 0x%08p", fault, xc->regs.pc); +#endif // FULL_SYSTEM + } + else { + // go to the next instruction + xc->regs.pc = xc->regs.npc; + xc->regs.npc += sizeof(MachInst); + } + +#ifdef FULL_SYSTEM + Addr oldpc; + do { + oldpc = xc->regs.pc; + system->pcEventQueue.service(xc); + } while (oldpc != xc->regs.pc); +#endif + + assert(status() == Running || + status() == Idle || + status() == DcacheMissStall); + + if (status() == Running && !tickEvent.scheduled()) + tickEvent.schedule(curTick + 1); +} + + +//////////////////////////////////////////////////////////////////////// +// +// SimpleCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + +#ifdef FULL_SYSTEM + SimObjectParam<AlphaItb *> itb; + SimObjectParam<AlphaDtb *> dtb; + SimObjectParam<FunctionalMemory *> mem; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<int> mult; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + + SimObjectParam<BaseMem *> icache; + SimObjectParam<BaseMem *> dcache; + +END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU) + + INIT_PARAM_DFLT(max_insts_any_thread, + "terminate when any thread reaches this insn count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "terminate when all threads have reached this insn count", + 0), + +#ifdef FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(mem, "memory"), + INIT_PARAM(system, "system object"), + INIT_PARAM_DFLT(cpu_id, "CPU identification number", 0), + INIT_PARAM_DFLT(mult, "system clock multiplier", 1), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM_DFLT(icache, "L1 instruction cache object", NULL), + INIT_PARAM_DFLT(dcache, "L1 data cache object", NULL) + +END_INIT_SIM_OBJECT_PARAMS(SimpleCPU) + + +CREATE_SIM_OBJECT(SimpleCPU) +{ +#ifdef FULL_SYSTEM + if (mult != 1) + panic("processor clock multiplier must be 1\n"); + + return new SimpleCPU(getInstanceName(), system, + max_insts_any_thread, max_insts_all_threads, + itb, dtb, mem, + (icache) ? icache->getInterface() : NULL, + (dcache) ? dcache->getInterface() : NULL, + cpu_id, ticksPerSecond * mult); +#else + + return new SimpleCPU(getInstanceName(), workload, + max_insts_any_thread, max_insts_all_threads, + icache->getInterface(), dcache->getInterface()); + +#endif // FULL_SYSTEM +} + +REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU) diff --git a/cpu/simple_cpu/simple_cpu.hh b/cpu/simple_cpu/simple_cpu.hh new file mode 100644 index 000000000..c5671eb6f --- /dev/null +++ b/cpu/simple_cpu/simple_cpu.hh @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2003 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 __SIMPLE_CPU_HH__ +#define __SIMPLE_CPU_HH__ + +#include "base_cpu.hh" +#include "eventq.hh" +#include "symtab.hh" +#include "pc_event.hh" +#include "statistics.hh" + + +// forward declarations +#ifdef FULL_SYSTEM +class Processor; +class Kernel; +class AlphaItb; +class AlphaDtb; +class PhysicalMemory; + +class RemoteGDB; +class GDBListener; +#endif // FULL_SYSTEM + +class MemInterface; +class IniFile; + +namespace Trace { + class InstRecord; +} + +class SimpleCPU : public BaseCPU +{ + public: + // main simulation loop (one cycle) + void tick(); + + private: + class TickEvent : public Event + { + private: + SimpleCPU *cpu; + + public: + TickEvent(SimpleCPU *c) + : Event(&mainEventQueue, 100), cpu(c) { } + void process() { cpu->tick(); } + virtual const char *description() { return "tick event"; } + }; + + TickEvent tickEvent; + + private: + Trace::InstRecord *traceData; + template<typename T> + void trace_data(T data) { + if (traceData) { + traceData->setData(data); + } + }; + + public: + // + enum Status { + Running, + Idle, + IcacheMissStall, + IcacheMissComplete, + DcacheMissStall + }; + + private: + Status _status; + + public: + void post_interrupt(int int_num, int index); + + void zero_fill_64(Addr addr) { + static int warned = 0; + if (!warned) { + warn ("WH64 is not implemented"); + warned = 1; + } + }; + +#ifdef FULL_SYSTEM + + SimpleCPU(const std::string &_name, + System *_system, + Counter max_insts_any_thread, Counter max_insts_all_threads, + AlphaItb *itb, AlphaDtb *dtb, FunctionalMemory *mem, + MemInterface *icache_interface, MemInterface *dcache_interface, + int cpu_id, Tick freq); + +#else + + SimpleCPU(const std::string &_name, Process *_process, + Counter max_insts_any_thread, + Counter max_insts_all_threads, + MemInterface *icache_interface, MemInterface *dcache_interface); + +#endif + + virtual ~SimpleCPU(); + + // execution context + ExecContext *xc; + +#ifdef FULL_SYSTEM + Addr dbg_vtophys(Addr addr); + + bool interval_stats; +#endif + + // L1 instruction cache + MemInterface *icacheInterface; + + // L1 data cache + MemInterface *dcacheInterface; + + // current instruction + MachInst inst; + + // current fault status + Fault fault; + + // Refcounted pointer to the one memory request. + MemReqPtr memReq; + + class CacheCompletionEvent : public Event + { + private: + SimpleCPU *cpu; + + public: + CacheCompletionEvent(SimpleCPU *_cpu); + + virtual void process(); + virtual const char *description(); + }; + + CacheCompletionEvent cacheCompletionEvent; + + Status status() const { return _status; } + virtual void execCtxStatusChg() { + if (xc) { + if (xc->status() == ExecContext::Active) + setStatus(Running); + else + setStatus(Idle); + } + } + + void setStatus(Status new_status) { + Status old_status = status(); + _status = new_status; + + switch (status()) { + case IcacheMissStall: + assert(old_status == Running); + lastIcacheStall = curTick; + if (tickEvent.scheduled()) + tickEvent.squash(); + break; + + case IcacheMissComplete: + assert(old_status == IcacheMissStall); + if (tickEvent.squashed()) + tickEvent.reschedule(curTick + 1); + else if (!tickEvent.scheduled()) + tickEvent.schedule(curTick + 1); + break; + + case DcacheMissStall: + assert(old_status == Running); + lastDcacheStall = curTick; + if (tickEvent.scheduled()) + tickEvent.squash(); + break; + + case Idle: + assert(old_status == Running); + last_idle = curTick; + if (tickEvent.scheduled()) + tickEvent.squash(); + break; + + case Running: + assert(old_status == Idle || + old_status == DcacheMissStall || + old_status == IcacheMissComplete); + if (old_status == Idle) + idleCycles += curTick - last_idle; + + if (tickEvent.squashed()) + tickEvent.reschedule(curTick + 1); + else if (!tickEvent.scheduled()) + tickEvent.schedule(curTick + 1); + break; + + default: + panic("can't get here"); + } + } + + // statistics + void regStats(); + + // number of simulated instructions + Counter numInst; + Statistics::Formula numInsts; + + // number of simulated memory references + Statistics::Scalar<> numMemRefs; + + // number of idle cycles + Statistics::Scalar<> idleCycles; + Statistics::Formula idleFraction; + Counter last_idle; + + // number of cycles stalled for I-cache misses + Statistics::Scalar<> icacheStallCycles; + Counter lastIcacheStall; + + // number of cycles stalled for D-cache misses + Statistics::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + void processCacheCompletion(); + + virtual void serialize(); + virtual void unserialize(IniFile &db, const std::string &category, + ConfigNode *node); + + 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); + + Fault prefetch(Addr addr, unsigned flags) + { + // need to do this... + return No_Fault; + } + + void writeHint(Addr addr, int size) + { + // need to do this... + } +}; + +#endif // __SIMPLE_CPU_HH__ diff --git a/cpu/static_inst.cc b/cpu/static_inst.cc new file mode 100644 index 000000000..cf25d5f05 --- /dev/null +++ b/cpu/static_inst.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2003 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 <iostream> +#include "static_inst.hh" +#include "universe.hh" + +template <class ISA> +StaticInstPtr<ISA> StaticInst<ISA>::nullStaticInstPtr; + +template <class ISA> +typename StaticInst<ISA>::DecodeCache StaticInst<ISA>::decodeCache; + +// Define the decode cache hash map. +template StaticInst<AlphaISA>::DecodeCache +StaticInst<AlphaISA>::decodeCache; + +template <class ISA> +void +StaticInst<ISA>::dumpDecodeCacheStats() +{ + using namespace std; + + cerr << "Decode hash table stats @ " << curTick << ":" << endl; + cerr << "\tnum entries = " << decodeCache.size() << endl; + cerr << "\tnum buckets = " << decodeCache.bucket_count() << endl; + vector<int> hist(100, 0); + int max_hist = 0; + for (int i = 0; i < decodeCache.bucket_count(); ++i) { + int count = decodeCache.elems_in_bucket(i); + if (count > max_hist) + max_hist = count; + hist[count]++; + } + for (int i = 0; i <= max_hist; ++i) { + cerr << "\tbuckets of size " << i << " = " << hist[i] << endl; + } +} + + +template StaticInstPtr<AlphaISA> +StaticInst<AlphaISA>::nullStaticInstPtr; + +template <class ISA> +bool +StaticInst<ISA>::hasBranchTarget(Addr pc, ExecContext *xc, Addr &tgt) +{ + if (isDirectCtrl()) { + tgt = branchTarget(pc); + return true; + } + + if (isIndirectCtrl()) { + tgt = branchTarget(xc); + return true; + } + + return false; +} + + +// force instantiation of template function(s) above +template StaticInst<AlphaISA>; diff --git a/cpu/static_inst.hh b/cpu/static_inst.hh new file mode 100644 index 000000000..b8f9cc00a --- /dev/null +++ b/cpu/static_inst.hh @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2003 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 __STATIC_INST_HH__ +#define __STATIC_INST_HH__ + +#include <bitset> +#include <string> + +#include "host.hh" +#include "hashmap.hh" +#include "refcnt.hh" + +#include "op_class.hh" +#include "isa_traits.hh" + +// forward declarations +class ExecContext; +class SpecExecContext; +class SimpleCPU; +class CPU; +class DynInst; +class SymbolTable; + +namespace Trace { + class InstRecord; +} + +/** + * Base, ISA-independent static instruction class. + * + * The main component of this class is the vector of flags and the + * associated methods for reading them. Any object that can rely + * solely on these flags can process instructions without being + * recompiled for multiple ISAs. + */ +class StaticInstBase : public RefCounted +{ + protected: + + /// Set of boolean static instruction properties. + /// + /// Notes: + /// - The IsInteger and IsFloating flags are based on the class of + /// registers accessed by the instruction. Although most + /// instructions will have exactly one of these two flags set, it + /// is possible for an instruction to have neither (e.g., direct + /// unconditional branches, memory barriers) or both (e.g., an + /// FP/int conversion). + /// - If IsMemRef is set, then exactly one of IsLoad or IsStore + /// will be set. Prefetches are marked as IsLoad, even if they + /// prefetch exclusive copies. + /// - If IsControl is set, then exactly one of IsDirectControl or + /// IsIndirect Control will be set, and exactly one of + /// IsCondControl or IsUncondControl will be set. + /// + enum Flags { + IsNop, ///< Is a no-op (no effect at all). + + IsInteger, ///< References integer regs. + IsFloating, ///< References FP regs. + + IsMemRef, ///< References memory (load, store, or prefetch). + IsLoad, ///< Reads from memory (load or prefetch). + IsStore, ///< Writes to memory. + IsInstPrefetch, ///< Instruction-cache prefetch. + IsDataPrefetch, ///< Data-cache prefetch. + + IsControl, ///< Control transfer instruction. + IsDirectControl, ///< PC relative control transfer. + IsIndirectControl, ///< Register indirect control transfer. + IsCondControl, ///< Conditional control transfer. + IsUncondControl, ///< Unconditional control transfer. + IsCall, ///< Subroutine call. + IsReturn, ///< Subroutine return. + + IsThreadSync, ///< Thread synchronization operation. + + NumFlags + }; + + /// Flag values for this instruction. + std::bitset<NumFlags> flags; + + /// See opClass(). + OpClass _opClass; + + /// See numSrcRegs(). + int8_t _numSrcRegs; + + /// See numDestRegs(). + int8_t _numDestRegs; + + /// The following are used to track physical register usage + /// for machines with separate int & FP reg files. + //@{ + int8_t _numFPDestRegs; + int8_t _numIntDestRegs; + //@} + + /// Constructor. + /// It's important to initialize everything here to a sane + /// default, since the decoder generally only overrides + /// the fields that are meaningful for the particular + /// instruction. + StaticInstBase(OpClass __opClass) + : _opClass(__opClass), _numSrcRegs(0), _numDestRegs(0), + _numFPDestRegs(0), _numIntDestRegs(0) + { + } + + public: + + /// @name Register information. + /// The sum of numFPDestRegs() and numIntDestRegs() equals + /// numDestRegs(). The former two functions are used to track + /// physical register usage for machines with separate int & FP + /// reg files. + //@{ + /// Number of source registers. + int8_t numSrcRegs() const { return _numSrcRegs; } + /// Number of destination registers. + int8_t numDestRegs() const { return _numDestRegs; } + /// Number of floating-point destination regs. + int8_t numFPDestRegs() const { return _numFPDestRegs; } + /// Number of integer destination regs. + int8_t numIntDestRegs() const { return _numIntDestRegs; } + //@} + + /// @name Flag accessors. + /// These functions are used to access the values of the various + /// instruction property flags. See StaticInstBase::Flags for descriptions + /// of the individual flags. + //@{ + + bool isNop() const { return flags[IsNop]; } + + bool isMemRef() const { return flags[IsMemRef]; } + bool isLoad() const { return flags[IsLoad]; } + bool isStore() const { return flags[IsStore]; } + bool isInstPrefetch() const { return flags[IsInstPrefetch]; } + bool isDataPrefetch() const { return flags[IsDataPrefetch]; } + + bool isInteger() const { return flags[IsInteger]; } + bool isFloating() const { return flags[IsFloating]; } + + bool isControl() const { return flags[IsControl]; } + bool isCall() const { return flags[IsCall]; } + bool isReturn() const { return flags[IsReturn]; } + bool isDirectCtrl() const { return flags[IsDirectControl]; } + bool isIndirectCtrl() const { return flags[IsIndirectControl]; } + bool isCondCtrl() const { return flags[IsCondControl]; } + bool isUncondCtrl() const { return flags[IsUncondControl]; } + + bool isThreadSync() const { return flags[IsThreadSync]; } + //@} + + /// Operation class. Used to select appropriate function unit in issue. + OpClass opClass() const { return _opClass; } +}; + + +// forward declaration +template <class ISA> +class StaticInstPtr; + +/** + * Generic yet ISA-dependent static instruction class. + * + * This class builds on StaticInstBase, defining fields and interfaces + * that are generic across all ISAs but that differ in details + * according to the specific ISA being used. + */ +template <class ISA> +class StaticInst : public StaticInstBase +{ + public: + + /// Binary machine instruction type. + typedef typename ISA::MachInst MachInst; + /// Memory address type. + typedef typename ISA::Addr Addr; + /// Logical register index type. + typedef typename ISA::RegIndex RegIndex; + + enum { + MaxInstSrcRegs = ISA::MaxInstSrcRegs, //< Max source regs + MaxInstDestRegs = ISA::MaxInstDestRegs, //< Max dest regs + }; + + + /// Return logical index (architectural reg num) of i'th destination reg. + /// Only the entries from 0 through numDestRegs()-1 are valid. + RegIndex destRegIdx(int i) { return _destRegIdx[i]; } + + /// Return logical index (architectural reg num) of i'th source reg. + /// Only the entries from 0 through numSrcRegs()-1 are valid. + RegIndex srcRegIdx(int i) { return _srcRegIdx[i]; } + + /// Pointer to a statically allocated "null" instruction object. + /// Used to give eaCompInst() and memAccInst() something to return + /// when called on non-memory instructions. + static StaticInstPtr<ISA> nullStaticInstPtr; + + /** + * Memory references only: returns "fake" instruction representing + * the effective address part of the memory operation. Used to + * obtain the dependence info (numSrcRegs and srcRegIdx[]) for + * just the EA computation. + */ + virtual StaticInstPtr<ISA> eaCompInst() { return nullStaticInstPtr; } + + /** + * Memory references only: returns "fake" instruction representing + * the memory access part of the memory operation. Used to + * obtain the dependence info (numSrcRegs and srcRegIdx[]) for + * just the memory access (not the EA computation). + */ + virtual StaticInstPtr<ISA> memAccInst() { return nullStaticInstPtr; } + + /// The binary machine instruction. + const MachInst machInst; + + protected: + + /// See destRegIdx(). + RegIndex _destRegIdx[MaxInstDestRegs]; + /// See srcRegIdx(). + RegIndex _srcRegIdx[MaxInstSrcRegs]; + + /** + * Base mnemonic (e.g., "add"). Used by generateDisassembly() + * methods. Also useful to readily identify instructions from + * within the debugger when #cachedDisassembly has not been + * initialized. + */ + const char *mnemonic; + + /** + * String representation of disassembly (lazily evaluated via + * disassemble()). + */ + std::string *cachedDisassembly; + + /** + * Internal function to generate disassembly string. + */ + virtual std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) = 0; + + /// Constructor. + StaticInst(const char *_mnemonic, MachInst _machInst, OpClass __opClass) + : StaticInstBase(__opClass), + machInst(_machInst), mnemonic(_mnemonic), cachedDisassembly(0) + { + } + + public: + + virtual ~StaticInst() + { + if (cachedDisassembly) + delete cachedDisassembly; + } + + /** + * Execute this instruction under SimpleCPU model. + */ + virtual Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) = 0; + + /** + * Execute this instruction under detailed CPU model. + */ + virtual Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) = 0; + + /** + * Return the target address for a PC-relative branch. + * Invalid if not a PC-relative branch (i.e. isDirectCtrl() + * should be true). + */ + virtual Addr branchTarget(Addr branchPC) + { + panic("StaticInst::branchTarget() called on instruction " + "that is not a PC-relative branch."); + } + + /** + * Return the target address for an indirect branch (jump). + * The register value is read from the supplied execution context. + * Invalid if not an indirect branch (i.e. isIndirectCtrl() + * should be true). + */ + virtual Addr branchTarget(ExecContext *xc) + { + panic("StaticInst::branchTarget() called on instruction " + "that is not an indirect branch."); + } + + /** + * Return true if the instruction is a control transfer, and if so, + * return the target address as well. + */ + bool hasBranchTarget(Addr pc, ExecContext *xc, Addr &tgt); + + /** + * Return string representation of disassembled instruction. + * The default version of this function will call the internal + * virtual generateDisassembly() function to get the string, + * then cache it in #cachedDisassembly. If the disassembly + * should not be cached, this function should be overridden directly. + */ + virtual const std::string &disassemble(Addr pc, + const SymbolTable *symtab = 0) + { + if (!cachedDisassembly) + cachedDisassembly = + new std::string(generateDisassembly(pc, symtab)); + + return *cachedDisassembly; + } + + /// Decoded instruction cache type. + /// For now we're using a generic hash_map; this seems to work + /// pretty well. + typedef m5::hash_map<MachInst, StaticInstPtr<ISA> > DecodeCache; + + /// A cache of decoded instruction objects. + static DecodeCache decodeCache; + + /** + * Dump some basic stats on the decode cache hash map. + * Only gets called if DECODE_CACHE_HASH_STATS is defined. + */ + static void dumpDecodeCacheStats(); + + /// Decode a machine instruction. + /// @param mach_inst The binary instruction to decode. + /// @retval A pointer to the corresponding StaticInst object. + static + StaticInstPtr<ISA> decode(MachInst mach_inst) + { +#ifdef DECODE_CACHE_HASH_STATS + // Simple stats on decode hash_map. Turns out the default + // hash function is as good as anything I could come up with. + const int dump_every_n = 10000000; + static int decodes_til_dump = dump_every_n; + + if (--decodes_til_dump == 0) { + dumpDecodeCacheStats(); + decodes_til_dump = dump_every_n; + } +#endif + + typename DecodeCache::iterator iter = decodeCache.find(mach_inst); + if (iter != decodeCache.end()) { + return iter->second; + } + + StaticInstPtr<ISA> si = ISA::decodeInst(mach_inst); + decodeCache[mach_inst] = si; + return si; + } +}; + +typedef RefCountingPtr<StaticInstBase> StaticInstBasePtr; + +/// Reference-counted pointer to a StaticInst object. +/// This type should be used instead of "StaticInst<ISA> *" so that +/// StaticInst objects can be properly reference-counted. +template <class ISA> +class StaticInstPtr : public RefCountingPtr<StaticInst<ISA> > +{ + public: + /// Constructor. + StaticInstPtr() + : RefCountingPtr<StaticInst<ISA> >() + { + } + + /// Conversion from "StaticInst<ISA> *". + StaticInstPtr(StaticInst<ISA> *p) + : RefCountingPtr<StaticInst<ISA> >(p) + { + } + + /// Copy constructor. + StaticInstPtr(const StaticInstPtr &r) + : RefCountingPtr<StaticInst<ISA> >(r) + { + } + + /// Construct directly from machine instruction. + /// Calls StaticInst<ISA>::decode(). + StaticInstPtr(typename ISA::MachInst mach_inst) + : RefCountingPtr<StaticInst<ISA> >(StaticInst<ISA>::decode(mach_inst)) + { + } + + /// Convert to pointer to StaticInstBase class. + operator const StaticInstBasePtr() + { + return get(); + } +}; + +#endif // __STATIC_INST_HH__ |