diff options
author | Nathan Binkert <binkertn@umich.edu> | 2005-06-04 20:50:10 -0400 |
---|---|---|
committer | Nathan Binkert <binkertn@umich.edu> | 2005-06-04 20:50:10 -0400 |
commit | 13c005a8af79a8481879ce099b45a1f98faae165 (patch) | |
tree | 3125dfe10539270433981b39119dd727295c255c /cpu/simple | |
parent | 5a94e6f2cc6ed8480063da68d20274ced2930925 (diff) | |
download | gem5-13c005a8af79a8481879ce099b45a1f98faae165.tar.xz |
shuffle files around for new directory structure
--HG--
rename : cpu/base_cpu.cc => cpu/base.cc
rename : cpu/base_cpu.hh => cpu/base.hh
rename : cpu/beta_cpu/2bit_local_pred.cc => cpu/o3/2bit_local_pred.cc
rename : cpu/beta_cpu/2bit_local_pred.hh => cpu/o3/2bit_local_pred.hh
rename : cpu/beta_cpu/alpha_full_cpu.cc => cpu/o3/alpha_cpu.cc
rename : cpu/beta_cpu/alpha_full_cpu.hh => cpu/o3/alpha_cpu.hh
rename : cpu/beta_cpu/alpha_full_cpu_builder.cc => cpu/o3/alpha_cpu_builder.cc
rename : cpu/beta_cpu/alpha_full_cpu_impl.hh => cpu/o3/alpha_cpu_impl.hh
rename : cpu/beta_cpu/alpha_dyn_inst.cc => cpu/o3/alpha_dyn_inst.cc
rename : cpu/beta_cpu/alpha_dyn_inst.hh => cpu/o3/alpha_dyn_inst.hh
rename : cpu/beta_cpu/alpha_dyn_inst_impl.hh => cpu/o3/alpha_dyn_inst_impl.hh
rename : cpu/beta_cpu/alpha_impl.hh => cpu/o3/alpha_impl.hh
rename : cpu/beta_cpu/alpha_params.hh => cpu/o3/alpha_params.hh
rename : cpu/beta_cpu/bpred_unit.cc => cpu/o3/bpred_unit.cc
rename : cpu/beta_cpu/bpred_unit.hh => cpu/o3/bpred_unit.hh
rename : cpu/beta_cpu/bpred_unit_impl.hh => cpu/o3/bpred_unit_impl.hh
rename : cpu/beta_cpu/btb.cc => cpu/o3/btb.cc
rename : cpu/beta_cpu/btb.hh => cpu/o3/btb.hh
rename : cpu/beta_cpu/comm.hh => cpu/o3/comm.hh
rename : cpu/beta_cpu/commit.cc => cpu/o3/commit.cc
rename : cpu/beta_cpu/commit.hh => cpu/o3/commit.hh
rename : cpu/beta_cpu/commit_impl.hh => cpu/o3/commit_impl.hh
rename : cpu/beta_cpu/full_cpu.cc => cpu/o3/cpu.cc
rename : cpu/beta_cpu/full_cpu.hh => cpu/o3/cpu.hh
rename : cpu/beta_cpu/cpu_policy.hh => cpu/o3/cpu_policy.hh
rename : cpu/beta_cpu/decode.cc => cpu/o3/decode.cc
rename : cpu/beta_cpu/decode.hh => cpu/o3/decode.hh
rename : cpu/beta_cpu/decode_impl.hh => cpu/o3/decode_impl.hh
rename : cpu/beta_cpu/fetch.cc => cpu/o3/fetch.cc
rename : cpu/beta_cpu/fetch.hh => cpu/o3/fetch.hh
rename : cpu/beta_cpu/fetch_impl.hh => cpu/o3/fetch_impl.hh
rename : cpu/beta_cpu/free_list.cc => cpu/o3/free_list.cc
rename : cpu/beta_cpu/free_list.hh => cpu/o3/free_list.hh
rename : cpu/beta_cpu/iew.cc => cpu/o3/iew.cc
rename : cpu/beta_cpu/iew.hh => cpu/o3/iew.hh
rename : cpu/beta_cpu/iew_impl.hh => cpu/o3/iew_impl.hh
rename : cpu/beta_cpu/inst_queue.cc => cpu/o3/inst_queue.cc
rename : cpu/beta_cpu/inst_queue.hh => cpu/o3/inst_queue.hh
rename : cpu/beta_cpu/inst_queue_impl.hh => cpu/o3/inst_queue_impl.hh
rename : cpu/beta_cpu/mem_dep_unit.cc => cpu/o3/mem_dep_unit.cc
rename : cpu/beta_cpu/mem_dep_unit.hh => cpu/o3/mem_dep_unit.hh
rename : cpu/beta_cpu/mem_dep_unit_impl.hh => cpu/o3/mem_dep_unit_impl.hh
rename : cpu/beta_cpu/ras.cc => cpu/o3/ras.cc
rename : cpu/beta_cpu/ras.hh => cpu/o3/ras.hh
rename : cpu/beta_cpu/regfile.hh => cpu/o3/regfile.hh
rename : cpu/beta_cpu/rename.cc => cpu/o3/rename.cc
rename : cpu/beta_cpu/rename.hh => cpu/o3/rename.hh
rename : cpu/beta_cpu/rename_impl.hh => cpu/o3/rename_impl.hh
rename : cpu/beta_cpu/rename_map.cc => cpu/o3/rename_map.cc
rename : cpu/beta_cpu/rename_map.hh => cpu/o3/rename_map.hh
rename : cpu/beta_cpu/rob.cc => cpu/o3/rob.cc
rename : cpu/beta_cpu/rob.hh => cpu/o3/rob.hh
rename : cpu/beta_cpu/rob_impl.hh => cpu/o3/rob_impl.hh
rename : cpu/beta_cpu/sat_counter.cc => cpu/o3/sat_counter.cc
rename : cpu/beta_cpu/sat_counter.hh => cpu/o3/sat_counter.hh
rename : cpu/beta_cpu/store_set.cc => cpu/o3/store_set.cc
rename : cpu/beta_cpu/store_set.hh => cpu/o3/store_set.hh
rename : cpu/beta_cpu/tournament_pred.cc => cpu/o3/tournament_pred.cc
rename : cpu/beta_cpu/tournament_pred.hh => cpu/o3/tournament_pred.hh
rename : cpu/ooo_cpu/ooo_cpu.cc => cpu/ozone/cpu.cc
rename : cpu/ooo_cpu/ooo_cpu.hh => cpu/ozone/cpu.hh
rename : cpu/ooo_cpu/ooo_impl.hh => cpu/ozone/cpu_impl.hh
rename : cpu/ooo_cpu/ea_list.cc => cpu/ozone/ea_list.cc
rename : cpu/ooo_cpu/ea_list.hh => cpu/ozone/ea_list.hh
rename : cpu/simple_cpu/simple_cpu.cc => cpu/simple/cpu.cc
rename : cpu/simple_cpu/simple_cpu.hh => cpu/simple/cpu.hh
rename : cpu/full_cpu/smt.hh => cpu/smt.hh
rename : cpu/full_cpu/op_class.hh => encumbered/cpu/full/op_class.hh
extra : convert_revision : c4a891d8d6d3e0e9e5ea56be47d851da44d8c032
Diffstat (limited to 'cpu/simple')
-rw-r--r-- | cpu/simple/cpu.cc | 911 | ||||
-rw-r--r-- | cpu/simple/cpu.hh | 343 |
2 files changed, 1254 insertions, 0 deletions
diff --git a/cpu/simple/cpu.cc b/cpu/simple/cpu.cc new file mode 100644 index 000000000..306398ac2 --- /dev/null +++ b/cpu/simple/cpu.cc @@ -0,0 +1,911 @@ +/* + * Copyright (c) 2002-2004 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <iomanip> +#include <list> +#include <sstream> +#include <string> + +#include "base/cprintf.hh" +#include "base/inifile.hh" +#include "base/loader/symtab.hh" +#include "base/misc.hh" +#include "base/pollevent.hh" +#include "base/range.hh" +#include "base/stats/events.hh" +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/exec_context.hh" +#include "cpu/exetrace.hh" +#include "cpu/sampler/sampler.hh" +#include "cpu/simple/cpu.hh" +#include "cpu/smt.hh" +#include "cpu/static_inst.hh" +#include "mem/base_mem.hh" +#include "mem/mem_interface.hh" +#include "sim/builder.hh" +#include "sim/debug.hh" +#include "sim/host.hh" +#include "sim/sim_events.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +#ifdef FULL_SYSTEM +#include "base/remote_gdb.hh" +#include "mem/functional/memory_control.hh" +#include "mem/functional/physical.hh" +#include "sim/system.hh" +#include "targetarch/alpha_memory.hh" +#include "targetarch/vtophys.hh" +#else // !FULL_SYSTEM +#include "eio/eio.hh" +#include "mem/functional/functional.hh" +#endif // FULL_SYSTEM + +using namespace std; + + +SimpleCPU::TickEvent::TickEvent(SimpleCPU *c, int w) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c), width(w) +{ +} + +void +SimpleCPU::TickEvent::process() +{ + int count = width; + do { + cpu->tick(); + } while (--count > 0 && cpu->status() == Running); +} + +const char * +SimpleCPU::TickEvent::description() +{ + return "SimpleCPU tick event"; +} + + +SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU *_cpu) + : Event(&mainEventQueue), cpu(_cpu) +{ +} + +void SimpleCPU::CacheCompletionEvent::process() +{ + cpu->processCacheCompletion(); +} + +const char * +SimpleCPU::CacheCompletionEvent::description() +{ + return "SimpleCPU cache completion event"; +} + +SimpleCPU::SimpleCPU(Params *p) + : BaseCPU(p), tickEvent(this, p->width), xc(NULL), + cacheCompletionEvent(this) +{ + _status = Idle; +#ifdef FULL_SYSTEM + xc = new ExecContext(this, 0, p->system, p->itb, p->dtb, p->mem); + + // initialize CPU, including PC + TheISA::initCPU(&xc->regs); +#else + xc = new ExecContext(this, /* thread_num */ 0, p->process, /* asid */ 0); +#endif // !FULL_SYSTEM + + icacheInterface = p->icache_interface; + dcacheInterface = p->dcache_interface; + + memReq = new MemReq(); + memReq->xc = xc; + memReq->asid = 0; + memReq->data = new uint8_t[64]; + + numInst = 0; + startNumInst = 0; + numLoad = 0; + startNumLoad = 0; + lastIcacheStall = 0; + lastDcacheStall = 0; + + execContexts.push_back(xc); +} + +SimpleCPU::~SimpleCPU() +{ +} + +void +SimpleCPU::switchOut(SamplingCPU *s) +{ + sampler = s; + if (status() == DcacheMissStall) { + DPRINTF(Sampler,"Outstanding dcache access, waiting for completion\n"); + _status = DcacheMissSwitch; + } + else { + _status = SwitchedOut; + + if (tickEvent.scheduled()) + tickEvent.squash(); + + sampler->signalSwitched(); + } +} + + +void +SimpleCPU::takeOverFrom(BaseCPU *oldCPU) +{ + BaseCPU::takeOverFrom(oldCPU); + + assert(!tickEvent.scheduled()); + + // if any of this CPU's ExecContexts are active, mark the CPU as + // running and schedule its tick event. + for (int i = 0; i < execContexts.size(); ++i) { + ExecContext *xc = execContexts[i]; + if (xc->status() == ExecContext::Active && _status != Running) { + _status = Running; + tickEvent.schedule(curTick); + } + } +} + + +void +SimpleCPU::activateContext(int thread_num, int delay) +{ + assert(thread_num == 0); + assert(xc); + + assert(_status == Idle); + notIdleFraction++; + scheduleTickEvent(delay); + _status = Running; +} + + +void +SimpleCPU::suspendContext(int thread_num) +{ + assert(thread_num == 0); + assert(xc); + + assert(_status == Running); + notIdleFraction--; + unscheduleTickEvent(); + _status = Idle; +} + + +void +SimpleCPU::deallocateContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + + +void +SimpleCPU::haltContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + + +void +SimpleCPU::regStats() +{ + using namespace Stats; + + BaseCPU::regStats(); + + numInsts + .name(name() + ".num_insts") + .desc("Number of instructions executed") + ; + + numMemRefs + .name(name() + ".num_refs") + .desc("Number of memory references") + ; + + notIdleFraction + .name(name() + ".not_idle_fraction") + .desc("Percentage of non-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 = constant(1.0) - notIdleFraction; +} + +void +SimpleCPU::resetStats() +{ + startNumInst = numInst; + notIdleFraction = (_status != Idle); +} + +void +SimpleCPU::serialize(ostream &os) +{ + BaseCPU::serialize(os); + SERIALIZE_ENUM(_status); + SERIALIZE_SCALAR(inst); + nameOut(os, csprintf("%s.xc", name())); + xc->serialize(os); + nameOut(os, csprintf("%s.tickEvent", name())); + tickEvent.serialize(os); + nameOut(os, csprintf("%s.cacheCompletionEvent", name())); + cacheCompletionEvent.serialize(os); +} + +void +SimpleCPU::unserialize(Checkpoint *cp, const string §ion) +{ + BaseCPU::unserialize(cp, section); + UNSERIALIZE_ENUM(_status); + UNSERIALIZE_SCALAR(inst); + xc->unserialize(cp, csprintf("%s.xc", section)); + tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); + cacheCompletionEvent + .unserialize(cp, csprintf("%s.cacheCompletionEvent", section)); +} + +void +change_thread_state(int thread_number, int activate, int priority) +{ +} + +Fault +SimpleCPU::copySrcTranslate(Addr src) +{ + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + int offset = src & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (src & TheISA::PageMask) != ((src + blk_size) & TheISA::PageMask) && + (src >> 40) != 0xfffffc) { + warn("Copied block source spans pages %x.", src); + no_warn = false; + } + + memReq->reset(src & ~(blk_size - 1), blk_size); + + // translate to physical address + Fault fault = xc->translateDataReadReq(memReq); + + assert(fault != Alignment_Fault); + + if (fault == No_Fault) { + xc->copySrcAddr = src; + xc->copySrcPhysAddr = memReq->paddr + offset; + } else { + xc->copySrcAddr = 0; + xc->copySrcPhysAddr = 0; + } + return fault; +} + +Fault +SimpleCPU::copy(Addr dest) +{ + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + uint8_t data[blk_size]; + //assert(xc->copySrcAddr); + int offset = dest & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (dest & TheISA::PageMask) != ((dest + blk_size) & TheISA::PageMask) && + (dest >> 40) != 0xfffffc) { + no_warn = false; + warn("Copied block destination spans pages %x. ", dest); + } + + memReq->reset(dest & ~(blk_size -1), blk_size); + // translate to physical address + Fault fault = xc->translateDataWriteReq(memReq); + + assert(fault != Alignment_Fault); + + if (fault == No_Fault) { + Addr dest_addr = memReq->paddr + offset; + // Need to read straight from memory since we have more than 8 bytes. + memReq->paddr = xc->copySrcPhysAddr; + xc->mem->read(memReq, data); + memReq->paddr = dest_addr; + xc->mem->write(memReq, data); + if (dcacheInterface) { + memReq->cmd = Copy; + memReq->completionEvent = NULL; + memReq->paddr = xc->copySrcPhysAddr; + memReq->dest = dest_addr; + memReq->size = 64; + memReq->time = curTick; + dcacheInterface->access(memReq); + } + } + return fault; +} + +// precise architected memory state accessor macros +template <class T> +Fault +SimpleCPU::read(Addr addr, T &data, unsigned flags) +{ + if (status() == DcacheMissStall || status() == DcacheMissSwitch) { + Fault fault = xc->read(memReq,data); + + if (traceData) { + traceData->setAddr(addr); + } + return fault; + } + + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = xc->translateDataReadReq(memReq); + + // if we have a cache, do cache access too + if (fault == No_Fault && dcacheInterface) { + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + 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; + lastDcacheStall = curTick; + unscheduleTickEvent(); + _status = DcacheMissStall; + } else { + // do functional access + fault = xc->read(memReq, data); + + } + } else if(fault == No_Fault) { + // do functional access + fault = xc->read(memReq, data); + + } + + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); + + 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) +{ + 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; + memcpy(memReq->data,(uint8_t *)&data,memReq->size); + memReq->completionEvent = NULL; + memReq->time = curTick; + 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; + lastDcacheStall = curTick; + unscheduleTickEvent(); + _status = DcacheMissStall; + } + } + + if (res && (fault == No_Fault)) + *res = memReq->result; + + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); + + 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 + +void +SimpleCPU::processCacheCompletion() +{ + switch (status()) { + case IcacheMissStall: + icacheStallCycles += curTick - lastIcacheStall; + _status = IcacheMissComplete; + scheduleTickEvent(1); + break; + case DcacheMissStall: + if (memReq->cmd.isRead()) { + curStaticInst->execute(this,traceData); + if (traceData) + traceData->finalize(); + } + dcacheStallCycles += curTick - lastDcacheStall; + _status = Running; + scheduleTickEvent(1); + break; + case DcacheMissSwitch: + if (memReq->cmd.isRead()) { + curStaticInst->execute(this,traceData); + if (traceData) + traceData->finalize(); + } + _status = SwitchedOut; + sampler->signalSwitched(); + case SwitchedOut: + // If this CPU has been switched out due to sampling/warm-up, + // ignore any further status changes (e.g., due to cache + // misses outstanding at the time of the switch). + return; + 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->activate(); + } +} +#endif // FULL_SYSTEM + +/* start simulation, program loaded, processor precise state initialized */ +void +SimpleCPU::tick() +{ + numCycles++; + + traceData = NULL; + + Fault fault = No_Fault; + +#ifdef FULL_SYSTEM + if (checkInterrupts && check_interrupts() && !xc->inPalMode() && + status() != IcacheMissComplete) { + int ipl = 0; + int summary = 0; + checkInterrupts = false; + 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. + + // Set status to running; tick event will get rescheduled if + // necessary at end of tick() function. + _status = 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; + 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; + lastIcacheStall = curTick; + unscheduleTickEvent(); + _status = 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++; + numInsts++; + + // check for instruction-count-based events + comInstEventQueue[0]->serviceEvents(numInst); + + // decode the instruction + inst = htoa(inst); + curStaticInst = StaticInst<TheISA>::decode(inst); + + traceData = Trace::getInstRecord(curTick, xc, this, curStaticInst, + xc->regs.pc); + +#ifdef FULL_SYSTEM + xc->setInst(inst); +#endif // FULL_SYSTEM + + xc->func_exe_inst++; + + fault = curStaticInst->execute(this, traceData); + +#ifdef FULL_SYSTEM + if (xc->fnbin) + xc->execute(curStaticInst.get()); +#endif + + if (curStaticInst->isMemRef()) { + numMemRefs++; + } + + if (curStaticInst->isLoad()) { + ++numLoad; + comLoadEventQueue[0]->serviceEvents(numLoad); + } + + // If we have a dcache miss, then we can't finialize the instruction + // trace yet because we want to populate it with the data later + if (traceData && + !(status() == DcacheMissStall && memReq->cmd.isRead())) { + traceData->finalize(); + } + + traceFunctions(xc->regs.pc); + + } // 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 + cycles(1)); +} + + +//////////////////////////////////////////////////////////////////////// +// +// SimpleCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + +#ifdef FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<FunctionalMemory *> mem; + SimObjectParam<System *> system; + Param<int> mult; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + + Param<int> clock; + SimObjectParam<BaseMem *> icache; + SimObjectParam<BaseMem *> dcache; + + Param<bool> defer_registration; + Param<int> width; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + +#ifdef FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(mem, "memory"), + INIT_PARAM(system, "system object"), + INIT_PARAM(mult, "system clock multiplier"), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(icache, "L1 instruction cache object"), + INIT_PARAM(dcache, "L1 data cache object"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(width, "cpu width"), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(SimpleCPU) + + +CREATE_SIM_OBJECT(SimpleCPU) +{ +#ifdef FULL_SYSTEM + if (mult != 1) + panic("processor clock multiplier must be 1\n"); +#endif + + SimpleCPU::Params *params = new SimpleCPU::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + params->deferRegistration = defer_registration; + params->clock = clock; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->icache_interface = (icache) ? icache->getInterface() : NULL; + params->dcache_interface = (dcache) ? dcache->getInterface() : NULL; + params->width = width; + +#ifdef FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->mem = mem; + params->system = system; +#else + params->process = workload; +#endif + + SimpleCPU *cpu = new SimpleCPU(params); + return cpu; +} + +REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU) + diff --git a/cpu/simple/cpu.hh b/cpu/simple/cpu.hh new file mode 100644 index 000000000..9a0c2952a --- /dev/null +++ b/cpu/simple/cpu.hh @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2002-2004 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CPU_SIMPLE_CPU_SIMPLE_CPU_HH__ +#define __CPU_SIMPLE_CPU_SIMPLE_CPU_HH__ + +#include "base/statistics.hh" +#include "cpu/base.hh" +#include "cpu/exec_context.hh" +#include "cpu/pc_event.hh" +#include "cpu/sampler/sampler.hh" +#include "cpu/static_inst.hh" +#include "sim/eventq.hh" + +// forward declarations +#ifdef FULL_SYSTEM +class Processor; +class AlphaITB; +class AlphaDTB; +class PhysicalMemory; + +class RemoteGDB; +class GDBListener; + +#else + +class Process; + +#endif // FULL_SYSTEM + +class MemInterface; +class Checkpoint; + +namespace Trace { + class InstRecord; +} + +class SimpleCPU : public BaseCPU +{ + public: + // main simulation loop (one cycle) + void tick(); + + private: + struct TickEvent : public Event + { + SimpleCPU *cpu; + int width; + + TickEvent(SimpleCPU *c, int w); + void process(); + const char *description(); + }; + + TickEvent tickEvent; + + /// Schedule tick event, regardless of its current state. + void scheduleTickEvent(int numCycles) + { + if (tickEvent.squashed()) + tickEvent.reschedule(curTick + cycles(numCycles)); + else if (!tickEvent.scheduled()) + tickEvent.schedule(curTick + cycles(numCycles)); + } + + /// Unschedule tick event, regardless of its current state. + void unscheduleTickEvent() + { + if (tickEvent.scheduled()) + tickEvent.squash(); + } + + private: + Trace::InstRecord *traceData; + + public: + // + enum Status { + Running, + Idle, + IcacheMissStall, + IcacheMissComplete, + DcacheMissStall, + DcacheMissSwitch, + SwitchedOut + }; + + 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; + } + }; + + public: + struct Params : public BaseCPU::Params + { + MemInterface *icache_interface; + MemInterface *dcache_interface; + int width; +#ifdef FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; + FunctionalMemory *mem; +#else + Process *process; +#endif + }; + SimpleCPU(Params *params); + virtual ~SimpleCPU(); + + public: + // execution context + ExecContext *xc; + + void switchOut(SamplingCPU *s); + void takeOverFrom(BaseCPU *oldCPU); + +#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; + + // Refcounted pointer to the one memory request. + MemReqPtr memReq; + + // Pointer to the sampler that is telling us to switchover. + // Used to signal the completion of the pipe drain and schedule + // the next switchover + SamplingCPU *sampler; + + StaticInstPtr<TheISA> curStaticInst; + + 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 activateContext(int thread_num, int delay); + virtual void suspendContext(int thread_num); + virtual void deallocateContext(int thread_num); + virtual void haltContext(int thread_num); + + // statistics + virtual void regStats(); + virtual void resetStats(); + + // number of simulated instructions + Counter numInst; + Counter startNumInst; + Stats::Scalar<> numInsts; + + virtual Counter totalInstructions() const + { + return numInst - startNumInst; + } + + // number of simulated memory references + Stats::Scalar<> numMemRefs; + + // number of simulated loads + Counter numLoad; + Counter startNumLoad; + + // number of idle cycles + Stats::Average<> notIdleFraction; + Stats::Formula idleFraction; + + // number of cycles stalled for I-cache misses + Stats::Scalar<> icacheStallCycles; + Counter lastIcacheStall; + + // number of cycles stalled for D-cache misses + Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + void processCacheCompletion(); + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + // These functions are only used in CPU models that split + // effective address computation from the actual memory access. + void setEA(Addr EA) { panic("SimpleCPU::setEA() not implemented\n"); } + Addr getEA() { panic("SimpleCPU::getEA() not implemented\n"); } + + void prefetch(Addr addr, unsigned flags) + { + // need to do this... + } + + void writeHint(Addr addr, int size, unsigned flags) + { + // need to do this... + } + + Fault copySrcTranslate(Addr src); + + Fault copy(Addr dest); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst<TheISA> *si, int idx) + { + return xc->readIntReg(si->srcRegIdx(idx)); + } + + float readFloatRegSingle(const StaticInst<TheISA> *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return xc->readFloatRegSingle(reg_idx); + } + + double readFloatRegDouble(const StaticInst<TheISA> *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return xc->readFloatRegDouble(reg_idx); + } + + uint64_t readFloatRegInt(const StaticInst<TheISA> *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return xc->readFloatRegInt(reg_idx); + } + + void setIntReg(const StaticInst<TheISA> *si, int idx, uint64_t val) + { + xc->setIntReg(si->destRegIdx(idx), val); + } + + void setFloatRegSingle(const StaticInst<TheISA> *si, int idx, float val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + xc->setFloatRegSingle(reg_idx, val); + } + + void setFloatRegDouble(const StaticInst<TheISA> *si, int idx, double val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + xc->setFloatRegDouble(reg_idx, val); + } + + void setFloatRegInt(const StaticInst<TheISA> *si, int idx, uint64_t val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + xc->setFloatRegInt(reg_idx, val); + } + + uint64_t readPC() { return xc->readPC(); } + void setNextPC(uint64_t val) { xc->setNextPC(val); } + + uint64_t readUniq() { return xc->readUniq(); } + void setUniq(uint64_t val) { xc->setUniq(val); } + + uint64_t readFpcr() { return xc->readFpcr(); } + void setFpcr(uint64_t val) { xc->setFpcr(val); } + +#ifdef FULL_SYSTEM + uint64_t readIpr(int idx, Fault &fault) { return xc->readIpr(idx, fault); } + Fault setIpr(int idx, uint64_t val) { return xc->setIpr(idx, val); } + Fault hwrei() { return xc->hwrei(); } + int readIntrFlag() { return xc->readIntrFlag(); } + void setIntrFlag(int val) { xc->setIntrFlag(val); } + bool inPalMode() { return xc->inPalMode(); } + void ev5_trap(Fault fault) { xc->ev5_trap(fault); } + bool simPalCheck(int palFunc) { return xc->simPalCheck(palFunc); } +#else + void syscall() { xc->syscall(); } +#endif + + bool misspeculating() { return xc->misspeculating(); } + ExecContext *xcBase() { return xc; } +}; + +#endif // __CPU_SIMPLE_CPU_SIMPLE_CPU_HH__ |