c4793184bd
build directory instead of being inferred from the name of the build directory. Options are passed to C++ via config/*.hh files instead of via the command line. Build option flags are now always defined to 0 or 1, so checks must use '#if' rather than '#ifdef'. SConscript: MySQL detection moved to SConstruct. Add config/*.hh files (via ConfigFile builder). arch/alpha/alpha_memory.cc: arch/alpha/ev5.cc: arch/alpha/ev5.hh: arch/alpha/isa_traits.hh: base/fast_alloc.hh: base/statistics.cc: base/statistics.hh: base/stats/events.cc: base/stats/events.hh: cpu/base.cc: cpu/base.hh: cpu/base_dyn_inst.cc: cpu/base_dyn_inst.hh: cpu/exec_context.cc: cpu/exec_context.hh: cpu/o3/alpha_cpu.hh: cpu/o3/alpha_cpu_builder.cc: cpu/o3/alpha_cpu_impl.hh: cpu/o3/alpha_dyn_inst.hh: cpu/o3/alpha_dyn_inst_impl.hh: cpu/o3/alpha_params.hh: cpu/o3/commit_impl.hh: cpu/o3/cpu.cc: cpu/o3/cpu.hh: cpu/o3/fetch_impl.hh: cpu/o3/iew.hh: cpu/o3/iew_impl.hh: cpu/o3/regfile.hh: cpu/o3/rename_impl.hh: cpu/o3/rob_impl.hh: cpu/ozone/cpu.hh: cpu/pc_event.cc: cpu/simple/cpu.cc: cpu/simple/cpu.hh: sim/process.cc: sim/process.hh: Convert compile flags from def/undef to 0/1. Set via #include config/*.hh instead of command line. arch/alpha/isa_desc: Convert compile flags from def/undef to 0/1. Set via #include config/*.hh instead of command line. Revamp fenv.h support... most of the ugliness is hidden in base/fenv.hh now. base/mysql.hh: Fix typo in #ifndef guard. build/SConstruct: Build options are set via a build_options file in the build directory instead of being inferred from the name of the build directory. Options are passed to C++ via config/*.hh files instead of via the command line. python/SConscript: Generate m5_build_env directly from scons options instead of indirectly via CPPDEFINES. python/m5/convert.py: Allow '0' and '1' for booleans. Rewrite toBool to use dict. base/fenv.hh: Revamp <fenv.h> support to make it a compile option (so we can test w/o it even if it's present) and to make isa_desc cleaner. --HG-- extra : convert_revision : 8f97dc11185bef5e1865b3269c7341df8525c9ad
502 lines
17 KiB
C++
502 lines
17 KiB
C++
/*
|
|
* Copyright (c) 2004-2005 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.
|
|
*/
|
|
|
|
#include "base/timebuf.hh"
|
|
#include "cpu/o3/commit.hh"
|
|
#include "cpu/exetrace.hh"
|
|
|
|
template <class Impl>
|
|
SimpleCommit<Impl>::SimpleCommit(Params ¶ms)
|
|
: dcacheInterface(params.dcacheInterface),
|
|
iewToCommitDelay(params.iewToCommitDelay),
|
|
renameToROBDelay(params.renameToROBDelay),
|
|
renameWidth(params.renameWidth),
|
|
iewWidth(params.executeWidth),
|
|
commitWidth(params.commitWidth)
|
|
{
|
|
_status = Idle;
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::regStats()
|
|
{
|
|
commitCommittedInsts
|
|
.name(name() + ".commitCommittedInsts")
|
|
.desc("The number of committed instructions")
|
|
.prereq(commitCommittedInsts);
|
|
commitSquashedInsts
|
|
.name(name() + ".commitSquashedInsts")
|
|
.desc("The number of squashed insts skipped by commit")
|
|
.prereq(commitSquashedInsts);
|
|
commitSquashEvents
|
|
.name(name() + ".commitSquashEvents")
|
|
.desc("The number of times commit is told to squash")
|
|
.prereq(commitSquashEvents);
|
|
commitNonSpecStalls
|
|
.name(name() + ".commitNonSpecStalls")
|
|
.desc("The number of times commit has been forced to stall to "
|
|
"communicate backwards")
|
|
.prereq(commitNonSpecStalls);
|
|
commitCommittedBranches
|
|
.name(name() + ".commitCommittedBranches")
|
|
.desc("The number of committed branches")
|
|
.prereq(commitCommittedBranches);
|
|
commitCommittedLoads
|
|
.name(name() + ".commitCommittedLoads")
|
|
.desc("The number of committed loads")
|
|
.prereq(commitCommittedLoads);
|
|
commitCommittedMemRefs
|
|
.name(name() + ".commitCommittedMemRefs")
|
|
.desc("The number of committed memory references")
|
|
.prereq(commitCommittedMemRefs);
|
|
branchMispredicts
|
|
.name(name() + ".branchMispredicts")
|
|
.desc("The number of times a branch was mispredicted")
|
|
.prereq(branchMispredicts);
|
|
n_committed_dist
|
|
.init(0,commitWidth,1)
|
|
.name(name() + ".COM:committed_per_cycle")
|
|
.desc("Number of insts commited each cycle")
|
|
.flags(Stats::pdf)
|
|
;
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::setCPU(FullCPU *cpu_ptr)
|
|
{
|
|
DPRINTF(Commit, "Commit: Setting CPU pointer.\n");
|
|
cpu = cpu_ptr;
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
|
|
{
|
|
DPRINTF(Commit, "Commit: Setting time buffer pointer.\n");
|
|
timeBuffer = tb_ptr;
|
|
|
|
// Setup wire to send information back to IEW.
|
|
toIEW = timeBuffer->getWire(0);
|
|
|
|
// Setup wire to read data from IEW (for the ROB).
|
|
robInfoFromIEW = timeBuffer->getWire(-iewToCommitDelay);
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
|
|
{
|
|
DPRINTF(Commit, "Commit: Setting rename queue pointer.\n");
|
|
renameQueue = rq_ptr;
|
|
|
|
// Setup wire to get instructions from rename (for the ROB).
|
|
fromRename = renameQueue->getWire(-renameToROBDelay);
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
|
|
{
|
|
DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n");
|
|
iewQueue = iq_ptr;
|
|
|
|
// Setup wire to get instructions from IEW.
|
|
fromIEW = iewQueue->getWire(-iewToCommitDelay);
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::setROB(ROB *rob_ptr)
|
|
{
|
|
DPRINTF(Commit, "Commit: Setting ROB pointer.\n");
|
|
rob = rob_ptr;
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::tick()
|
|
{
|
|
// If the ROB is currently in its squash sequence, then continue
|
|
// to squash. In this case, commit does not do anything. Otherwise
|
|
// run commit.
|
|
if (_status == ROBSquashing) {
|
|
if (rob->isDoneSquashing()) {
|
|
_status = Running;
|
|
} else {
|
|
rob->doSquash();
|
|
|
|
// Send back sequence number of tail of ROB, so other stages
|
|
// can squash younger instructions. Note that really the only
|
|
// stage that this is important for is the IEW stage; other
|
|
// stages can just clear all their state as long as selective
|
|
// replay isn't used.
|
|
toIEW->commitInfo.doneSeqNum = rob->readTailSeqNum();
|
|
toIEW->commitInfo.robSquashing = true;
|
|
}
|
|
} else {
|
|
commit();
|
|
}
|
|
|
|
markCompletedInsts();
|
|
|
|
// Writeback number of free ROB entries here.
|
|
DPRINTF(Commit, "Commit: ROB has %d free entries.\n",
|
|
rob->numFreeEntries());
|
|
toIEW->commitInfo.freeROBEntries = rob->numFreeEntries();
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::commit()
|
|
{
|
|
//////////////////////////////////////
|
|
// Check for interrupts
|
|
//////////////////////////////////////
|
|
|
|
// Process interrupts if interrupts are enabled and not in PAL mode.
|
|
// Take the PC from commit and write it to the IPR, then squash. The
|
|
// interrupt completing will take care of restoring the PC from that value
|
|
// in the IPR. Look at IPR[EXC_ADDR];
|
|
// hwrei() is what resets the PC to the place where instruction execution
|
|
// beings again.
|
|
#if FULL_SYSTEM
|
|
if (//checkInterrupts &&
|
|
cpu->check_interrupts() &&
|
|
!cpu->inPalMode(readCommitPC())) {
|
|
// Will need to squash all instructions currently in flight and have
|
|
// the interrupt handler restart at the last non-committed inst.
|
|
// Most of that can be handled through the trap() function. The
|
|
// processInterrupts() function really just checks for interrupts
|
|
// and then calls trap() if there is an interrupt present.
|
|
|
|
// CPU will handle implementation of the interrupt.
|
|
cpu->processInterrupts();
|
|
}
|
|
#endif // FULL_SYSTEM
|
|
|
|
////////////////////////////////////
|
|
// Check for squash signal, handle that first
|
|
////////////////////////////////////
|
|
|
|
// Want to mainly check if the IEW stage is telling the ROB to squash.
|
|
// Should I also check if the commit stage is telling the ROB to squah?
|
|
// This might be necessary to keep the same timing between the IQ and
|
|
// the ROB...
|
|
if (fromIEW->squash) {
|
|
DPRINTF(Commit, "Commit: Squashing instructions in the ROB.\n");
|
|
|
|
_status = ROBSquashing;
|
|
|
|
InstSeqNum squashed_inst = fromIEW->squashedSeqNum;
|
|
|
|
rob->squash(squashed_inst);
|
|
|
|
// Send back the sequence number of the squashed instruction.
|
|
toIEW->commitInfo.doneSeqNum = squashed_inst;
|
|
|
|
// Send back the squash signal to tell stages that they should squash.
|
|
toIEW->commitInfo.squash = true;
|
|
|
|
// Send back the rob squashing signal so other stages know that the
|
|
// ROB is in the process of squashing.
|
|
toIEW->commitInfo.robSquashing = true;
|
|
|
|
toIEW->commitInfo.branchMispredict = fromIEW->branchMispredict;
|
|
|
|
toIEW->commitInfo.branchTaken = fromIEW->branchTaken;
|
|
|
|
toIEW->commitInfo.nextPC = fromIEW->nextPC;
|
|
|
|
toIEW->commitInfo.mispredPC = fromIEW->mispredPC;
|
|
|
|
if (toIEW->commitInfo.branchMispredict) {
|
|
++branchMispredicts;
|
|
}
|
|
}
|
|
|
|
if (_status != ROBSquashing) {
|
|
// If we're not currently squashing, then get instructions.
|
|
getInsts();
|
|
|
|
// Try to commit any instructions.
|
|
commitInsts();
|
|
}
|
|
|
|
// If the ROB is empty, we can set this stage to idle. Use this
|
|
// in the future when the Idle status will actually be utilized.
|
|
#if 0
|
|
if (rob->isEmpty()) {
|
|
DPRINTF(Commit, "Commit: ROB is empty. Status changed to idle.\n");
|
|
_status = Idle;
|
|
// Schedule an event so that commit will actually wake up
|
|
// once something gets put in the ROB.
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Loop that goes through as many instructions in the ROB as possible and
|
|
// tries to commit them. The actual work for committing is done by the
|
|
// commitHead() function.
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::commitInsts()
|
|
{
|
|
////////////////////////////////////
|
|
// Handle commit
|
|
// Note that commit will be handled prior to the ROB so that the ROB
|
|
// only tries to commit instructions it has in this current cycle, and
|
|
// not instructions it is writing in during this cycle.
|
|
// Can't commit and squash things at the same time...
|
|
////////////////////////////////////
|
|
|
|
if (rob->isEmpty())
|
|
return;
|
|
|
|
DynInstPtr head_inst = rob->readHeadInst();
|
|
|
|
unsigned num_committed = 0;
|
|
|
|
// Commit as many instructions as possible until the commit bandwidth
|
|
// limit is reached, or it becomes impossible to commit any more.
|
|
while (!rob->isEmpty() &&
|
|
head_inst->readyToCommit() &&
|
|
num_committed < commitWidth)
|
|
{
|
|
DPRINTF(Commit, "Commit: Trying to commit head instruction.\n");
|
|
|
|
// If the head instruction is squashed, it is ready to retire at any
|
|
// time. However, we need to avoid updating any other state
|
|
// incorrectly if it's already been squashed.
|
|
if (head_inst->isSquashed()) {
|
|
|
|
DPRINTF(Commit, "Commit: Retiring squashed instruction from "
|
|
"ROB.\n");
|
|
|
|
// Tell ROB to retire head instruction. This retires the head
|
|
// inst in the ROB without affecting any other stages.
|
|
rob->retireHead();
|
|
|
|
++commitSquashedInsts;
|
|
|
|
} else {
|
|
// Increment the total number of non-speculative instructions
|
|
// executed.
|
|
// Hack for now: it really shouldn't happen until after the
|
|
// commit is deemed to be successful, but this count is needed
|
|
// for syscalls.
|
|
cpu->funcExeInst++;
|
|
|
|
// Try to commit the head instruction.
|
|
bool commit_success = commitHead(head_inst, num_committed);
|
|
|
|
// Update what instruction we are looking at if the commit worked.
|
|
if (commit_success) {
|
|
++num_committed;
|
|
|
|
// Send back which instruction has been committed.
|
|
// @todo: Update this later when a wider pipeline is used.
|
|
// Hmm, can't really give a pointer here...perhaps the
|
|
// sequence number instead (copy).
|
|
toIEW->commitInfo.doneSeqNum = head_inst->seqNum;
|
|
|
|
++commitCommittedInsts;
|
|
|
|
if (!head_inst->isNop()) {
|
|
cpu->instDone();
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update the pointer to read the next instruction in the ROB.
|
|
head_inst = rob->readHeadInst();
|
|
}
|
|
|
|
DPRINTF(CommitRate, "%i\n", num_committed);
|
|
n_committed_dist.sample(num_committed);
|
|
}
|
|
|
|
template <class Impl>
|
|
bool
|
|
SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num)
|
|
{
|
|
// Make sure instruction is valid
|
|
assert(head_inst);
|
|
|
|
// If the instruction is not executed yet, then it is a non-speculative
|
|
// or store inst. Signal backwards that it should be executed.
|
|
if (!head_inst->isExecuted()) {
|
|
// Keep this number correct. We have not yet actually executed
|
|
// and committed this instruction.
|
|
cpu->funcExeInst--;
|
|
|
|
if (head_inst->isNonSpeculative()) {
|
|
DPRINTF(Commit, "Commit: Encountered a store or non-speculative "
|
|
"instruction at the head of the ROB, PC %#x.\n",
|
|
head_inst->readPC());
|
|
|
|
toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum;
|
|
|
|
// Change the instruction so it won't try to commit again until
|
|
// it is executed.
|
|
head_inst->clearCanCommit();
|
|
|
|
++commitNonSpecStalls;
|
|
|
|
return false;
|
|
} else {
|
|
panic("Commit: Trying to commit un-executed instruction "
|
|
"of unknown type!\n");
|
|
}
|
|
}
|
|
|
|
// Now check if it's one of the special trap or barrier or
|
|
// serializing instructions.
|
|
if (head_inst->isThreadSync() ||
|
|
head_inst->isSerializing() ||
|
|
head_inst->isMemBarrier() ||
|
|
head_inst->isWriteBarrier() )
|
|
{
|
|
// Not handled for now. Mem barriers and write barriers are safe
|
|
// to simply let commit as memory accesses only happen once they
|
|
// reach the head of commit. Not sure about the other two.
|
|
panic("Serializing or barrier instructions"
|
|
" are not handled yet.\n");
|
|
}
|
|
|
|
// Check if the instruction caused a fault. If so, trap.
|
|
Fault inst_fault = head_inst->getFault();
|
|
|
|
if (inst_fault != No_Fault && inst_fault != Fake_Mem_Fault) {
|
|
if (!head_inst->isNop()) {
|
|
#if FULL_SYSTEM
|
|
cpu->trap(inst_fault);
|
|
#else // !FULL_SYSTEM
|
|
panic("fault (%d) detected @ PC %08p", inst_fault,
|
|
head_inst->PC);
|
|
#endif // FULL_SYSTEM
|
|
}
|
|
}
|
|
|
|
// Check if we're really ready to commit. If not then return false.
|
|
// I'm pretty sure all instructions should be able to commit if they've
|
|
// reached this far. For now leave this in as a check.
|
|
if (!rob->isHeadReady()) {
|
|
panic("Commit: Unable to commit head instruction!\n");
|
|
return false;
|
|
}
|
|
|
|
// If it's a branch, then send back branch prediction update info
|
|
// to the fetch stage.
|
|
// This should be handled in the iew stage if a mispredict happens...
|
|
|
|
if (head_inst->isControl()) {
|
|
|
|
#if 0
|
|
toIEW->nextPC = head_inst->readPC();
|
|
//Maybe switch over to BTB incorrect.
|
|
toIEW->btbMissed = head_inst->btbMiss();
|
|
toIEW->target = head_inst->nextPC;
|
|
//Maybe also include global history information.
|
|
//This simple version will have no branch prediction however.
|
|
#endif
|
|
|
|
++commitCommittedBranches;
|
|
}
|
|
|
|
// Now that the instruction is going to be committed, finalize its
|
|
// trace data.
|
|
if (head_inst->traceData) {
|
|
head_inst->traceData->finalize();
|
|
}
|
|
|
|
//Finally clear the head ROB entry.
|
|
rob->retireHead();
|
|
|
|
// Return true to indicate that we have committed an instruction.
|
|
return true;
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::getInsts()
|
|
{
|
|
//////////////////////////////////////
|
|
// Handle ROB functions
|
|
//////////////////////////////////////
|
|
|
|
// Read any issued instructions and place them into the ROB. Do this
|
|
// prior to squashing to avoid having instructions in the ROB that
|
|
// don't get squashed properly.
|
|
int insts_to_process = min((int)renameWidth, fromRename->size);
|
|
|
|
for (int inst_num = 0;
|
|
inst_num < insts_to_process;
|
|
++inst_num)
|
|
{
|
|
if (!fromRename->insts[inst_num]->isSquashed()) {
|
|
DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n",
|
|
fromRename->insts[inst_num]->readPC());
|
|
rob->insertInst(fromRename->insts[inst_num]);
|
|
} else {
|
|
DPRINTF(Commit, "Commit: Instruction %i PC %#x was "
|
|
"squashed, skipping.\n",
|
|
fromRename->insts[inst_num]->seqNum,
|
|
fromRename->insts[inst_num]->readPC());
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class Impl>
|
|
void
|
|
SimpleCommit<Impl>::markCompletedInsts()
|
|
{
|
|
// Grab completed insts out of the IEW instruction queue, and mark
|
|
// instructions completed within the ROB.
|
|
for (int inst_num = 0;
|
|
inst_num < fromIEW->size && fromIEW->insts[inst_num];
|
|
++inst_num)
|
|
{
|
|
DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n",
|
|
fromIEW->insts[inst_num]->readPC(),
|
|
fromIEW->insts[inst_num]->seqNum);
|
|
|
|
// Mark the instruction as ready to commit.
|
|
fromIEW->insts[inst_num]->setCanCommit();
|
|
}
|
|
}
|
|
|
|
template <class Impl>
|
|
uint64_t
|
|
SimpleCommit<Impl>::readCommitPC()
|
|
{
|
|
return rob->readHeadPC();
|
|
}
|