/* * Copyright (c) 2004-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 * Korey Sewell */ #include "config/full_system.hh" #include "cpu/o3/rob.hh" #include template ROB::ROB(unsigned _numEntries, unsigned _squashWidth, std::string _smtROBPolicy, unsigned _smtROBThreshold, unsigned _numThreads) : numEntries(_numEntries), squashWidth(_squashWidth), numInstsInROB(0), numThreads(_numThreads) { for (int tid=0; tid < numThreads; tid++) { squashedSeqNum[tid] = 0; doneSquashing[tid] = true; threadEntries[tid] = 0; } std::string policy = _smtROBPolicy; //Convert string to lowercase std::transform(policy.begin(), policy.end(), policy.begin(), (int(*)(int)) tolower); //Figure out rob policy if (policy == "dynamic") { robPolicy = Dynamic; //Set Max Entries to Total ROB Capacity for (int i = 0; i < numThreads; i++) { maxEntries[i]=numEntries; } } else if (policy == "partitioned") { robPolicy = Partitioned; DPRINTF(Fetch, "ROB sharing policy set to Partitioned\n"); //@todo:make work if part_amt doesnt divide evenly. int part_amt = numEntries / numThreads; //Divide ROB up evenly for (int i = 0; i < numThreads; i++) { maxEntries[i]=part_amt; } } else if (policy == "threshold") { robPolicy = Threshold; DPRINTF(Fetch, "ROB sharing policy set to Threshold\n"); int threshold = _smtROBThreshold;; //Divide up by threshold amount for (int i = 0; i < numThreads; i++) { maxEntries[i]=threshold; } } else { assert(0 && "Invalid ROB Sharing Policy.Options Are:{Dynamic," "Partitioned, Threshold}"); } } template std::string ROB::name() const { return cpu->name() + ".rob"; } template void ROB::setCPU(O3CPU *cpu_ptr) { cpu = cpu_ptr; // Set the per-thread iterators to the end of the instruction list. for (int i=0; i < numThreads;i++) { squashIt[i] = instList[i].end(); } // Initialize the "universal" ROB head & tail point to invalid // pointers head = instList[0].end(); tail = instList[0].end(); } template void ROB::setActiveThreads(std::list *at_ptr) { DPRINTF(ROB, "Setting active threads list pointer.\n"); activeThreads = at_ptr; } template void ROB::switchOut() { for (int tid = 0; tid < numThreads; tid++) { instList[tid].clear(); } } template void ROB::takeOverFrom() { for (int tid=0; tid < numThreads; tid++) { doneSquashing[tid] = true; threadEntries[tid] = 0; squashIt[tid] = instList[tid].end(); } numInstsInROB = 0; // Initialize the "universal" ROB head & tail point to invalid // pointers head = instList[0].end(); tail = instList[0].end(); } template void ROB::resetEntries() { if (robPolicy != Dynamic || numThreads > 1) { int active_threads = (*activeThreads).size(); std::list::iterator threads = (*activeThreads).begin(); std::list::iterator list_end = (*activeThreads).end(); while (threads != list_end) { if (robPolicy == Partitioned) { maxEntries[*threads++] = numEntries / active_threads; } else if (robPolicy == Threshold && active_threads == 1) { maxEntries[*threads++] = numEntries; } } } } template int ROB::entryAmount(int num_threads) { if (robPolicy == Partitioned) { return numEntries / num_threads; } else { return 0; } } template int ROB::countInsts() { int total=0; for (int i=0;i < numThreads;i++) total += countInsts(i); return total; } template int ROB::countInsts(unsigned tid) { return instList[tid].size(); } template void ROB::insertInst(DynInstPtr &inst) { //assert(numInstsInROB == countInsts()); assert(inst); DPRINTF(ROB, "Adding inst PC %#x to the ROB.\n", inst->readPC()); assert(numInstsInROB != numEntries); int tid = inst->threadNumber; instList[tid].push_back(inst); //Set Up head iterator if this is the 1st instruction in the ROB if (numInstsInROB == 0) { head = instList[tid].begin(); assert((*head) == inst); } //Must Decrement for iterator to actually be valid since __.end() //actually points to 1 after the last inst tail = instList[tid].end(); tail--; inst->setInROB(); ++numInstsInROB; ++threadEntries[tid]; assert((*tail) == inst); DPRINTF(ROB, "[tid:%i] Now has %d instructions.\n", tid, threadEntries[tid]); } // Whatever calls this function needs to ensure that it properly frees up // registers prior to this function. /* template void ROB::retireHead() { //assert(numInstsInROB == countInsts()); assert(numInstsInROB > 0); int tid = (*head)->threadNumber; retireHead(tid); if (numInstsInROB == 0) { tail = instList[tid].end(); } } */ template void ROB::retireHead(unsigned tid) { //assert(numInstsInROB == countInsts()); assert(numInstsInROB > 0); // Get the head ROB instruction. InstIt head_it = instList[tid].begin(); DynInstPtr head_inst = (*head_it); assert(head_inst->readyToCommit()); DPRINTF(ROB, "[tid:%u]: Retiring head instruction, " "instruction PC %#x,[sn:%lli]\n", tid, head_inst->readPC(), head_inst->seqNum); --numInstsInROB; --threadEntries[tid]; head_inst->clearInROB(); head_inst->setCommitted(); instList[tid].erase(head_it); //Update "Global" Head of ROB updateHead(); // @todo: A special case is needed if the instruction being // retired is the only instruction in the ROB; otherwise the tail // iterator will become invalidated. cpu->removeFrontInst(head_inst); } /* template bool ROB::isHeadReady() { if (numInstsInROB != 0) { return (*head)->readyToCommit(); } return false; } */ template bool ROB::isHeadReady(unsigned tid) { if (threadEntries[tid] != 0) { return instList[tid].front()->readyToCommit(); } return false; } template bool ROB::canCommit() { //@todo: set ActiveThreads through ROB or CPU std::list::iterator threads = (*activeThreads).begin(); while (threads != (*activeThreads).end()) { unsigned tid = *threads++; if (isHeadReady(tid)) { return true; } } return false; } template unsigned ROB::numFreeEntries() { //assert(numInstsInROB == countInsts()); return numEntries - numInstsInROB; } template unsigned ROB::numFreeEntries(unsigned tid) { return maxEntries[tid] - threadEntries[tid]; } template void ROB::doSquash(unsigned tid) { DPRINTF(ROB, "[tid:%u]: Squashing instructions until [sn:%i].\n", tid, squashedSeqNum[tid]); assert(squashIt[tid] != instList[tid].end()); if ((*squashIt[tid])->seqNum < squashedSeqNum[tid]) { DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", tid); squashIt[tid] = instList[tid].end(); doneSquashing[tid] = true; return; } bool robTailUpdate = false; for (int numSquashed = 0; numSquashed < squashWidth && squashIt[tid] != instList[tid].end() && (*squashIt[tid])->seqNum > squashedSeqNum[tid]; ++numSquashed) { DPRINTF(ROB, "[tid:%u]: Squashing instruction PC %#x, seq num %i.\n", (*squashIt[tid])->threadNumber, (*squashIt[tid])->readPC(), (*squashIt[tid])->seqNum); // Mark the instruction as squashed, and ready to commit so that // it can drain out of the pipeline. (*squashIt[tid])->setSquashed(); (*squashIt[tid])->setCanCommit(); if (squashIt[tid] == instList[tid].begin()) { DPRINTF(ROB, "Reached head of instruction list while " "squashing.\n"); squashIt[tid] = instList[tid].end(); doneSquashing[tid] = true; return; } InstIt tail_thread = instList[tid].end(); tail_thread--; if ((*squashIt[tid]) == (*tail_thread)) robTailUpdate = true; squashIt[tid]--; } // Check if ROB is done squashing. if ((*squashIt[tid])->seqNum <= squashedSeqNum[tid]) { DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", tid); squashIt[tid] = instList[tid].end(); doneSquashing[tid] = true; } if (robTailUpdate) { updateTail(); } } template void ROB::updateHead() { DynInstPtr head_inst; InstSeqNum lowest_num = 0; bool first_valid = true; // @todo: set ActiveThreads through ROB or CPU std::list::iterator threads = (*activeThreads).begin(); while (threads != (*activeThreads).end()) { unsigned thread_num = *threads++; if (instList[thread_num].empty()) continue; if (first_valid) { head = instList[thread_num].begin(); lowest_num = (*head)->seqNum; first_valid = false; continue; } InstIt head_thread = instList[thread_num].begin(); DynInstPtr head_inst = (*head_thread); assert(head_inst != 0); if (head_inst->seqNum < lowest_num) { head = head_thread; lowest_num = head_inst->seqNum; } } if (first_valid) { head = instList[0].end(); } } template void ROB::updateTail() { tail = instList[0].end(); bool first_valid = true; std::list::iterator threads = (*activeThreads).begin(); while (threads != (*activeThreads).end()) { unsigned tid = *threads++; if (instList[tid].empty()) { continue; } // If this is the first valid then assign w/out // comparison if (first_valid) { tail = instList[tid].end(); tail--; first_valid = false; continue; } // Assign new tail if this thread's tail is younger // than our current "tail high" InstIt tail_thread = instList[tid].end(); tail_thread--; if ((*tail_thread)->seqNum > (*tail)->seqNum) { tail = tail_thread; } } } template void ROB::squash(InstSeqNum squash_num,unsigned tid) { if (isEmpty()) { DPRINTF(ROB, "Does not need to squash due to being empty " "[sn:%i]\n", squash_num); return; } DPRINTF(ROB, "Starting to squash within the ROB.\n"); robStatus[tid] = ROBSquashing; doneSquashing[tid] = false; squashedSeqNum[tid] = squash_num; if (!instList[tid].empty()) { InstIt tail_thread = instList[tid].end(); tail_thread--; squashIt[tid] = tail_thread; doSquash(tid); } } /* template typename Impl::DynInstPtr ROB::readHeadInst() { if (numInstsInROB != 0) { assert((*head)->isInROB()==true); return *head; } else { return dummyInst; } } */ template typename Impl::DynInstPtr ROB::readHeadInst(unsigned tid) { if (threadEntries[tid] != 0) { InstIt head_thread = instList[tid].begin(); assert((*head_thread)->isInROB()==true); return *head_thread; } else { return dummyInst; } } /* template uint64_t ROB::readHeadPC() { //assert(numInstsInROB == countInsts()); DynInstPtr head_inst = *head; return head_inst->readPC(); } template uint64_t ROB::readHeadPC(unsigned tid) { //assert(numInstsInROB == countInsts()); InstIt head_thread = instList[tid].begin(); return (*head_thread)->readPC(); } template uint64_t ROB::readHeadNextPC() { //assert(numInstsInROB == countInsts()); DynInstPtr head_inst = *head; return head_inst->readNextPC(); } template uint64_t ROB::readHeadNextPC(unsigned tid) { //assert(numInstsInROB == countInsts()); InstIt head_thread = instList[tid].begin(); return (*head_thread)->readNextPC(); } template InstSeqNum ROB::readHeadSeqNum() { //assert(numInstsInROB == countInsts()); DynInstPtr head_inst = *head; return head_inst->seqNum; } template InstSeqNum ROB::readHeadSeqNum(unsigned tid) { InstIt head_thread = instList[tid].begin(); return ((*head_thread)->seqNum); } template typename Impl::DynInstPtr ROB::readTailInst() { //assert(numInstsInROB == countInsts()); //assert(tail != instList[0].end()); return (*tail); } */ template typename Impl::DynInstPtr ROB::readTailInst(unsigned tid) { //assert(tail_thread[tid] != instList[tid].end()); InstIt tail_thread = instList[tid].end(); tail_thread--; return *tail_thread; } /* template uint64_t ROB::readTailPC() { //assert(numInstsInROB == countInsts()); //assert(tail != instList[0].end()); return (*tail)->readPC(); } template uint64_t ROB::readTailPC(unsigned tid) { //assert(tail_thread[tid] != instList[tid].end()); InstIt tail_thread = instList[tid].end(); tail_thread--; return (*tail_thread)->readPC(); } template InstSeqNum ROB::readTailSeqNum() { // Return the last sequence number that has not been squashed. Other // stages can use it to squash any instructions younger than the current // tail. return (*tail)->seqNum; } template InstSeqNum ROB::readTailSeqNum(unsigned tid) { // Return the last sequence number that has not been squashed. Other // stages can use it to squash any instructions younger than the current // tail. // assert(tail_thread[tid] != instList[tid].end()); InstIt tail_thread = instList[tid].end(); tail_thread--; return (*tail_thread)->seqNum; } */