blob: 5a4d4bb5562aff7925836d4698c21d6477cbcabb [file] [log] [blame]
/*
* Copyright (c) 2007 MIPS Technologies, Inc.
* 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: Korey Sewell
*
*/
#include <list>
#include <vector>
#include "cpu/inorder/resources/mult_div_unit.hh"
#include "cpu/inorder/cpu.hh"
#include "cpu/inorder/resource_pool.hh"
#include "cpu/op_class.hh"
#include "debug/InOrderMDU.hh"
#include "debug/Resource.hh"
using namespace std;
using namespace ThePipeline;
MultDivUnit::MultDivUnit(string res_name, int res_id, int res_width,
Cycles res_latency, InOrderCPU *_cpu,
ThePipeline::Params *params)
: Resource(res_name, res_id, res_width, res_latency, _cpu),
multRepeatRate(params->multRepeatRate),
multLatency(params->multLatency),
div8RepeatRate(params->div8RepeatRate),
div8Latency(params->div8Latency),
div16RepeatRate(params->div16RepeatRate),
div16Latency(params->div16Latency),
div24RepeatRate(params->div24RepeatRate),
div24Latency(params->div24Latency),
div32RepeatRate(params->div32RepeatRate),
div32Latency(params->div32Latency),
lastMDUCycle(0), lastOpType(No_OpClass)
{ }
void
MultDivUnit::regStats()
{
multiplies
.name(name() + ".multiplies")
.desc("Number of Multipy Operations Executed");
divides
.name(name() + ".divides")
.desc("Number of Divide Operations Executed");
Resource::regStats();
}
void
MultDivUnit::init()
{
// Set Up Resource Events to Appropriate Resource BandWidth
resourceEvent = new MDUEvent[width];
for (int i = 0; i < width; i++) {
reqs[i] = new ResourceRequest(this);
}
initSlots();
}
//@TODO: Should we push this behavior into base-class to generically
// accomodate all multicyle resources?
void
MultDivUnit::requestAgain(DynInstPtr inst, bool &service_request)
{
ResReqPtr mult_div_req = findRequest(inst);
assert(mult_div_req);
service_request = true;
// Check to see if this instruction is requesting the same command
// or a different one
if (mult_div_req->cmd != inst->curSkedEntry->cmd) {
// If different, then update command in the request
mult_div_req->cmd = inst->curSkedEntry->cmd;
DPRINTF(InOrderMDU,
"[tid:%i]: [sn:%i]: Updating the command for this "
"instruction\n", inst->readTid(), inst->seqNum);
} else {
// If same command, just check to see if access was completed
// but dont try to re-execute
DPRINTF(InOrderMDU,
"[tid:%i]: [sn:%i]: requesting this resource again\n",
inst->readTid(), inst->seqNum);
}
}
int
MultDivUnit::getSlot(DynInstPtr inst)
{
// If MDU already has instruction, return current slot.
int slot_num = findSlot(inst);
// If we have this instruction's request already then return
if (slot_num != -1 &&
inst->curSkedEntry->cmd == reqs[slot_num]->cmd)
return slot_num;
unsigned repeat_rate = 0;
/** Enforce MDU dependencies after a multiply is seen last */
if (lastOpType == IntMultOp) {
repeat_rate = multRepeatRate;
}
/** Enforce dependencies after a divide is seen last */
if (lastOpType == IntDivOp) {
switch (lastDivSize) {
case 8:
repeat_rate = div8RepeatRate;
break;
case 16:
repeat_rate = div16RepeatRate;
break;
case 24:
repeat_rate = div24RepeatRate;
break;
case 32:
repeat_rate = div32RepeatRate;
break;
}
}
if (lastMDUCycle + repeat_rate > curTick()) {
DPRINTF(InOrderMDU, "MDU not ready to process another inst. until %i, "
"denying request.\n", lastMDUCycle + repeat_rate);
return -1;
} else {
int rval = Resource::getSlot(inst);
DPRINTF(InOrderMDU, "MDU request should pass: %i.\n",
rval);
if (rval != -1) {
lastMDUCycle = curTick();
lastOpType = inst->opClass();
lastInstName = inst->staticInst->getName();
}
return rval;
}
}
int
MultDivUnit::getDivOpSize(DynInstPtr inst)
{
// Get RT Register from instruction (index #1)
uint32_t div_op = inst->readIntSrc(1);
if (div_op <= 0xFF) {
return 8;
} else if (div_op <= 0xFFFF) {
return 16;
} else if (div_op <= 0xFFFFFF) {
return 24;
} else {
return 32;
}
}
void
MultDivUnit::execute(int slot_num)
{
ResourceRequest* mult_div_req = reqs[slot_num];
DynInstPtr inst = reqs[slot_num]->inst;
if (inst->fault != NoFault) {
DPRINTF(InOrderMDU,
"[tid:%i]: [sn:%i]: Detected %s fault @ %x. Forwarding to "
"next stage.\n", inst->readTid(), inst->seqNum, inst->fault->name(),
inst->pcState());
mult_div_req->done();
return;
}
DPRINTF(InOrderMDU, "Executing [sn:%i] ...\n", slot_num);
switch (mult_div_req->cmd)
{
case StartMultDiv:
{
DPRINTF(InOrderMDU, "Start MDU called ...\n");
OpClass op_class = inst->opClass();
if (op_class == IntMultOp) {
scheduleEvent(slot_num, multLatency);
} else if (op_class == IntDivOp) {
int op_size = getDivOpSize(inst);
switch (op_size)
{
case 8:
scheduleEvent(slot_num, div8Latency);
break;
case 16:
scheduleEvent(slot_num, div16Latency);
break;
case 24:
scheduleEvent(slot_num, div24Latency);
break;
case 32:
scheduleEvent(slot_num, div32Latency);
break;
}
lastDivSize = op_size;
}
// Allow to pass through to next stage while
// event processes
mult_div_req->setProcessing();
mult_div_req->setCompleted();
}
break;
case MultDiv:
DPRINTF(InOrderMDU, "Execute MDU called ...\n");
exeMulDiv(slot_num);
mult_div_req->done();
break;
case EndMultDiv:
//@TODO: Why not allow high-latency requests to sleep
// within stage until event wakes up????
// Seems wasteful to continually check to see if
// this is done when we have a event in parallel
// counting down the time
{
DPRINTF(InOrderMDU, "End MDU called ...\n");
if (!mult_div_req->isProcessing()) {
DPRINTF(InOrderMDU, "Mult/Div finished.\n");
mult_div_req->done();
} else {
mult_div_req->setCompleted(false);
}
}
break;
default:
fatal("Unrecognized command to %s", resName);
}
}
void
MultDivUnit::exeMulDiv(int slot_num)
{
ResourceRequest* mult_div_req = reqs[slot_num];
DynInstPtr inst = reqs[slot_num]->inst;
inst->fault = inst->execute();
if (inst->opClass() == IntMultOp) {
multiplies++;
} else if (inst->opClass() == IntDivOp) {
divides++;
}
if (inst->fault == NoFault) {
inst->setExecuted();
DPRINTF(InOrderMDU, "[tid:%i]: The result of execution is 0x%x.\n",
inst->readTid(), inst->readIntResult(0));
} else {
DPRINTF(InOrderMDU, "[tid:%i]: [sn:%i]: had a %s "
"fault.\n", inst->readTid(), inst->seqNum, inst->fault->name());
}
mult_div_req->setProcessing(false);
cpu->wakeCPU();
}
void
MultDivUnit::squash(DynInstPtr inst, int stage_num, InstSeqNum squash_seq_num,
ThreadID tid)
{
for (int i = 0; i < width; i++) {
ResReqPtr req_ptr = reqs[i];
DynInstPtr inst = req_ptr->getInst();
if (req_ptr->valid &&
inst->readTid() == tid &&
inst->seqNum > squash_seq_num) {
DPRINTF(InOrderMDU, "[tid:%i]: Squashing [sn:%i].\n",
req_ptr->getInst()->readTid(),
req_ptr->getInst()->seqNum);
req_ptr->setSquashed();
int req_slot_num = req_ptr->getSlot();
if (req_ptr->isProcessing())
DPRINTF(InOrderMDU, "[tid:%i]: Squashed [sn:%i], but "
"waiting for MDU operation to complete.\n",
req_ptr->getInst()->readTid(),
req_ptr->getInst()->seqNum);
else
freeSlot(req_slot_num);
}
}
}
MDUEvent::MDUEvent()
: ResourceEvent()
{ }
void
MDUEvent::process()
{
MultDivUnit* mdu_res = reinterpret_cast<MultDivUnit*>(resource);
mdu_res->exeMulDiv(slotIdx);
ResourceRequest* mult_div_req = resource->reqs[slotIdx];
if (mult_div_req->isSquashed())
mdu_res->freeSlot(slotIdx);
}