| /* |
| * Copyright (c) 2006 The Regents of The University of Michigan |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Authors: Kevin Lim |
| * Nathan Binkert |
| */ |
| |
| #include "config/full_system.hh" |
| #include "config/use_checker.hh" |
| |
| #include "arch/isa_traits.hh" // For MachInst |
| #include "base/trace.hh" |
| #include "cpu/base.hh" |
| #include "cpu/simple_thread.hh" |
| #include "cpu/thread_context.hh" |
| #include "cpu/exetrace.hh" |
| #include "cpu/ozone/cpu.hh" |
| #include "cpu/quiesce_event.hh" |
| #include "cpu/static_inst.hh" |
| #include "sim/sim_object.hh" |
| #include "sim/stats.hh" |
| |
| #if FULL_SYSTEM |
| #include "arch/faults.hh" |
| #include "arch/alpha/osfpal.hh" |
| #include "arch/tlb.hh" |
| #include "arch/types.hh" |
| #include "arch/kernel_stats.hh" |
| #include "arch/vtophys.hh" |
| #include "base/callback.hh" |
| #include "cpu/profile.hh" |
| #include "sim/faults.hh" |
| #include "sim/sim_events.hh" |
| #include "sim/sim_exit.hh" |
| #include "sim/system.hh" |
| #else // !FULL_SYSTEM |
| #include "sim/process.hh" |
| #endif // FULL_SYSTEM |
| |
| #if USE_CHECKER |
| #include "cpu/checker/thread_context.hh" |
| #endif |
| |
| using namespace TheISA; |
| |
| template <class Impl> |
| OzoneCPU<Impl>::TickEvent::TickEvent(OzoneCPU *c, int w) |
| : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c), width(w) |
| { |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::TickEvent::process() |
| { |
| cpu->tick(); |
| } |
| |
| template <class Impl> |
| const char * |
| OzoneCPU<Impl>::TickEvent::description() const |
| { |
| return "OzoneCPU tick"; |
| } |
| |
| template <class Impl> |
| OzoneCPU<Impl>::OzoneCPU(Params *p) |
| #if FULL_SYSTEM |
| : BaseCPU(p), thread(this, 0), tickEvent(this, p->width), |
| #else |
| : BaseCPU(p), thread(this, 0, p->workload[0], 0), |
| tickEvent(this, p->width), |
| #endif |
| #ifndef NDEBUG |
| instcount(0), |
| #endif |
| comm(5, 5) |
| { |
| frontEnd = new FrontEnd(p); |
| backEnd = new BackEnd(p); |
| |
| _status = Idle; |
| |
| if (p->checker) { |
| #if USE_CHECKER |
| BaseCPU *temp_checker = p->checker; |
| checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker); |
| #if FULL_SYSTEM |
| checker->setSystem(p->system); |
| #endif |
| checkerTC = new CheckerThreadContext<OzoneTC>(&ozoneTC, checker); |
| thread.tc = checkerTC; |
| tc = checkerTC; |
| #else |
| panic("Checker enabled but not compiled in!"); |
| #endif |
| } else { |
| // If checker is not being used, then the xcProxy points |
| // directly to the CPU's ExecContext. |
| checker = NULL; |
| thread.tc = &ozoneTC; |
| tc = &ozoneTC; |
| } |
| |
| ozoneTC.cpu = this; |
| ozoneTC.thread = &thread; |
| |
| thread.inSyscall = false; |
| |
| itb = p->itb; |
| dtb = p->dtb; |
| #if FULL_SYSTEM |
| // Setup thread state stuff. |
| thread.cpu = this; |
| thread.setTid(0); |
| |
| thread.quiesceEvent = new EndQuiesceEvent(tc); |
| |
| system = p->system; |
| physmem = p->system->physmem; |
| |
| if (p->profile) { |
| thread.profile = new FunctionProfile(p->system->kernelSymtab); |
| // @todo: This might be better as an ThreadContext instead of OzoneTC |
| Callback *cb = |
| new MakeCallback<OzoneTC, |
| &OzoneTC::dumpFuncProfile>(&ozoneTC); |
| registerExitCallback(cb); |
| } |
| |
| // let's fill with a dummy node for now so we don't get a segfault |
| // on the first cycle when there's no node available. |
| static ProfileNode dummyNode; |
| thread.profileNode = &dummyNode; |
| thread.profilePC = 3; |
| #else |
| thread.cpu = this; |
| #endif // !FULL_SYSTEM |
| |
| numInst = 0; |
| startNumInst = 0; |
| |
| threadContexts.push_back(tc); |
| |
| frontEnd->setCPU(this); |
| backEnd->setCPU(this); |
| |
| frontEnd->setTC(tc); |
| backEnd->setTC(tc); |
| |
| frontEnd->setThreadState(&thread); |
| backEnd->setThreadState(&thread); |
| |
| frontEnd->setCommBuffer(&comm); |
| backEnd->setCommBuffer(&comm); |
| |
| frontEnd->setBackEnd(backEnd); |
| backEnd->setFrontEnd(frontEnd); |
| |
| globalSeqNum = 1; |
| |
| lockFlag = 0; |
| |
| // Setup rename table, initializing all values to ready. |
| for (int i = 0; i < TheISA::TotalNumRegs; ++i) { |
| thread.renameTable[i] = new DynInst(this); |
| thread.renameTable[i]->setResultReady(); |
| } |
| |
| frontEnd->renameTable.copyFrom(thread.renameTable); |
| backEnd->renameTable.copyFrom(thread.renameTable); |
| |
| #if FULL_SYSTEM |
| Port *mem_port; |
| FunctionalPort *phys_port; |
| VirtualPort *virt_port; |
| phys_port = new FunctionalPort(csprintf("%s-%d-funcport", |
| name(), 0)); |
| mem_port = system->physmem->getPort("functional"); |
| mem_port->setPeer(phys_port); |
| phys_port->setPeer(mem_port); |
| |
| virt_port = new VirtualPort(csprintf("%s-%d-vport", |
| name(), 0)); |
| mem_port = system->physmem->getPort("functional"); |
| mem_port->setPeer(virt_port); |
| virt_port->setPeer(mem_port); |
| |
| thread.setPhysPort(phys_port); |
| thread.setVirtPort(virt_port); |
| #endif |
| |
| DPRINTF(OzoneCPU, "OzoneCPU: Created Ozone cpu object.\n"); |
| } |
| |
| template <class Impl> |
| OzoneCPU<Impl>::~OzoneCPU() |
| { |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::switchOut() |
| { |
| BaseCPU::switchOut(); |
| switchCount = 0; |
| // Front end needs state from back end, so switch out the back end first. |
| backEnd->switchOut(); |
| frontEnd->switchOut(); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::signalSwitched() |
| { |
| // Only complete the switchout when both the front end and back |
| // end have signalled they are ready to switch. |
| if (++switchCount == 2) { |
| backEnd->doSwitchOut(); |
| frontEnd->doSwitchOut(); |
| #if USE_CHECKER |
| if (checker) |
| checker->switchOut(); |
| #endif |
| |
| _status = SwitchedOut; |
| #ifndef NDEBUG |
| // Loop through all registers |
| for (int i = 0; i < AlphaISA::TotalNumRegs; ++i) { |
| assert(thread.renameTable[i] == frontEnd->renameTable[i]); |
| |
| assert(thread.renameTable[i] == backEnd->renameTable[i]); |
| |
| DPRINTF(OzoneCPU, "Checking if register %i matches.\n", i); |
| } |
| #endif |
| |
| if (tickEvent.scheduled()) |
| tickEvent.squash(); |
| } |
| assert(switchCount <= 2); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::takeOverFrom(BaseCPU *oldCPU) |
| { |
| BaseCPU::takeOverFrom(oldCPU); |
| |
| thread.trapPending = false; |
| thread.inSyscall = false; |
| |
| backEnd->takeOverFrom(); |
| frontEnd->takeOverFrom(); |
| frontEnd->renameTable.copyFrom(thread.renameTable); |
| backEnd->renameTable.copyFrom(thread.renameTable); |
| assert(!tickEvent.scheduled()); |
| |
| #ifndef NDEBUG |
| // Check rename table. |
| for (int i = 0; i < TheISA::TotalNumRegs; ++i) { |
| assert(thread.renameTable[i]->isResultReady()); |
| } |
| #endif |
| |
| // @todo: Fix hardcoded number |
| // Clear out any old information in time buffer. |
| for (int i = 0; i < 15; ++i) { |
| comm.advance(); |
| } |
| |
| // if any of this CPU's ThreadContexts are active, mark the CPU as |
| // running and schedule its tick event. |
| for (int i = 0; i < threadContexts.size(); ++i) { |
| ThreadContext *tc = threadContexts[i]; |
| if (tc->status() == ThreadContext::Active && |
| _status != Running) { |
| _status = Running; |
| tickEvent.schedule(curTick); |
| } |
| } |
| // Nothing running, change status to reflect that we're no longer |
| // switched out. |
| if (_status == SwitchedOut) { |
| _status = Idle; |
| } |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::activateContext(int thread_num, int delay) |
| { |
| // Eventually change this in SMT. |
| assert(thread_num == 0); |
| |
| assert(_status == Idle); |
| notIdleFraction++; |
| scheduleTickEvent(delay); |
| _status = Running; |
| #if FULL_SYSTEM |
| if (thread.quiesceEvent && thread.quiesceEvent->scheduled()) |
| thread.quiesceEvent->deschedule(); |
| #endif |
| thread.setStatus(ThreadContext::Active); |
| frontEnd->wakeFromQuiesce(); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::suspendContext(int thread_num) |
| { |
| // Eventually change this in SMT. |
| assert(thread_num == 0); |
| // @todo: Figure out how to initially set the status properly so |
| // this is running. |
| // assert(_status == Running); |
| notIdleFraction--; |
| unscheduleTickEvent(); |
| _status = Idle; |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::deallocateContext(int thread_num, int delay) |
| { |
| // for now, these are equivalent |
| suspendContext(thread_num); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::haltContext(int thread_num) |
| { |
| // for now, these are equivalent |
| suspendContext(thread_num); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::regStats() |
| { |
| using namespace Stats; |
| |
| BaseCPU::regStats(); |
| |
| thread.numInsts |
| .name(name() + ".num_insts") |
| .desc("Number of instructions executed") |
| ; |
| |
| thread.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") |
| ; |
| |
| quiesceCycles |
| .name(name() + ".quiesce_cycles") |
| .desc("Number of cycles spent in quiesce") |
| ; |
| |
| idleFraction = constant(1.0) - notIdleFraction; |
| |
| frontEnd->regStats(); |
| backEnd->regStats(); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::resetStats() |
| { |
| // startNumInst = numInst; |
| notIdleFraction = (_status != Idle); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::init() |
| { |
| BaseCPU::init(); |
| |
| // Mark this as in syscall so it won't need to squash |
| thread.inSyscall = true; |
| #if FULL_SYSTEM |
| for (int i = 0; i < threadContexts.size(); ++i) { |
| ThreadContext *tc = threadContexts[i]; |
| |
| // initialize CPU, including PC |
| TheISA::initCPU(tc, tc->contextId()); |
| } |
| #endif |
| frontEnd->renameTable.copyFrom(thread.renameTable); |
| backEnd->renameTable.copyFrom(thread.renameTable); |
| |
| thread.inSyscall = false; |
| } |
| |
| template <class Impl> |
| Port * |
| OzoneCPU<Impl>::getPort(const std::string &if_name, int idx) |
| { |
| if (if_name == "dcache_port") |
| return backEnd->getDcachePort(); |
| else if (if_name == "icache_port") |
| return frontEnd->getIcachePort(); |
| else |
| panic("No Such Port\n"); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::serialize(std::ostream &os) |
| { |
| BaseCPU::serialize(os); |
| SERIALIZE_ENUM(_status); |
| nameOut(os, csprintf("%s.tc", name())); |
| ozoneTC.serialize(os); |
| nameOut(os, csprintf("%s.tickEvent", name())); |
| tickEvent.serialize(os); |
| |
| // Use SimpleThread's ability to checkpoint to make it easier to |
| // write out the registers. Also make this static so it doesn't |
| // get instantiated multiple times (causes a panic in statistics). |
| static SimpleThread temp; |
| |
| nameOut(os, csprintf("%s.xc.0", name())); |
| temp.copyTC(thread.getTC()); |
| temp.serialize(os); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::unserialize(Checkpoint *cp, const std::string §ion) |
| { |
| BaseCPU::unserialize(cp, section); |
| UNSERIALIZE_ENUM(_status); |
| ozoneTC.unserialize(cp, csprintf("%s.tc", section)); |
| tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); |
| |
| // Use SimpleThread's ability to checkpoint to make it easier to |
| // read in the registers. Also make this static so it doesn't |
| // get instantiated multiple times (causes a panic in statistics). |
| static SimpleThread temp; |
| |
| temp.copyTC(thread.getTC()); |
| temp.unserialize(cp, csprintf("%s.xc.0", section)); |
| thread.getTC()->copyArchRegs(temp.getTC()); |
| } |
| |
| template <class Impl> |
| Fault |
| OzoneCPU<Impl>::copySrcTranslate(Addr src) |
| { |
| panic("Copy not implemented!\n"); |
| return NoFault; |
| #if 0 |
| static bool no_warn = true; |
| unsigned 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 = tc->translateDataReadReq(memReq); |
| |
| assert(fault != Alignment_Fault); |
| |
| if (fault == NoFault) { |
| tc->copySrcAddr = src; |
| tc->copySrcPhysAddr = memReq->paddr + offset; |
| } else { |
| tc->copySrcAddr = 0; |
| tc->copySrcPhysAddr = 0; |
| } |
| return fault; |
| #endif |
| } |
| |
| template <class Impl> |
| Fault |
| OzoneCPU<Impl>::copy(Addr dest) |
| { |
| panic("Copy not implemented!\n"); |
| return NoFault; |
| #if 0 |
| static bool no_warn = true; |
| unsigned blk_size = dcacheInterface ? dcacheInterface->getBlockSize() : 64; |
| // Only support block sizes of 64 atm. |
| assert(blk_size == 64); |
| uint8_t data[blk_size]; |
| //assert(tc->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 = tc->translateDataWriteReq(memReq); |
| |
| assert(fault != Alignment_Fault); |
| |
| if (fault == NoFault) { |
| Addr dest_addr = memReq->paddr + offset; |
| // Need to read straight from memory since we have more than 8 bytes. |
| memReq->paddr = tc->copySrcPhysAddr; |
| tc->mem->read(memReq, data); |
| memReq->paddr = dest_addr; |
| tc->mem->write(memReq, data); |
| if (dcacheInterface) { |
| memReq->cmd = Copy; |
| memReq->completionEvent = NULL; |
| memReq->paddr = tc->copySrcPhysAddr; |
| memReq->dest = dest_addr; |
| memReq->size = 64; |
| memReq->time = curTick; |
| dcacheInterface->access(memReq); |
| } |
| } |
| return fault; |
| #endif |
| } |
| |
| #if FULL_SYSTEM |
| template <class Impl> |
| Addr |
| OzoneCPU<Impl>::dbg_vtophys(Addr addr) |
| { |
| return vtophys(tc, addr); |
| } |
| #endif // FULL_SYSTEM |
| |
| #if FULL_SYSTEM |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::wakeup() |
| { |
| if (_status == Idle) { |
| DPRINTF(IPI,"Suspended Processor awoke\n"); |
| // thread.activate(); |
| // Hack for now. Otherwise might have to go through the tc, or |
| // I need to figure out what's the right thing to call. |
| activateContext(thread.threadId(), 1); |
| } |
| } |
| #endif // FULL_SYSTEM |
| |
| /* start simulation, program loaded, processor precise state initialized */ |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::tick() |
| { |
| DPRINTF(OzoneCPU, "\n\nOzoneCPU: Ticking cpu.\n"); |
| |
| _status = Running; |
| thread.renameTable[ZeroReg]->setIntResult(0); |
| thread.renameTable[ZeroReg+TheISA::FP_Base_DepTag]-> |
| setDoubleResult(0.0); |
| |
| comm.advance(); |
| frontEnd->tick(); |
| backEnd->tick(); |
| |
| // check for instruction-count-based events |
| comInstEventQueue[0]->serviceEvents(numInst); |
| |
| if (!tickEvent.scheduled() && _status == Running) |
| tickEvent.schedule(curTick + ticks(1)); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::squashFromTC() |
| { |
| thread.inSyscall = true; |
| backEnd->generateTCEvent(); |
| } |
| |
| #if !FULL_SYSTEM |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::syscall(uint64_t &callnum) |
| { |
| // Not sure this copy is needed, depending on how the TC proxy is made. |
| thread.renameTable.copyFrom(backEnd->renameTable); |
| |
| thread.inSyscall = true; |
| |
| thread.funcExeInst++; |
| |
| DPRINTF(OzoneCPU, "FuncExeInst: %i\n", thread.funcExeInst); |
| |
| thread.process->syscall(callnum, tc); |
| |
| thread.funcExeInst--; |
| |
| thread.inSyscall = false; |
| |
| frontEnd->renameTable.copyFrom(thread.renameTable); |
| backEnd->renameTable.copyFrom(thread.renameTable); |
| } |
| #else |
| template <class Impl> |
| Fault |
| OzoneCPU<Impl>::hwrei() |
| { |
| // Need to move this to ISA code |
| // May also need to make this per thread |
| |
| lockFlag = false; |
| lockAddrList.clear(); |
| thread.kernelStats->hwrei(); |
| |
| // FIXME: XXX check for interrupts? XXX |
| return NoFault; |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::processInterrupts() |
| { |
| // Check for interrupts here. For now can copy the code that |
| // exists within isa_fullsys_traits.hh. Also assume that thread 0 |
| // is the one that handles the interrupts. |
| |
| // Check if there are any outstanding interrupts |
| //Handle the interrupts |
| Fault interrupt = this->interrupts->getInterrupt(thread.getTC()); |
| |
| if (interrupt != NoFault) { |
| this->interrupts->updateIntrInfo(thread.getTC()); |
| interrupt->invoke(thread.getTC()); |
| } |
| } |
| |
| template <class Impl> |
| bool |
| OzoneCPU<Impl>::simPalCheck(int palFunc) |
| { |
| // Need to move this to ISA code |
| // May also need to make this per thread |
| thread.kernelStats->callpal(palFunc, tc); |
| |
| switch (palFunc) { |
| case PAL::halt: |
| haltContext(thread.threadId()); |
| if (--System::numSystemsRunning == 0) |
| exitSimLoop("all cpus halted"); |
| break; |
| |
| case PAL::bpt: |
| case PAL::bugchk: |
| if (system->breakpoint()) |
| return false; |
| break; |
| } |
| |
| return true; |
| } |
| #endif |
| |
| template <class Impl> |
| BaseCPU * |
| OzoneCPU<Impl>::OzoneTC::getCpuPtr() |
| { |
| return cpu; |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setStatus(Status new_status) |
| { |
| thread->setStatus(new_status); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::activate(int delay) |
| { |
| cpu->activateContext(thread->threadId(), delay); |
| } |
| |
| /// Set the status to Suspended. |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::suspend() |
| { |
| cpu->suspendContext(thread->threadId()); |
| } |
| |
| /// Set the status to Halted. |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::halt() |
| { |
| cpu->haltContext(thread->threadId()); |
| } |
| |
| #if FULL_SYSTEM |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::dumpFuncProfile() |
| { |
| thread->dumpFuncProfile(); |
| } |
| #endif |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::takeOverFrom(ThreadContext *old_context) |
| { |
| // some things should already be set up |
| #if FULL_SYSTEM |
| assert(getSystemPtr() == old_context->getSystemPtr()); |
| #else |
| assert(getProcessPtr() == old_context->getProcessPtr()); |
| #endif |
| |
| // copy over functional state |
| setStatus(old_context->status()); |
| copyArchRegs(old_context); |
| setCpuId(old_context->cpuId()); |
| setContextId(old_context->contextId()); |
| |
| thread->setInst(old_context->getInst()); |
| #if !FULL_SYSTEM |
| setFuncExeInst(old_context->readFuncExeInst()); |
| #else |
| EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent(); |
| if (other_quiesce) { |
| // Point the quiesce event's TC at this TC so that it wakes up |
| // the proper CPU. |
| other_quiesce->tc = this; |
| } |
| if (thread->quiesceEvent) { |
| thread->quiesceEvent->tc = this; |
| } |
| |
| // Copy kernel stats pointer from old context. |
| thread->kernelStats = old_context->getKernelStats(); |
| // storeCondFailures = 0; |
| cpu->lockFlag = false; |
| #endif |
| |
| old_context->setStatus(ThreadContext::Halted); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::regStats(const std::string &name) |
| { |
| #if FULL_SYSTEM |
| thread->kernelStats = new TheISA::Kernel::Statistics(cpu->system); |
| thread->kernelStats->regStats(name + ".kern"); |
| #endif |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::serialize(std::ostream &os) |
| { |
| // Once serialization is added, serialize the quiesce event and |
| // kernel stats. Will need to make sure there aren't multiple |
| // things that serialize them. |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::unserialize(Checkpoint *cp, const std::string §ion) |
| { } |
| |
| #if FULL_SYSTEM |
| template <class Impl> |
| EndQuiesceEvent * |
| OzoneCPU<Impl>::OzoneTC::getQuiesceEvent() |
| { |
| return thread->quiesceEvent; |
| } |
| |
| template <class Impl> |
| Tick |
| OzoneCPU<Impl>::OzoneTC::readLastActivate() |
| { |
| return thread->lastActivate; |
| } |
| |
| template <class Impl> |
| Tick |
| OzoneCPU<Impl>::OzoneTC::readLastSuspend() |
| { |
| return thread->lastSuspend; |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::profileClear() |
| { |
| thread->profileClear(); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::profileSample() |
| { |
| thread->profileSample(); |
| } |
| #endif |
| |
| template <class Impl> |
| int |
| OzoneCPU<Impl>::OzoneTC::threadId() |
| { |
| return thread->threadId(); |
| } |
| |
| template <class Impl> |
| TheISA::MachInst |
| OzoneCPU<Impl>::OzoneTC::getInst() |
| { |
| return thread->getInst(); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::copyArchRegs(ThreadContext *tc) |
| { |
| thread->PC = tc->readPC(); |
| thread->nextPC = tc->readNextPC(); |
| |
| cpu->frontEnd->setPC(thread->PC); |
| cpu->frontEnd->setNextPC(thread->nextPC); |
| |
| // First loop through the integer registers. |
| for (int i = 0; i < TheISA::NumIntRegs; ++i) { |
| /* DPRINTF(OzoneCPU, "Copying over register %i, had data %lli, " |
| "now has data %lli.\n", |
| i, thread->renameTable[i]->readIntResult(), |
| tc->readIntReg(i)); |
| */ |
| thread->renameTable[i]->setIntResult(tc->readIntReg(i)); |
| } |
| |
| // Then loop through the floating point registers. |
| for (int i = 0; i < TheISA::NumFloatRegs; ++i) { |
| int fp_idx = i + TheISA::FP_Base_DepTag; |
| thread->renameTable[fp_idx]->setIntResult(tc->readFloatRegBits(i)); |
| } |
| |
| #if !FULL_SYSTEM |
| thread->funcExeInst = tc->readFuncExeInst(); |
| #endif |
| |
| // Need to copy the TC values into the current rename table, |
| // copy the misc regs. |
| copyMiscRegs(tc, this); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::clearArchRegs() |
| { |
| panic("Unimplemented!"); |
| } |
| |
| template <class Impl> |
| uint64_t |
| OzoneCPU<Impl>::OzoneTC::readIntReg(int reg_idx) |
| { |
| return thread->renameTable[reg_idx]->readIntResult(); |
| } |
| |
| template <class Impl> |
| TheISA::FloatReg |
| OzoneCPU<Impl>::OzoneTC::readFloatReg(int reg_idx, int width) |
| { |
| int idx = reg_idx + TheISA::FP_Base_DepTag; |
| switch(width) { |
| case 32: |
| return thread->renameTable[idx]->readFloatResult(); |
| case 64: |
| return thread->renameTable[idx]->readDoubleResult(); |
| default: |
| panic("Unsupported width!"); |
| return 0; |
| } |
| } |
| |
| template <class Impl> |
| double |
| OzoneCPU<Impl>::OzoneTC::readFloatReg(int reg_idx) |
| { |
| int idx = reg_idx + TheISA::FP_Base_DepTag; |
| return thread->renameTable[idx]->readFloatResult(); |
| } |
| |
| template <class Impl> |
| uint64_t |
| OzoneCPU<Impl>::OzoneTC::readFloatRegBits(int reg_idx, int width) |
| { |
| int idx = reg_idx + TheISA::FP_Base_DepTag; |
| return thread->renameTable[idx]->readIntResult(); |
| } |
| |
| template <class Impl> |
| uint64_t |
| OzoneCPU<Impl>::OzoneTC::readFloatRegBits(int reg_idx) |
| { |
| int idx = reg_idx + TheISA::FP_Base_DepTag; |
| return thread->renameTable[idx]->readIntResult(); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setIntReg(int reg_idx, uint64_t val) |
| { |
| thread->renameTable[reg_idx]->setIntResult(val); |
| |
| if (!thread->inSyscall) { |
| cpu->squashFromTC(); |
| } |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setFloatReg(int reg_idx, FloatReg val, int width) |
| { |
| int idx = reg_idx + TheISA::FP_Base_DepTag; |
| switch(width) { |
| case 32: |
| panic("Unimplemented!"); |
| break; |
| case 64: |
| thread->renameTable[idx]->setDoubleResult(val); |
| break; |
| default: |
| panic("Unsupported width!"); |
| } |
| |
| if (!thread->inSyscall) { |
| cpu->squashFromTC(); |
| } |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setFloatReg(int reg_idx, FloatReg val) |
| { |
| int idx = reg_idx + TheISA::FP_Base_DepTag; |
| |
| thread->renameTable[idx]->setDoubleResult(val); |
| |
| if (!thread->inSyscall) { |
| cpu->squashFromTC(); |
| } |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setFloatRegBits(int reg_idx, FloatRegBits val, |
| int width) |
| { |
| panic("Unimplemented!"); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setFloatRegBits(int reg_idx, FloatRegBits val) |
| { |
| panic("Unimplemented!"); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setPC(Addr val) |
| { |
| thread->PC = val; |
| cpu->frontEnd->setPC(val); |
| |
| if (!thread->inSyscall) { |
| cpu->squashFromTC(); |
| } |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setNextPC(Addr val) |
| { |
| thread->nextPC = val; |
| cpu->frontEnd->setNextPC(val); |
| |
| if (!thread->inSyscall) { |
| cpu->squashFromTC(); |
| } |
| } |
| |
| template <class Impl> |
| TheISA::MiscReg |
| OzoneCPU<Impl>::OzoneTC::readMiscRegNoEffect(int misc_reg) |
| { |
| return thread->miscRegFile.readRegNoEffect(misc_reg); |
| } |
| |
| template <class Impl> |
| TheISA::MiscReg |
| OzoneCPU<Impl>::OzoneTC::readMiscReg(int misc_reg) |
| { |
| return thread->miscRegFile.readReg(misc_reg, this); |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setMiscRegNoEffect(int misc_reg, const MiscReg &val) |
| { |
| // Needs to setup a squash event unless we're in syscall mode |
| thread->miscRegFile.setRegNoEffect(misc_reg, val); |
| |
| if (!thread->inSyscall) { |
| cpu->squashFromTC(); |
| } |
| } |
| |
| template <class Impl> |
| void |
| OzoneCPU<Impl>::OzoneTC::setMiscReg(int misc_reg, const MiscReg &val) |
| { |
| // Needs to setup a squash event unless we're in syscall mode |
| thread->miscRegFile.setReg(misc_reg, val, this); |
| |
| if (!thread->inSyscall) { |
| cpu->squashFromTC(); |
| } |
| } |