Files
gem5/src/cpu/o3/rob_impl.hh
Gabe Black 12311c5540 arch, base, cpu, gpu, mem: Replace assert(0 or false with panic.
Neither assert(0) nor assert(false) give any hint as to why control
getting to them is bad, and their more descriptive versions,
assert(0 && "description") and assert(false && "description"), jury
rig assert to add an error message when the utility function panic()
already does that directly with better formatting options.

This change replaces that flavor of call to assert with panic, except
in the actual code which processes the formatting that panic uses (to
avoid infinitely recurring error handling), and in some *.sm files
since I don't know what rules those have to follow and don't want to
accidentaly break them.

Change-Id: I8addfbfaf77eaed94ec8191f2ae4efb477cefdd0
Reviewed-on: https://gem5-review.googlesource.com/c/14636
Reviewed-by: Brandon Potter <Brandon.Potter@amd.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Jason Lowe-Power <jason@lowepower.com>
2018-11-27 21:58:24 +00:00

567 lines
14 KiB
C++

/*
* 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 "base/logging.hh"
#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 {
panic("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(const 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 by copying it and remove it from the list
InstIt head_it = instList[tid].begin();
DynInstPtr head_inst = std::move(*head_it);
instList[tid].erase(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();
//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>
const 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__