blob: 29dbf8b16776ed83d4364c1ac4c0c5925dba78dd [file] [log] [blame]
/*
* Copyright (c) 2013-2014 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.
*
* 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 "cpu/minor/pipeline.hh"
#include <algorithm>
#include "cpu/minor/decode.hh"
#include "cpu/minor/execute.hh"
#include "cpu/minor/fetch1.hh"
#include "cpu/minor/fetch2.hh"
#include "debug/Drain.hh"
#include "debug/MinorCPU.hh"
#include "debug/MinorTrace.hh"
#include "debug/Quiesce.hh"
namespace Minor
{
Pipeline::Pipeline(MinorCPU &cpu_, MinorCPUParams &params) :
Ticked(cpu_, &(cpu_.BaseCPU::numCycles)),
cpu(cpu_),
allow_idling(params.enableIdling),
f1ToF2(cpu.name() + ".f1ToF2", "lines",
params.fetch1ToFetch2ForwardDelay),
f2ToF1(cpu.name() + ".f2ToF1", "prediction",
params.fetch1ToFetch2BackwardDelay, true),
f2ToD(cpu.name() + ".f2ToD", "insts",
params.fetch2ToDecodeForwardDelay),
dToE(cpu.name() + ".dToE", "insts",
params.decodeToExecuteForwardDelay),
eToF1(cpu.name() + ".eToF1", "branch",
params.executeBranchDelay),
execute(cpu.name() + ".execute", cpu, params,
dToE.output(), eToF1.input()),
decode(cpu.name() + ".decode", cpu, params,
f2ToD.output(), dToE.input(), execute.inputBuffer),
fetch2(cpu.name() + ".fetch2", cpu, params,
f1ToF2.output(), eToF1.output(), f2ToF1.input(), f2ToD.input(),
decode.inputBuffer),
fetch1(cpu.name() + ".fetch1", cpu, params,
eToF1.output(), f1ToF2.input(), f2ToF1.output(), fetch2.inputBuffer),
activityRecorder(cpu.name() + ".activity", Num_StageId,
/* The max depth of inter-stage FIFOs */
std::max(params.fetch1ToFetch2ForwardDelay,
std::max(params.fetch2ToDecodeForwardDelay,
std::max(params.decodeToExecuteForwardDelay,
params.executeBranchDelay)))),
needToSignalDrained(false)
{
if (params.fetch1ToFetch2ForwardDelay < 1) {
fatal("%s: fetch1ToFetch2ForwardDelay must be >= 1 (%d)\n",
cpu.name(), params.fetch1ToFetch2ForwardDelay);
}
if (params.fetch2ToDecodeForwardDelay < 1) {
fatal("%s: fetch2ToDecodeForwardDelay must be >= 1 (%d)\n",
cpu.name(), params.fetch2ToDecodeForwardDelay);
}
if (params.decodeToExecuteForwardDelay < 1) {
fatal("%s: decodeToExecuteForwardDelay must be >= 1 (%d)\n",
cpu.name(), params.decodeToExecuteForwardDelay);
}
if (params.executeBranchDelay < 1) {
fatal("%s: executeBranchDelay must be >= 1\n",
cpu.name(), params.executeBranchDelay);
}
}
void
Pipeline::minorTrace() const
{
fetch1.minorTrace();
f1ToF2.minorTrace();
f2ToF1.minorTrace();
fetch2.minorTrace();
f2ToD.minorTrace();
decode.minorTrace();
dToE.minorTrace();
execute.minorTrace();
eToF1.minorTrace();
activityRecorder.minorTrace();
}
void
Pipeline::evaluate()
{
/* Note that it's important to evaluate the stages in order to allow
* 'immediate', 0-time-offset TimeBuffer activity to be visible from
* later stages to earlier ones in the same cycle */
execute.evaluate();
decode.evaluate();
fetch2.evaluate();
fetch1.evaluate();
if (DTRACE(MinorTrace))
minorTrace();
/* Update the time buffers after the stages */
f1ToF2.evaluate();
f2ToF1.evaluate();
f2ToD.evaluate();
dToE.evaluate();
eToF1.evaluate();
/* The activity recorder must be be called after all the stages and
* before the idler (which acts on the advice of the activity recorder */
activityRecorder.evaluate();
if (allow_idling) {
/* Become idle if we can but are not draining */
if (!activityRecorder.active() && !needToSignalDrained) {
DPRINTF(Quiesce, "Suspending as the processor is idle\n");
stop();
}
/* Deactivate all stages. Note that the stages *could*
* activate and deactivate themselves but that's fraught
* with additional difficulty.
* As organised herre */
activityRecorder.deactivateStage(Pipeline::CPUStageId);
activityRecorder.deactivateStage(Pipeline::Fetch1StageId);
activityRecorder.deactivateStage(Pipeline::Fetch2StageId);
activityRecorder.deactivateStage(Pipeline::DecodeStageId);
activityRecorder.deactivateStage(Pipeline::ExecuteStageId);
}
if (needToSignalDrained) /* Must be draining */
{
DPRINTF(Drain, "Still draining\n");
if (isDrained()) {
DPRINTF(Drain, "Signalling end of draining\n");
cpu.signalDrainDone();
needToSignalDrained = false;
stop();
}
}
}
MinorCPU::MinorCPUPort &
Pipeline::getInstPort()
{
return fetch1.getIcachePort();
}
MinorCPU::MinorCPUPort &
Pipeline::getDataPort()
{
return execute.getDcachePort();
}
void
Pipeline::wakeupFetch(ThreadID tid)
{
fetch1.wakeupFetch(tid);
}
bool
Pipeline::drain()
{
DPRINTF(MinorCPU, "Draining pipeline by halting inst fetches. "
" Execution should drain naturally\n");
execute.drain();
/* Make sure that needToSignalDrained isn't accidentally set if we
* are 'pre-drained' */
bool drained = isDrained();
needToSignalDrained = !drained;
return drained;
}
void
Pipeline::drainResume()
{
DPRINTF(Drain, "Drain resume\n");
for (ThreadID tid = 0; tid < cpu.numThreads; tid++) {
fetch1.wakeupFetch(tid);
}
execute.drainResume();
}
bool
Pipeline::isDrained()
{
bool fetch1_drained = fetch1.isDrained();
bool fetch2_drained = fetch2.isDrained();
bool decode_drained = decode.isDrained();
bool execute_drained = execute.isDrained();
bool f1_to_f2_drained = f1ToF2.empty();
bool f2_to_f1_drained = f2ToF1.empty();
bool f2_to_d_drained = f2ToD.empty();
bool d_to_e_drained = dToE.empty();
bool ret = fetch1_drained && fetch2_drained &&
decode_drained && execute_drained &&
f1_to_f2_drained && f2_to_f1_drained &&
f2_to_d_drained && d_to_e_drained;
DPRINTF(MinorCPU, "Pipeline undrained stages state:%s%s%s%s%s%s%s%s\n",
(fetch1_drained ? "" : " Fetch1"),
(fetch2_drained ? "" : " Fetch2"),
(decode_drained ? "" : " Decode"),
(execute_drained ? "" : " Execute"),
(f1_to_f2_drained ? "" : " F1->F2"),
(f2_to_f1_drained ? "" : " F2->F1"),
(f2_to_d_drained ? "" : " F2->D"),
(d_to_e_drained ? "" : " D->E")
);
return ret;
}
}