|  | /* | 
|  | * Copyright (c) 2012 ARM Limited | 
|  | * All rights reserved | 
|  | * | 
|  | * The license below extends only to copyright in the software and shall | 
|  | * not be construed as granting a license to any other intellectual | 
|  | * property including but not limited to intellectual property relating | 
|  | * to a hardware implementation of the functionality of the software | 
|  | * licensed hereunder.  You may use the software subject to the license | 
|  | * terms below provided that you ensure that this notice is replicated | 
|  | * unmodified and in its entirety in all distributions of the software, | 
|  | * modified or unmodified, in source code or in binary form. | 
|  | * | 
|  | * 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 | 
|  | */ | 
|  |  | 
|  | #ifndef __CPU_O3_ROB_IMPL_HH__ | 
|  | #define __CPU_O3_ROB_IMPL_HH__ | 
|  |  | 
|  | #include <list> | 
|  |  | 
|  | #include "cpu/o3/rob.hh" | 
|  | #include "debug/Fetch.hh" | 
|  | #include "debug/ROB.hh" | 
|  | #include "params/DerivO3CPU.hh" | 
|  |  | 
|  | using namespace std; | 
|  |  | 
|  | template <class Impl> | 
|  | ROB<Impl>::ROB(O3CPU *_cpu, DerivO3CPUParams *params) | 
|  | : cpu(_cpu), | 
|  | numEntries(params->numROBEntries), | 
|  | squashWidth(params->squashWidth), | 
|  | numInstsInROB(0), | 
|  | numThreads(params->numThreads) | 
|  | { | 
|  | std::string policy = params->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 (ThreadID tid = 0; tid < numThreads; tid++) { | 
|  | maxEntries[tid] = 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 (ThreadID tid = 0; tid < numThreads; tid++) { | 
|  | maxEntries[tid] = part_amt; | 
|  | } | 
|  |  | 
|  | } else if (policy == "threshold") { | 
|  | robPolicy = Threshold; | 
|  | DPRINTF(Fetch, "ROB sharing policy set to Threshold\n"); | 
|  |  | 
|  | int threshold =  params->smtROBThreshold;; | 
|  |  | 
|  | //Divide up by threshold amount | 
|  | for (ThreadID tid = 0; tid < numThreads; tid++) { | 
|  | maxEntries[tid] = threshold; | 
|  | } | 
|  | } else { | 
|  | assert(0 && "Invalid ROB Sharing Policy.Options Are:{Dynamic," | 
|  | "Partitioned, Threshold}"); | 
|  | } | 
|  |  | 
|  | resetState(); | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | void | 
|  | ROB<Impl>::resetState() | 
|  | { | 
|  | for (ThreadID tid = 0; tid  < numThreads; tid++) { | 
|  | doneSquashing[tid] = true; | 
|  | threadEntries[tid] = 0; | 
|  | squashIt[tid] = instList[tid].end(); | 
|  | squashedSeqNum[tid] = 0; | 
|  | } | 
|  | numInstsInROB = 0; | 
|  |  | 
|  | // 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(list<ThreadID> *at_ptr) | 
|  | { | 
|  | DPRINTF(ROB, "Setting active threads list pointer.\n"); | 
|  | activeThreads = at_ptr; | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | void | 
|  | ROB<Impl>::drainSanityCheck() const | 
|  | { | 
|  | for (ThreadID tid = 0; tid  < numThreads; tid++) | 
|  | assert(instList[tid].empty()); | 
|  | assert(isEmpty()); | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | void | 
|  | ROB<Impl>::takeOverFrom() | 
|  | { | 
|  | resetState(); | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | void | 
|  | ROB<Impl>::resetEntries() | 
|  | { | 
|  | if (robPolicy != Dynamic || numThreads > 1) { | 
|  | int active_threads = activeThreads->size(); | 
|  |  | 
|  | list<ThreadID>::iterator threads = activeThreads->begin(); | 
|  | list<ThreadID>::iterator end = activeThreads->end(); | 
|  |  | 
|  | while (threads != end) { | 
|  | ThreadID 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(ThreadID num_threads) | 
|  | { | 
|  | if (robPolicy == Partitioned) { | 
|  | return numEntries / num_threads; | 
|  | } else { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | int | 
|  | ROB<Impl>::countInsts() | 
|  | { | 
|  | int total = 0; | 
|  |  | 
|  | for (ThreadID tid = 0; tid < numThreads; tid++) | 
|  | total += countInsts(tid); | 
|  |  | 
|  | return total; | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | int | 
|  | ROB<Impl>::countInsts(ThreadID tid) | 
|  | { | 
|  | return instList[tid].size(); | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | void | 
|  | ROB<Impl>::insertInst(DynInstPtr &inst) | 
|  | { | 
|  | assert(inst); | 
|  |  | 
|  | robWrites++; | 
|  |  | 
|  | DPRINTF(ROB, "Adding inst PC %s to the ROB.\n", inst->pcState()); | 
|  |  | 
|  | assert(numInstsInROB != numEntries); | 
|  |  | 
|  | ThreadID 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]); | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | void | 
|  | ROB<Impl>::retireHead(ThreadID tid) | 
|  | { | 
|  | robWrites++; | 
|  |  | 
|  | 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 %s, [sn:%lli]\n", tid, head_inst->pcState(), | 
|  | 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(ThreadID tid) | 
|  | { | 
|  | robReads++; | 
|  | 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 | 
|  | list<ThreadID>::iterator threads = activeThreads->begin(); | 
|  | list<ThreadID>::iterator end = activeThreads->end(); | 
|  |  | 
|  | while (threads != end) { | 
|  | ThreadID tid = *threads++; | 
|  |  | 
|  | if (isHeadReady(tid)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | unsigned | 
|  | ROB<Impl>::numFreeEntries() | 
|  | { | 
|  | return numEntries - numInstsInROB; | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | unsigned | 
|  | ROB<Impl>::numFreeEntries(ThreadID tid) | 
|  | { | 
|  | return maxEntries[tid] - threadEntries[tid]; | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | void | 
|  | ROB<Impl>::doSquash(ThreadID tid) | 
|  | { | 
|  | robWrites++; | 
|  | 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 %s, seq num %i.\n", | 
|  | (*squashIt[tid])->threadNumber, | 
|  | (*squashIt[tid])->pcState(), | 
|  | (*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() | 
|  | { | 
|  | InstSeqNum lowest_num = 0; | 
|  | bool first_valid = true; | 
|  |  | 
|  | // @todo: set ActiveThreads through ROB or CPU | 
|  | list<ThreadID>::iterator threads = activeThreads->begin(); | 
|  | list<ThreadID>::iterator end = activeThreads->end(); | 
|  |  | 
|  | while (threads != end) { | 
|  | ThreadID 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; | 
|  |  | 
|  | list<ThreadID>::iterator threads = activeThreads->begin(); | 
|  | list<ThreadID>::iterator end = activeThreads->end(); | 
|  |  | 
|  | while (threads != end) { | 
|  | ThreadID 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, ThreadID tid) | 
|  | { | 
|  | if (isEmpty(tid)) { | 
|  | 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(ThreadID tid) | 
|  | { | 
|  | if (threadEntries[tid] != 0) { | 
|  | InstIt head_thread = instList[tid].begin(); | 
|  |  | 
|  | assert((*head_thread)->isInROB()); | 
|  |  | 
|  | return *head_thread; | 
|  | } else { | 
|  | return dummyInst; | 
|  | } | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | typename Impl::DynInstPtr | 
|  | ROB<Impl>::readTailInst(ThreadID tid) | 
|  | { | 
|  | InstIt tail_thread = instList[tid].end(); | 
|  | tail_thread--; | 
|  |  | 
|  | return *tail_thread; | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | void | 
|  | ROB<Impl>::regStats() | 
|  | { | 
|  | using namespace Stats; | 
|  | robReads | 
|  | .name(name() + ".rob_reads") | 
|  | .desc("The number of ROB reads"); | 
|  |  | 
|  | robWrites | 
|  | .name(name() + ".rob_writes") | 
|  | .desc("The number of ROB writes"); | 
|  | } | 
|  |  | 
|  | template <class Impl> | 
|  | typename Impl::DynInstPtr | 
|  | ROB<Impl>::findInst(ThreadID tid, InstSeqNum squash_inst) | 
|  | { | 
|  | for (InstIt it = instList[tid].begin(); it != instList[tid].end(); it++) { | 
|  | if ((*it)->seqNum == squash_inst) { | 
|  | return *it; | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | #endif//__CPU_O3_ROB_IMPL_HH__ |