| /* |
| * 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 <list> |
| |
| template <class Impl> |
| ROB<Impl>::ROB(O3CPU *_cpu, unsigned _numEntries, unsigned _squashWidth, |
| std::string _smtROBPolicy, unsigned _smtROBThreshold, |
| unsigned _numThreads) |
| : cpu(_cpu), |
| 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}"); |
| } |
| |
| // 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 <class Impl> |
| std::string |
| ROB<Impl>::name() const |
| { |
| return cpu->name() + ".rob"; |
| } |
| |
| template <class Impl> |
| void |
| ROB<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) |
| { |
| DPRINTF(ROB, "Setting active threads list pointer.\n"); |
| activeThreads = at_ptr; |
| } |
| |
| template <class Impl> |
| void |
| ROB<Impl>::switchOut() |
| { |
| for (int tid = 0; tid < numThreads; tid++) { |
| instList[tid].clear(); |
| } |
| } |
| |
| template <class Impl> |
| void |
| ROB<Impl>::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 <class Impl> |
| void |
| ROB<Impl>::resetEntries() |
| { |
| if (robPolicy != Dynamic || numThreads > 1) { |
| int active_threads = activeThreads->size(); |
| |
| std::list<unsigned>::iterator threads = activeThreads->begin(); |
| std::list<unsigned>::iterator end = activeThreads->end(); |
| |
| while (threads != end) { |
| unsigned tid = *threads++; |
| |
| if (robPolicy == Partitioned) { |
| maxEntries[tid] = numEntries / active_threads; |
| } else if (robPolicy == Threshold && active_threads == 1) { |
| maxEntries[tid] = numEntries; |
| } |
| } |
| } |
| } |
| |
| template <class Impl> |
| int |
| ROB<Impl>::entryAmount(int num_threads) |
| { |
| if (robPolicy == Partitioned) { |
| return numEntries / num_threads; |
| } else { |
| return 0; |
| } |
| } |
| |
| template <class Impl> |
| int |
| ROB<Impl>::countInsts() |
| { |
| int total=0; |
| |
| for (int i=0;i < numThreads;i++) |
| total += countInsts(i); |
| |
| return total; |
| } |
| |
| template <class Impl> |
| int |
| ROB<Impl>::countInsts(unsigned tid) |
| { |
| return instList[tid].size(); |
| } |
| |
| template <class Impl> |
| void |
| ROB<Impl>::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 <class Impl> |
| void |
| ROB<Impl>::retireHead() |
| { |
| //assert(numInstsInROB == countInsts()); |
| assert(numInstsInROB > 0); |
| |
| int tid = (*head)->threadNumber; |
| |
| retireHead(tid); |
| |
| if (numInstsInROB == 0) { |
| tail = instList[tid].end(); |
| } |
| } |
| */ |
| |
| template <class Impl> |
| void |
| ROB<Impl>::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 <class Impl> |
| bool |
| ROB<Impl>::isHeadReady() |
| { |
| if (numInstsInROB != 0) { |
| return (*head)->readyToCommit(); |
| } |
| |
| return false; |
| } |
| */ |
| template <class Impl> |
| bool |
| ROB<Impl>::isHeadReady(unsigned tid) |
| { |
| if (threadEntries[tid] != 0) { |
| return instList[tid].front()->readyToCommit(); |
| } |
| |
| return false; |
| } |
| |
| template <class Impl> |
| bool |
| ROB<Impl>::canCommit() |
| { |
| //@todo: set ActiveThreads through ROB or CPU |
| std::list<unsigned>::iterator threads = activeThreads->begin(); |
| std::list<unsigned>::iterator end = activeThreads->end(); |
| |
| while (threads != end) { |
| unsigned tid = *threads++; |
| |
| if (isHeadReady(tid)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| template <class Impl> |
| unsigned |
| ROB<Impl>::numFreeEntries() |
| { |
| //assert(numInstsInROB == countInsts()); |
| |
| return numEntries - numInstsInROB; |
| } |
| |
| template <class Impl> |
| unsigned |
| ROB<Impl>::numFreeEntries(unsigned tid) |
| { |
| return maxEntries[tid] - threadEntries[tid]; |
| } |
| |
| template <class Impl> |
| void |
| ROB<Impl>::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 <class Impl> |
| void |
| ROB<Impl>::updateHead() |
| { |
| DynInstPtr head_inst; |
| InstSeqNum lowest_num = 0; |
| bool first_valid = true; |
| |
| // @todo: set ActiveThreads through ROB or CPU |
| std::list<unsigned>::iterator threads = activeThreads->begin(); |
| std::list<unsigned>::iterator end = activeThreads->end(); |
| |
| while (threads != end) { |
| unsigned tid = *threads++; |
| |
| if (instList[tid].empty()) |
| continue; |
| |
| if (first_valid) { |
| head = instList[tid].begin(); |
| lowest_num = (*head)->seqNum; |
| first_valid = false; |
| continue; |
| } |
| |
| InstIt head_thread = instList[tid].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 <class Impl> |
| void |
| ROB<Impl>::updateTail() |
| { |
| tail = instList[0].end(); |
| bool first_valid = true; |
| |
| std::list<unsigned>::iterator threads = activeThreads->begin(); |
| std::list<unsigned>::iterator end = activeThreads->end(); |
| |
| while (threads != 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 <class Impl> |
| void |
| ROB<Impl>::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 <class Impl> |
| typename Impl::DynInstPtr |
| ROB<Impl>::readHeadInst() |
| { |
| if (numInstsInROB != 0) { |
| assert((*head)->isInROB()==true); |
| return *head; |
| } else { |
| return dummyInst; |
| } |
| } |
| */ |
| |
| template <class Impl> |
| typename Impl::DynInstPtr |
| ROB<Impl>::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 <class Impl> |
| uint64_t |
| ROB<Impl>::readHeadPC() |
| { |
| //assert(numInstsInROB == countInsts()); |
| |
| DynInstPtr head_inst = *head; |
| |
| return head_inst->readPC(); |
| } |
| |
| template <class Impl> |
| uint64_t |
| ROB<Impl>::readHeadPC(unsigned tid) |
| { |
| //assert(numInstsInROB == countInsts()); |
| InstIt head_thread = instList[tid].begin(); |
| |
| return (*head_thread)->readPC(); |
| } |
| |
| |
| template <class Impl> |
| uint64_t |
| ROB<Impl>::readHeadNextPC() |
| { |
| //assert(numInstsInROB == countInsts()); |
| |
| DynInstPtr head_inst = *head; |
| |
| return head_inst->readNextPC(); |
| } |
| |
| template <class Impl> |
| uint64_t |
| ROB<Impl>::readHeadNextPC(unsigned tid) |
| { |
| //assert(numInstsInROB == countInsts()); |
| InstIt head_thread = instList[tid].begin(); |
| |
| return (*head_thread)->readNextPC(); |
| } |
| |
| template <class Impl> |
| InstSeqNum |
| ROB<Impl>::readHeadSeqNum() |
| { |
| //assert(numInstsInROB == countInsts()); |
| DynInstPtr head_inst = *head; |
| |
| return head_inst->seqNum; |
| } |
| |
| template <class Impl> |
| InstSeqNum |
| ROB<Impl>::readHeadSeqNum(unsigned tid) |
| { |
| InstIt head_thread = instList[tid].begin(); |
| |
| return ((*head_thread)->seqNum); |
| } |
| |
| template <class Impl> |
| typename Impl::DynInstPtr |
| ROB<Impl>::readTailInst() |
| { |
| //assert(numInstsInROB == countInsts()); |
| //assert(tail != instList[0].end()); |
| |
| return (*tail); |
| } |
| */ |
| template <class Impl> |
| typename Impl::DynInstPtr |
| ROB<Impl>::readTailInst(unsigned tid) |
| { |
| //assert(tail_thread[tid] != instList[tid].end()); |
| |
| InstIt tail_thread = instList[tid].end(); |
| tail_thread--; |
| |
| return *tail_thread; |
| } |
| |
| /* |
| template <class Impl> |
| uint64_t |
| ROB<Impl>::readTailPC() |
| { |
| //assert(numInstsInROB == countInsts()); |
| |
| //assert(tail != instList[0].end()); |
| |
| return (*tail)->readPC(); |
| } |
| |
| template <class Impl> |
| uint64_t |
| ROB<Impl>::readTailPC(unsigned tid) |
| { |
| //assert(tail_thread[tid] != instList[tid].end()); |
| |
| InstIt tail_thread = instList[tid].end(); |
| tail_thread--; |
| |
| return (*tail_thread)->readPC(); |
| } |
| |
| template <class Impl> |
| InstSeqNum |
| ROB<Impl>::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 <class Impl> |
| InstSeqNum |
| ROB<Impl>::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; |
| } |
| */ |