/*
- * Copyright (c) 2003 The Regents of The University of Michigan
+ * 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
#include "base/pollevent.hh"
#include "base/range.hh"
#include "base/trace.hh"
+#include "base/stats/events.hh"
#include "cpu/base_cpu.hh"
#include "cpu/exec_context.hh"
#include "cpu/exetrace.hh"
#include "cpu/static_inst.hh"
#include "mem/base_mem.hh"
#include "mem/mem_interface.hh"
-#include "sim/annotation.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/sim_stats.hh"
+#include "sim/stats.hh"
#ifdef FULL_SYSTEM
#include "base/remote_gdb.hh"
using namespace std;
SimpleCPU::TickEvent::TickEvent(SimpleCPU *c)
- : Event(&mainEventQueue, 100), cpu(c)
+ : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c), multiplier(1)
{
}
void
SimpleCPU::TickEvent::process()
{
- cpu->tick();
+ int count = multiplier;
+ do {
+ cpu->tick();
+ } while (--count > 0 && cpu->status() == Running);
}
const char *
Counter max_insts_all_threads,
Counter max_loads_any_thread,
Counter max_loads_all_threads,
- AlphaItb *itb, AlphaDtb *dtb,
+ AlphaITB *itb, AlphaDTB *dtb,
FunctionalMemory *mem,
MemInterface *icache_interface,
MemInterface *dcache_interface,
- Tick freq)
- : BaseCPU(_name, /* number_of_threads */ 1,
+ bool _def_reg, Tick freq,
+ bool _function_trace, Tick _function_trace_start)
+ : BaseCPU(_name, /* number_of_threads */ 1, _def_reg,
max_insts_any_thread, max_insts_all_threads,
max_loads_any_thread, max_loads_all_threads,
- _system, freq),
+ _system, freq, _function_trace, _function_trace_start),
#else
SimpleCPU::SimpleCPU(const string &_name, Process *_process,
Counter max_insts_any_thread,
Counter max_loads_any_thread,
Counter max_loads_all_threads,
MemInterface *icache_interface,
- MemInterface *dcache_interface)
- : BaseCPU(_name, /* number_of_threads */ 1,
+ MemInterface *dcache_interface,
+ bool _def_reg,
+ bool _function_trace, Tick _function_trace_start)
+ : BaseCPU(_name, /* number_of_threads */ 1, _def_reg,
max_insts_any_thread, max_insts_all_threads,
- max_loads_any_thread, max_loads_all_threads),
+ max_loads_any_thread, max_loads_all_threads,
+ _function_trace, _function_trace_start),
#endif
tickEvent(this), xc(NULL), cacheCompletionEvent(this)
{
void
-SimpleCPU::execCtxStatusChg(int thread_num) {
+SimpleCPU::activateContext(int thread_num, int delay)
+{
assert(thread_num == 0);
assert(xc);
- if (xc->status() == ExecContext::Active)
- setStatus(Running);
- else
- setStatus(Idle);
+ assert(_status == Idle);
+ notIdleFraction++;
+ scheduleTickEvent(delay);
+ _status = Running;
}
+
void
-SimpleCPU::setStatus(Status new_status)
+SimpleCPU::suspendContext(int thread_num)
{
- Status old_status = status();
-
- // We should never even get here if the CPU has been switched out.
- assert(old_status != SwitchedOut);
-
- _status = new_status;
-
- switch (status()) {
- case IcacheMissStall:
- assert(old_status == Running);
- lastIcacheStall = curTick;
- if (tickEvent.scheduled())
- tickEvent.squash();
- break;
+ assert(thread_num == 0);
+ assert(xc);
- case IcacheMissComplete:
- assert(old_status == IcacheMissStall);
- if (tickEvent.squashed())
- tickEvent.reschedule(curTick + 1);
- else if (!tickEvent.scheduled())
- tickEvent.schedule(curTick + 1);
- break;
+ assert(_status == Running);
+ notIdleFraction--;
+ unscheduleTickEvent();
+ _status = Idle;
+}
- case DcacheMissStall:
- assert(old_status == Running);
- lastDcacheStall = curTick;
- if (tickEvent.scheduled())
- tickEvent.squash();
- break;
- case Idle:
- assert(old_status == Running);
- notIdleFraction--;
- if (tickEvent.scheduled())
- tickEvent.squash();
- break;
+void
+SimpleCPU::deallocateContext(int thread_num)
+{
+ // for now, these are equivalent
+ suspendContext(thread_num);
+}
- case Running:
- assert(old_status == Idle ||
- old_status == DcacheMissStall ||
- old_status == IcacheMissComplete);
- if (old_status == Idle)
- notIdleFraction++;
-
- if (tickEvent.squashed())
- tickEvent.reschedule(curTick + 1);
- else if (!tickEvent.scheduled())
- tickEvent.schedule(curTick + 1);
- break;
- default:
- panic("can't get here");
- }
+void
+SimpleCPU::haltContext(int thread_num)
+{
+ // for now, these are equivalent
+ suspendContext(thread_num);
}
+
void
SimpleCPU::regStats()
{
- using namespace Statistics;
+ using namespace Stats;
BaseCPU::regStats();
.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")
;
idleFraction = constant(1.0) - notIdleFraction;
- numInsts = Statistics::scalar(numInst) - Statistics::scalar(startNumInst);
- simInsts += numInsts;
}
void
void
SimpleCPU::serialize(ostream &os)
{
+ BaseCPU::serialize(os);
SERIALIZE_ENUM(_status);
SERIALIZE_SCALAR(inst);
nameOut(os, csprintf("%s.xc", name()));
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));
{
}
+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)
+SimpleCPU::read(Addr addr, T &data, unsigned flags)
{
memReq->reset(addr, sizeof(T), flags);
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) {
+ if (result != MA_HIT && dcacheInterface->doEvents()) {
memReq->completionEvent = &cacheCompletionEvent;
- setStatus(DcacheMissStall);
+ lastDcacheStall = curTick;
+ unscheduleTickEvent();
+ _status = DcacheMissStall;
}
}
+ if (!dcacheInterface && (memReq->flags & UNCACHEABLE))
+ recordEvent("Uncached Read");
+
return fault;
}
template
Fault
-SimpleCPU::read(Addr addr, uint64_t& data, unsigned flags);
+SimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
template
Fault
-SimpleCPU::read(Addr addr, uint32_t& data, unsigned flags);
+SimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
template
Fault
-SimpleCPU::read(Addr addr, uint16_t& data, unsigned flags);
+SimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
template
Fault
-SimpleCPU::read(Addr addr, uint8_t& data, unsigned flags);
+SimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
#endif //DOXYGEN_SHOULD_SKIP_THIS
template<>
Fault
-SimpleCPU::read(Addr addr, double& data, unsigned flags)
+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)
+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)
+SimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
{
return read(addr, (uint32_t&)data, flags);
}
memcpy(memReq->data,(uint8_t *)&data,memReq->size);
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) {
+ if (result != MA_HIT && dcacheInterface->doEvents()) {
memReq->completionEvent = &cacheCompletionEvent;
- setStatus(DcacheMissStall);
+ lastDcacheStall = curTick;
+ unscheduleTickEvent();
+ _status = DcacheMissStall;
}
}
if (res && (fault == No_Fault))
*res = memReq->result;
+ if (!dcacheInterface && (memReq->flags & UNCACHEABLE))
+ recordEvent("Uncached Write");
+
return fault;
}
switch (status()) {
case IcacheMissStall:
icacheStallCycles += curTick - lastIcacheStall;
- setStatus(IcacheMissComplete);
+ _status = IcacheMissComplete;
+ scheduleTickEvent(1);
break;
case DcacheMissStall:
dcacheStallCycles += curTick - lastDcacheStall;
- setStatus(Running);
+ _status = Running;
+ scheduleTickEvent(1);
break;
case SwitchedOut:
// If this CPU has been switched out due to sampling/warm-up,
if (xc->status() == ExecContext::Suspended) {
DPRINTF(IPI,"Suspended Processor awoke\n");
- xc->setStatus(ExecContext::Active);
- Annotate::Resume(xc);
+ xc->activate();
}
}
#endif // FULL_SYSTEM
void
SimpleCPU::tick()
{
+ numCycles++;
+
traceData = NULL;
Fault fault = No_Fault;
#ifdef FULL_SYSTEM
- if (AlphaISA::check_interrupts &&
- xc->cpu->check_interrupts() &&
- !PC_PAL(xc->regs.pc) &&
+ if (checkInterrupts && check_interrupts() && !xc->inPalMode() &&
status() != IcacheMissComplete) {
int ipl = 0;
int summary = 0;
- AlphaISA::check_interrupts = 0;
+ checkInterrupts = false;
IntReg *ipr = xc->regs.ipr;
if (xc->regs.ipr[TheISA::IPR_SIRR]) {
// We've already fetched an instruction and were stalled on an
// I-cache miss. No need to fetch it again.
- setStatus(Running);
+ // Set status to running; tick event will get rescheduled if
+ // necessary at end of tick() function.
+ _status = Running;
}
else {
// Try to fetch an instruction
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) {
+ if (result != MA_HIT && icacheInterface->doEvents()) {
memReq->completionEvent = &cacheCompletionEvent;
- setStatus(IcacheMissStall);
+ lastIcacheStall = curTick;
+ unscheduleTickEvent();
+ _status = IcacheMissStall;
return;
}
}
// keep an instruction count
numInst++;
+ numInsts++;
// check for instruction-count-based events
- comInsnEventQueue[0]->serviceEvents(numInst);
+ comInstEventQueue[0]->serviceEvents(numInst);
// decode the instruction
+ inst = htoa(inst);
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;
+ xc->setInst(inst);
#endif // FULL_SYSTEM
- xc->func_exe_insn++;
+ xc->func_exe_inst++;
- fault = si->execute(this, xc, traceData);
-#ifdef FS_MEASURE
- if (!(xc->misspeculating()) && (xc->system->bin)) {
- SWContext *ctx = xc->swCtx;
- if (ctx && !ctx->callStack.empty()) {
- if (si->isCall()) {
- ctx->calls++;
- }
- if (si->isReturn()) {
- if (ctx->calls == 0) {
- fnCall *top = ctx->callStack.top();
- DPRINTF(TCPIP, "Removing %s from callstack.\n", top->name);
- delete top;
- ctx->callStack.pop();
- if (ctx->callStack.empty())
- xc->system->nonPath->activate();
- else
- ctx->callStack.top()->myBin->activate();
-
- xc->system->dumpState(xc);
- } else {
- ctx->calls--;
- }
- }
- }
- }
+ fault = si->execute(this, traceData);
+
+#ifdef FULL_SYSTEM
+ if (xc->fnbin)
+ xc->execute(si.get());
#endif
+
if (si->isMemRef()) {
numMemRefs++;
}
if (traceData)
traceData->finalize();
+ traceFunctions(xc->regs.pc);
+
} // if (fault == No_Fault)
if (fault != No_Fault) {
Param<Counter> max_loads_all_threads;
#ifdef FULL_SYSTEM
- SimObjectParam<AlphaItb *> itb;
- SimObjectParam<AlphaDtb *> dtb;
+ SimObjectParam<AlphaITB *> itb;
+ SimObjectParam<AlphaDTB *> dtb;
SimObjectParam<FunctionalMemory *> mem;
SimObjectParam<System *> system;
Param<int> mult;
SimObjectParam<BaseMem *> dcache;
Param<bool> defer_registration;
+ Param<int> multiplier;
+ Param<bool> function_trace;
+ Param<Tick> function_trace_start;
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",
+ "terminate when any thread reaches this inst count",
0),
INIT_PARAM_DFLT(max_insts_all_threads,
- "terminate when all threads have reached this insn count",
+ "terminate when all threads have reached this inst count",
0),
INIT_PARAM_DFLT(max_loads_any_thread,
"terminate when any thread reaches this load count",
INIT_PARAM_DFLT(icache, "L1 instruction cache object", NULL),
INIT_PARAM_DFLT(dcache, "L1 data cache object", NULL),
INIT_PARAM_DFLT(defer_registration, "defer registration with system "
- "(for sampling)", false)
+ "(for sampling)", false),
+
+ INIT_PARAM_DFLT(multiplier, "clock multiplier", 1),
+ INIT_PARAM_DFLT(function_trace, "Enable function trace", false),
+ INIT_PARAM_DFLT(function_trace_start, "Cycle to start function trace", 0)
END_INIT_SIM_OBJECT_PARAMS(SimpleCPU)
itb, dtb, mem,
(icache) ? icache->getInterface() : NULL,
(dcache) ? dcache->getInterface() : NULL,
- ticksPerSecond * mult);
+ defer_registration,
+ ticksPerSecond * mult,
+ function_trace, function_trace_start);
#else
cpu = new SimpleCPU(getInstanceName(), workload,
max_insts_any_thread, max_insts_all_threads,
max_loads_any_thread, max_loads_all_threads,
(icache) ? icache->getInterface() : NULL,
- (dcache) ? dcache->getInterface() : NULL);
+ (dcache) ? dcache->getInterface() : NULL,
+ defer_registration,
+ function_trace, function_trace_start);
#endif // FULL_SYSTEM
- if (!defer_registration) {
- cpu->registerExecContexts();
- }
+ cpu->setTickMultiplier(multiplier);
return cpu;
}
REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU)
+