/* * 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 */ #include "sim/faults.hh" #include "arch/types.hh" #include "cpu/ozone/inorder_back_end.hh" #include "cpu/ozone/thread_state.hh" template InorderBackEnd::InorderBackEnd(Params *params) : squashPending(false), squashSeqNum(0), squashNextPC(0), faultFromFetch(NoFault), interruptBlocked(false), cacheCompletionEvent(this), dcacheInterface(params->dcacheInterface), width(params->backEndWidth), latency(params->backEndLatency), squashLatency(params->backEndSquashLatency), numInstsToWB(0, latency + 1) { instsAdded = numInstsToWB.getWire(latency); instsToExecute = numInstsToWB.getWire(0); memReq = new MemReq; memReq->data = new uint8_t[64]; status = Running; } template std::string InorderBackEnd::name() const { return cpu->name() + ".inorderbackend"; } template void InorderBackEnd::setXC(ExecContext *xc_ptr) { xc = xc_ptr; memReq->xc = xc; } template void InorderBackEnd::setThreadState(OzoneThreadState *thread_ptr) { thread = thread_ptr; thread->setFuncExeInst(0); } #if FULL_SYSTEM template void InorderBackEnd::checkInterrupts() { //Check if there are any outstanding interrupts //Handle the interrupts int ipl = 0; int summary = 0; if (thread->readMiscRegNoEffect(IPR_ASTRR)) panic("asynchronous traps not implemented\n"); if (thread->readMiscRegNoEffect(IPR_SIRR)) { for (int i = INTLEVEL_SOFTWARE_MIN; i < INTLEVEL_SOFTWARE_MAX; i++) { if (thread->readMiscRegNoEffect(IPR_SIRR) & (ULL(1) << i)) { // See table 4-19 of the 21164 hardware reference ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; summary |= (ULL(1) << i); } } } uint64_t interrupts = cpu->intr_status(); if (interrupts) { for (int i = INTLEVEL_EXTERNAL_MIN; i < INTLEVEL_EXTERNAL_MAX; i++) { if (interrupts & (ULL(1) << i)) { // See table 4-19 of the 21164 hardware reference ipl = i; summary |= (ULL(1) << i); } } } if (ipl && ipl > thread->readMiscRegNoEffect(IPR_IPLR)) { thread->inSyscall = true; thread->setMiscRegNoEffect(IPR_ISR, summary); thread->setMiscRegNoEffect(IPR_INTID, ipl); Fault(new InterruptFault)->invoke(xc); DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", thread->readMiscRegNoEffect(IPR_IPLR), ipl, summary); // May need to go 1 inst prior squashPending = true; thread->inSyscall = false; setSquashInfoFromXC(); } } #endif template void InorderBackEnd::tick() { // Squash due to an external source // Not sure if this or an interrupt has higher priority if (squashPending) { squash(squashSeqNum, squashNextPC); return; } // if (interrupt) then set thread PC, stall front end, record that // I'm waiting for it to drain. (for now just squash) #if FULL_SYSTEM if (interruptBlocked || cpu->check_interrupts(tc)) { if (!robEmpty()) { interruptBlocked = true; //AlphaDep } else if (robEmpty() && (PC & 0x3)) { // Will need to let the front end continue a bit until // we're out of pal mode. Hopefully we never get into an // infinite loop... interruptBlocked = false; } else { interruptBlocked = false; checkInterrupts(); return; } } #endif if (status != DcacheMissLoadStall && status != DcacheMissStoreStall) { for (int i = 0; i < width && (*instsAdded) < width; ++i) { DynInstPtr inst = frontEnd->getInst(); if (!inst) break; instList.push_back(inst); (*instsAdded)++; } #if FULL_SYSTEM if (faultFromFetch && robEmpty() && frontEnd->isEmpty()) { handleFault(); } else { executeInsts(); } #else executeInsts(); #endif } } template void InorderBackEnd::executeInsts() { bool completed_last_inst = true; int insts_to_execute = *instsToExecute; int freed_regs = 0; while (insts_to_execute > 0) { assert(!instList.empty()); DynInstPtr inst = instList.front(); commitPC = inst->readPC(); thread->setPC(commitPC); thread->setNextPC(inst->readNextPC()); #if FULL_SYSTEM int count = 0; Addr oldpc; do { if (count == 0) assert(!thread->inSyscall && !thread->trapPending); oldpc = thread->readPC(); cpu->system->pcEventQueue.service( thread->getXCProxy()); count++; } while (oldpc != thread->readPC()); if (count > 1) { DPRINTF(IBE, "PC skip function event, stopping commit\n"); completed_last_inst = false; squashPending = true; break; } #endif Fault inst_fault = NoFault; if (status == DcacheMissComplete) { DPRINTF(IBE, "Completing inst [sn:%lli]\n", inst->seqNum); status = Running; } else if (inst->isMemRef() && status != DcacheMissComplete && (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { DPRINTF(IBE, "Initiating mem op inst [sn:%lli] PC: %#x\n", inst->seqNum, inst->readPC()); cacheCompletionEvent.inst = inst; inst_fault = inst->initiateAcc(); if (inst_fault == NoFault && status != DcacheMissLoadStall && status != DcacheMissStoreStall) { inst_fault = inst->completeAcc(); } ++thread->funcExeInst; } else { DPRINTF(IBE, "Executing inst [sn:%lli] PC: %#x\n", inst->seqNum, inst->readPC()); inst_fault = inst->execute(); ++thread->funcExeInst; } // Will need to be able to break this loop in case the load // misses. Split access/complete ops would be useful here // with writeback events. if (status == DcacheMissLoadStall) { *instsToExecute = insts_to_execute; completed_last_inst = false; break; } else if (status == DcacheMissStoreStall) { // Figure out how to fix this hack. Probably have DcacheMissLoad // vs DcacheMissStore. *instsToExecute = insts_to_execute; completed_last_inst = false; /* instList.pop_front(); --insts_to_execute; if (inst->traceData) { inst->traceData->finalize(); } */ // Don't really need to stop for a store stall as long as // the memory system is able to handle store forwarding // and such. Breaking out might help avoid the cache // interface becoming blocked. break; } inst->setExecuted(); inst->setResultReady(); inst->setCanCommit(); instList.pop_front(); --insts_to_execute; --(*instsToExecute); if (inst->traceData) { inst->traceData->finalize(); inst->traceData = NULL; } if (inst_fault != NoFault) { #if FULL_SYSTEM DPRINTF(IBE, "Inst [sn:%lli] PC %#x has a fault\n", inst->seqNum, inst->readPC()); assert(!thread->inSyscall); thread->inSyscall = true; // Hack for now; DTB will sometimes need the machine instruction // for when faults happen. So we will set it here, prior to the // DTB possibly needing it for this translation. thread->setInst( static_cast(inst->staticInst->machInst)); // Consider holding onto the trap and waiting until the trap event // happens for this to be executed. inst_fault->invoke(xc); // Exit state update mode to avoid accidental updating. thread->inSyscall = false; squashPending = true; // Generate trap squash event. // generateTrapEvent(tid); completed_last_inst = false; break; #else // !FULL_SYSTEM panic("fault (%d) detected @ PC %08p", inst_fault, inst->PC); #endif // FULL_SYSTEM } for (int i = 0; i < inst->numDestRegs(); ++i) { renameTable[inst->destRegIdx(i)] = inst; thread->renameTable[inst->destRegIdx(i)] = inst; ++freed_regs; } inst->clearDependents(); comm->access(0)->doneSeqNum = inst->seqNum; if (inst->mispredicted()) { squash(inst->seqNum, inst->readNextPC()); thread->setNextPC(inst->readNextPC()); break; } else if (squashPending) { // Something external happened that caused the CPU to squash. // Break out of commit and handle the squash next cycle. break; } // If it didn't mispredict, then it executed fine. Send back its // registers and BP info? What about insts that may still have // latency, like loads? Probably can send back the information after // it is completed. // keep an instruction count cpu->numInst++; thread->numInsts++; } frontEnd->addFreeRegs(freed_regs); assert(insts_to_execute >= 0); // Should only advance this if I have executed all instructions. if (insts_to_execute == 0) { numInstsToWB.advance(); } // Should I set the PC to the next PC here? What do I set next PC to? if (completed_last_inst) { thread->setPC(thread->readNextPC()); thread->setNextPC(thread->readPC() + sizeof(MachInst)); } if (squashPending) { setSquashInfoFromXC(); } } template void InorderBackEnd::handleFault() { DPRINTF(Commit, "Handling fault from fetch\n"); assert(!thread->inSyscall); thread->inSyscall = true; // Consider holding onto the trap and waiting until the trap event // happens for this to be executed. faultFromFetch->invoke(xc); // Exit state update mode to avoid accidental updating. thread->inSyscall = false; squashPending = true; setSquashInfoFromXC(); } template void InorderBackEnd::squash(const InstSeqNum &squash_num, const Addr &next_PC) { DPRINTF(IBE, "Squashing from [sn:%lli], setting PC to %#x\n", squash_num, next_PC); InstListIt squash_it = --(instList.end()); int freed_regs = 0; while (!instList.empty() && (*squash_it)->seqNum > squash_num) { DynInstPtr inst = *squash_it; DPRINTF(IBE, "Squashing instruction PC %#x, [sn:%lli].\n", inst->readPC(), inst->seqNum); // May cause problems with misc regs freed_regs+= inst->numDestRegs(); inst->clearDependents(); squash_it--; instList.pop_back(); } frontEnd->addFreeRegs(freed_regs); for (int i = 0; i < latency+1; ++i) { numInstsToWB.advance(); } squashPending = false; // Probably want to make sure that this squash is the one that set the // thread into inSyscall mode. thread->inSyscall = false; // Tell front end to squash, reset PC to new one. frontEnd->squash(squash_num, next_PC); faultFromFetch = NULL; } template void InorderBackEnd::squashFromXC() { // Record that I need to squash squashPending = true; thread->inSyscall = true; } template void InorderBackEnd::setSquashInfoFromXC() { // Need to handle the case of the instList being empty. In that case // probably any number works, except maybe with stores in the store buffer. squashSeqNum = instList.empty() ? 0 : instList.front()->seqNum - 1; squashNextPC = thread->PC; } template void InorderBackEnd::fetchFault(Fault &fault) { faultFromFetch = fault; } template void InorderBackEnd::dumpInsts() { int num = 0; int valid_num = 0; InstListIt inst_list_it = instList.begin(); cprintf("Inst list size: %i\n", instList.size()); while (inst_list_it != instList.end()) { cprintf("Instruction:%i\n", num); if (!(*inst_list_it)->isSquashed()) { if (!(*inst_list_it)->isIssued()) { ++valid_num; cprintf("Count:%i\n", valid_num); } else if ((*inst_list_it)->isMemRef() && !(*inst_list_it)->memOpDone) { // Loads that have not been marked as executed still count // towards the total instructions. ++valid_num; cprintf("Count:%i\n", valid_num); } } cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" "Issued:%i\nSquashed:%i\n", (*inst_list_it)->readPC(), (*inst_list_it)->seqNum, (*inst_list_it)->threadNumber, (*inst_list_it)->isIssued(), (*inst_list_it)->isSquashed()); if ((*inst_list_it)->isMemRef()) { cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); } cprintf("\n"); inst_list_it++; ++num; } } template InorderBackEnd::DCacheCompletionEvent::DCacheCompletionEvent( InorderBackEnd *_be) : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) { // this->setFlags(Event::AutoDelete); } template void InorderBackEnd::DCacheCompletionEvent::process() { inst->completeAcc(); be->status = DcacheMissComplete; } template const char * InorderBackEnd::DCacheCompletionEvent::description() { return "DCache completion"; }