| /* | |
| * Copyright (c) 2007 MIPS Technologies, Inc. | |
| * 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: Korey Sewell | |
| * | |
| */ | |
| #include <list> | |
| #include <vector> | |
| #include "base/str.hh" | |
| #include "cpu/inorder/cpu.hh" | |
| #include "cpu/inorder/resource.hh" | |
| #include "cpu/inorder/resource_pool.hh" | |
| #include "debug/ExecFaulting.hh" | |
| #include "debug/RefCount.hh" | |
| #include "debug/ResReqCount.hh" | |
| #include "debug/Resource.hh" | |
| using namespace std; | |
| Resource::Resource(string res_name, int res_id, int res_width, | |
| Cycles res_latency, InOrderCPU *_cpu) | |
| : resName(res_name), id(res_id), | |
| width(res_width), latency(res_latency), cpu(_cpu), | |
| resourceEvent(NULL) | |
| { | |
| reqs.resize(width); | |
| // Use to deny a instruction a resource. | |
| deniedReq = new ResourceRequest(this); | |
| deniedReq->valid = true; | |
| } | |
| Resource::~Resource() | |
| { | |
| if (resourceEvent) { | |
| delete [] resourceEvent; | |
| } | |
| delete deniedReq; | |
| for (int i = 0; i < width; i++) { | |
| delete reqs[i]; | |
| } | |
| } | |
| void | |
| Resource::init() | |
| { | |
| // If the resource has a zero-cycle (no latency) | |
| // function, then no reason to have events | |
| // that will process them for the right tick | |
| if (latency > Cycles(0)) | |
| resourceEvent = new ResourceEvent[width]; | |
| for (int i = 0; i < width; i++) | |
| reqs[i] = new ResourceRequest(this); | |
| initSlots(); | |
| } | |
| void | |
| Resource::initSlots() | |
| { | |
| // Add available slot numbers for resource | |
| for (int slot_idx = 0; slot_idx < width; slot_idx++) { | |
| availSlots.push_back(slot_idx); | |
| if (resourceEvent) { | |
| resourceEvent[slot_idx].init(this, slot_idx); | |
| } | |
| } | |
| } | |
| std::string | |
| Resource::name() | |
| { | |
| return cpu->name() + "." + resName; | |
| } | |
| int | |
| Resource::slotsAvail() | |
| { | |
| return availSlots.size(); | |
| } | |
| int | |
| Resource::slotsInUse() | |
| { | |
| return width - availSlots.size(); | |
| } | |
| void | |
| Resource::freeSlot(int slot_idx) | |
| { | |
| DPRINTF(Resource, "Deallocating [slot:%i].\n", | |
| slot_idx); | |
| // Put slot number on this resource's free list | |
| availSlots.push_back(slot_idx); | |
| // Invalidate Request & Reset it's flags | |
| reqs[slot_idx]->clearRequest(); | |
| } | |
| int | |
| Resource::findSlot(DynInstPtr inst) | |
| { | |
| int slot_num = -1; | |
| for (int i = 0; i < width; i++) { | |
| if (reqs[i]->valid && | |
| reqs[i]->getInst()->seqNum == inst->seqNum) { | |
| slot_num = reqs[i]->getSlot(); | |
| } | |
| } | |
| return slot_num; | |
| } | |
| int | |
| Resource::getSlot(DynInstPtr inst) | |
| { | |
| int slot_num = -1; | |
| if (slotsAvail() != 0) { | |
| slot_num = availSlots[0]; | |
| vector<int>::iterator vect_it = availSlots.begin(); | |
| assert(slot_num == *vect_it); | |
| availSlots.erase(vect_it); | |
| } | |
| return slot_num; | |
| } | |
| ResReqPtr | |
| Resource::request(DynInstPtr inst) | |
| { | |
| // See if the resource is already serving this instruction. | |
| // If so, use that request; | |
| bool try_request = false; | |
| int slot_num = -1; | |
| int stage_num; | |
| ResReqPtr inst_req = findRequest(inst); | |
| if (inst_req) { | |
| // If some preprocessing has to be done on instruction | |
| // that has already requested once, then handle it here. | |
| // update the 'try_request' variable if we should | |
| // re-execute the request. | |
| requestAgain(inst, try_request); | |
| slot_num = inst_req->getSlot(); | |
| stage_num = inst_req->getStageNum(); | |
| } else { | |
| // Get new slot # for instruction | |
| slot_num = getSlot(inst); | |
| if (slot_num != -1) { | |
| DPRINTF(Resource, "Allocating [slot:%i] for [tid:%i]: [sn:%i]\n", | |
| slot_num, inst->readTid(), inst->seqNum); | |
| // Get Stage # from Schedule Entry | |
| stage_num = inst->curSkedEntry->stageNum; | |
| unsigned cmd = inst->curSkedEntry->cmd; | |
| // Generate Resource Request | |
| inst_req = getRequest(inst, stage_num, id, slot_num, cmd); | |
| if (inst->staticInst) { | |
| DPRINTF(Resource, "[tid:%i]: [sn:%i] requesting this " | |
| "resource.\n", | |
| inst->readTid(), inst->seqNum); | |
| } else { | |
| DPRINTF(Resource, "[tid:%i]: instruction requesting this " | |
| "resource.\n", | |
| inst->readTid()); | |
| } | |
| try_request = true; | |
| } else { | |
| DPRINTF(Resource, "No slot available for [tid:%i]: [sn:%i]\n", | |
| inst->readTid(), inst->seqNum); | |
| } | |
| } | |
| if (try_request) { | |
| // Schedule execution of resource | |
| scheduleExecution(slot_num); | |
| } else { | |
| inst_req = deniedReq; | |
| rejectRequest(inst); | |
| } | |
| return inst_req; | |
| } | |
| void | |
| Resource::requestAgain(DynInstPtr inst, bool &do_request) | |
| { | |
| do_request = true; | |
| if (inst->staticInst) { | |
| DPRINTF(Resource, "[tid:%i]: [sn:%i] requesting this resource " | |
| "again.\n", | |
| inst->readTid(), inst->seqNum); | |
| } else { | |
| DPRINTF(Resource, "[tid:%i]: requesting this resource again.\n", | |
| inst->readTid()); | |
| } | |
| } | |
| ResReqPtr | |
| Resource::getRequest(DynInstPtr inst, int stage_num, int res_idx, | |
| int slot_num, unsigned cmd) | |
| { | |
| reqs[slot_num]->setRequest(inst, stage_num, id, slot_num, cmd); | |
| return reqs[slot_num]; | |
| } | |
| ResReqPtr | |
| Resource::findRequest(DynInstPtr inst) | |
| { | |
| for (int i = 0; i < width; i++) { | |
| if (reqs[i]->valid && | |
| reqs[i]->getInst() == inst) { | |
| return reqs[i]; | |
| } | |
| } | |
| return NULL; | |
| } | |
| void | |
| Resource::rejectRequest(DynInstPtr inst) | |
| { | |
| DPRINTF(RefCount, "[tid:%i]: Unable to grant request for [sn:%i].\n", | |
| inst->readTid(), inst->seqNum); | |
| } | |
| void | |
| Resource::execute(int slot_idx) | |
| { | |
| //@todo: have each resource print out command their executing | |
| DPRINTF(Resource, "[tid:%i]: Executing %s resource.\n", | |
| reqs[slot_idx]->getTid(), name()); | |
| reqs[slot_idx]->setCompleted(true); | |
| reqs[slot_idx]->done(); | |
| } | |
| void | |
| Resource::deactivateThread(ThreadID tid) | |
| { | |
| // In the most basic case, deactivation means squashing everything | |
| // from a particular thread | |
| DynInstPtr dummy_inst = new InOrderDynInst(cpu, NULL, 0, tid, tid); | |
| squash(dummy_inst, 0, 0, tid); | |
| } | |
| void | |
| Resource::setupSquash(DynInstPtr inst, int stage_num, ThreadID tid) | |
| { | |
| // Squash In Pipeline Stage | |
| cpu->pipelineStage[stage_num]->setupSquash(inst, tid); | |
| // Schedule Squash Through-out Resource Pool | |
| cpu->resPool->scheduleEvent( | |
| (InOrderCPU::CPUEventType)ResourcePool::SquashAll, inst, | |
| Cycles(0)); | |
| } | |
| void | |
| Resource::squash(DynInstPtr inst, int stage_num, InstSeqNum squash_seq_num, | |
| ThreadID tid) | |
| { | |
| //@todo: check squash seq num before squashing. can save time going | |
| // through this function. | |
| for (int i = 0; i < width; i++) { | |
| ResReqPtr req_ptr = reqs[i]; | |
| DynInstPtr inst = req_ptr->getInst(); | |
| if (req_ptr->valid && | |
| inst->readTid() == tid && | |
| inst->seqNum > squash_seq_num) { | |
| DPRINTF(Resource, "[tid:%i]: Squashing [sn:%i].\n", | |
| req_ptr->getInst()->readTid(), | |
| req_ptr->getInst()->seqNum); | |
| req_ptr->setSquashed(); | |
| int req_slot_num = req_ptr->getSlot(); | |
| if (latency > Cycles(0)) { | |
| if (resourceEvent[req_slot_num].scheduled()) | |
| unscheduleEvent(req_slot_num); | |
| } | |
| freeSlot(req_slot_num); | |
| } | |
| } | |
| } | |
| void | |
| Resource::squashDueToMemStall(DynInstPtr inst, int stage_num, | |
| InstSeqNum squash_seq_num, | |
| ThreadID tid) | |
| { | |
| squash(inst, stage_num, squash_seq_num, tid); | |
| } | |
| void | |
| Resource::squashThenTrap(int stage_num, DynInstPtr inst) | |
| { | |
| ThreadID tid = inst->readTid(); | |
| inst->setSquashInfo(stage_num); | |
| setupSquash(inst, stage_num, tid); | |
| if (inst->traceData) { | |
| if (inst->staticInst && | |
| inst->fault != NoFault && DTRACE(ExecFaulting)) { | |
| inst->traceData->setStageCycle(stage_num, curTick()); | |
| inst->traceData->setFetchSeq(inst->seqNum); | |
| inst->traceData->dump(); | |
| } | |
| delete inst->traceData; | |
| inst->traceData = NULL; | |
| } | |
| cpu->trapContext(inst->fault, tid, inst); | |
| } | |
| void | |
| Resource::scheduleExecution(int slot_num) | |
| { | |
| if (latency > Cycles(0)) { | |
| scheduleEvent(slot_num, latency); | |
| } else { | |
| execute(slot_num); | |
| } | |
| } | |
| void | |
| Resource::scheduleEvent(int slot_idx, Cycles delay) | |
| { | |
| DPRINTF(Resource, "[tid:%i]: Scheduling event for [sn:%i] on tick %i.\n", | |
| reqs[slot_idx]->inst->readTid(), | |
| reqs[slot_idx]->inst->seqNum, | |
| cpu->clockEdge(delay)); | |
| resourceEvent[slot_idx].scheduleEvent(delay); | |
| } | |
| bool | |
| Resource::scheduleEvent(DynInstPtr inst, Cycles delay) | |
| { | |
| int slot_idx = findSlot(inst); | |
| if(slot_idx != -1) | |
| resourceEvent[slot_idx].scheduleEvent(delay); | |
| return slot_idx; | |
| } | |
| void | |
| Resource::unscheduleEvent(int slot_idx) | |
| { | |
| resourceEvent[slot_idx].unscheduleEvent(); | |
| } | |
| bool | |
| Resource::unscheduleEvent(DynInstPtr inst) | |
| { | |
| int slot_idx = findSlot(inst); | |
| if(slot_idx != -1) | |
| resourceEvent[slot_idx].unscheduleEvent(); | |
| return slot_idx; | |
| } | |
| int ResourceRequest::resReqID = 0; | |
| int ResourceRequest::maxReqCount = 0; | |
| ResourceRequest::ResourceRequest(Resource *_res) | |
| : res(_res), inst(NULL), stagePasses(0), valid(false), doneInResource(false), | |
| completed(false), squashed(false), processing(false), | |
| memStall(false) | |
| { | |
| } | |
| ResourceRequest::~ResourceRequest() | |
| { | |
| #ifdef DEBUG | |
| res->cpu->resReqCount--; | |
| DPRINTF(ResReqCount, "Res. Req %i deleted. resReqCount=%i.\n", reqID, | |
| res->cpu->resReqCount); | |
| #endif | |
| inst = NULL; | |
| } | |
| std::string | |
| ResourceRequest::name() | |
| { | |
| return csprintf("%s[slot:%i]:", res->name(), slotNum); | |
| } | |
| void | |
| ResourceRequest::setRequest(DynInstPtr _inst, int stage_num, | |
| int res_idx, int slot_num, unsigned _cmd) | |
| { | |
| valid = true; | |
| inst = _inst; | |
| stageNum = stage_num; | |
| resIdx = res_idx; | |
| slotNum = slot_num; | |
| cmd = _cmd; | |
| } | |
| void | |
| ResourceRequest::clearRequest() | |
| { | |
| valid = false; | |
| inst = NULL; | |
| stagePasses = 0; | |
| completed = false; | |
| doneInResource = false; | |
| squashed = false; | |
| memStall = false; | |
| } | |
| void | |
| ResourceRequest::freeSlot() | |
| { | |
| assert(res); | |
| // Free Slot So Another Instruction Can Use This Resource | |
| res->freeSlot(slotNum); | |
| } | |
| void | |
| ResourceRequest::done(bool completed) | |
| { | |
| DPRINTF(Resource, "done with request from " | |
| "[sn:%i] [tid:%i].\n", | |
| inst->seqNum, inst->readTid()); | |
| setCompleted(completed); | |
| doneInResource = true; | |
| } | |
| ResourceEvent::ResourceEvent() | |
| : Event((Event::Priority)Resource_Event_Pri) | |
| { } | |
| ResourceEvent::ResourceEvent(Resource *res, int slot_idx) | |
| : Event((Event::Priority)Resource_Event_Pri), resource(res), | |
| slotIdx(slot_idx) | |
| { } | |
| void | |
| ResourceEvent::init(Resource *res, int slot_idx) | |
| { | |
| resource = res; | |
| slotIdx = slot_idx; | |
| } | |
| void | |
| ResourceEvent::process() | |
| { | |
| resource->execute(slotIdx); | |
| } | |
| const char * | |
| ResourceEvent::description() const | |
| { | |
| string desc = resource->name() + "-event:slot[" + to_string(slotIdx) | |
| + "]"; | |
| return desc.c_str(); | |
| } | |
| void | |
| ResourceEvent::scheduleEvent(Cycles delay) | |
| { | |
| assert(!scheduled() || squashed()); | |
| resource->cpu->reschedule(this, | |
| resource->cpu->clockEdge(delay), true); | |
| } |