/*******************************************************************************
* openDLX - A DLX/MIPS processor simulator.
* Copyright (C) 2013 The openDLX project, University of Augsburg, Germany
* Project URL: <https://sourceforge.net/projects/opendlx>
* Development branch: <https://github.com/smetzlaff/openDLX>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program, see <LICENSE>. If not, see
* <http://www.gnu.org/licenses/>.
******************************************************************************/
package openDLX;
import java.util.Queue;
import org.apache.log4j.Logger;
import openDLX.datatypes.*;
import openDLX.exception.ExecuteStageException;
import openDLX.exception.PipelineException;
import openDLX.util.Statistics;
public class Execute
{
private static Logger logger = Logger.getLogger("EXECUTE");
private Statistics stat = Statistics.getInstance();
private ALU alu;
private BranchControl branch_control;
private Queue<DecodeExecuteData> decode_execute_latch;
private Queue<BranchPredictionModuleExecuteData> branchprediction_execute_latch;
private Queue<ExecuteMemoryData> fw_eml;
private Queue<MemoryWritebackData> fw_mwl;
private Queue<WriteBackData> fw_wbl;
public Execute()
{
// TODO handle ISA DLX/MIPS flavour
alu = new ALU();
branch_control = new BranchControl();
}
public void setInputLatches(Queue<DecodeExecuteData> decodeExecuteLatch, Queue<BranchPredictionModuleExecuteData> branchpredictionExecuteLatch)
{
decode_execute_latch = decodeExecuteLatch;
branchprediction_execute_latch = branchpredictionExecuteLatch;
}
public void setForwardingLatches(Queue<ExecuteMemoryData> executeMemoryLatch, Queue<MemoryWritebackData> memoryWritebackLatch, Queue<WriteBackData> writebackLatch)
{
fw_eml = executeMemoryLatch;
fw_mwl = memoryWritebackLatch;
fw_wbl = writebackLatch;
}
public ExecuteOutputData doCycle() throws PipelineException
{
boolean[] stall_out = new boolean[PipelineConstants.STAGES];
for(byte i = 0; i < PipelineConstants.STAGES; i++)
{
stall_out[i] = false;
}
DecodeExecuteData ded = decode_execute_latch.element();
Instruction inst = ded.getInst();
uint32 alu_in_a = ded.getAluInA();
uint32 alu_in_b = ded.getAluInB();
uint32 branch_ctrl_in_a = ded.getBranchCtrlInA();
uint32 branch_ctrl_in_b = ded.getBranchCtrlInB();
uint32 pc = ded.getPc();
uint32 store_value = ded.getStoreValue();
// STRUCTURE ALLOCATION FOR DATA FORWARDING
// FROM EXECUTE TO MEM STAGE
ExecuteMemoryData fw_emd = fw_eml.element();
Instruction fw_emd_inst = fw_emd.getInst();
uint32[] fw_emd_alu_result = fw_emd.getAluOut();
uint32 fw_emd_ld_result = new uint32(0); // cannot be forwarded due to stall of the pipeline
// FROM MEM TO WRITE BACK STAGE
MemoryWritebackData fw_mwd = fw_mwl.element();
Instruction fw_mwd_inst = fw_mwd.getInst();
uint32[] fw_mwd_alu_result = fw_mwd.getAluOut();
uint32 fw_mwd_ld_result = fw_mwd.getLdResult();
// FROM WRITE BACK OUT STAGE
WriteBackData fw_wbd = fw_wbl.element();
Instruction fw_wbd_inst = fw_wbd.getInst();
uint32[] fw_wbd_alu_result = fw_wbd.getAluOut();
uint32 fw_wbd_ld_result = fw_wbd.getLdResult();
// MIPS ISA flavour always uses forwarding, DLX only if enabled
if((ArchCfg.isa_type == ISAType.MIPS) || (ArchCfg.use_forwarding == true))
{
// DATA FORWARDING
// forward already calculated results from the WB stage and from
// the EX stage
// for ALU PORT A
int old_alu_in_a = alu_in_a.getValue();
switch (inst.getALUPortA())
{
case RS:
{
forwarding(inst.getRs(), alu_in_a, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
boolean fw_wb = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
forwarding(inst.getRs(), alu_in_a, fw_mwd_alu_result[0], fw_mwd_ld_result, fw_mwd_inst);
boolean fw_mem = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
forwarding(inst.getRs(), alu_in_a, fw_emd_alu_result[0], fw_emd_ld_result, fw_emd_inst);
boolean fw_ex = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
stat.countALUForward(fw_ex, fw_mem, fw_wb);
break;
}
case RT:
{
forwarding(inst.getRt(), alu_in_a, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
boolean fw_wb = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
forwarding(inst.getRt(), alu_in_a, fw_mwd_alu_result[0], fw_mwd_ld_result, fw_mwd_inst);
boolean fw_mem = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
forwarding(inst.getRt(), alu_in_a, fw_emd_alu_result[0], fw_emd_ld_result, fw_emd_inst);
boolean fw_ex = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
stat.countALUForward(fw_ex, fw_mem, fw_wb);
break;
}
case LO:
{
forwarding(SpecialRegisters.LO, alu_in_a, fw_wbd_alu_result, fw_wbd_inst);
boolean fw_wb = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
forwarding(SpecialRegisters.LO, alu_in_a, fw_mwd_alu_result, fw_mwd_inst);
boolean fw_mem = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
forwarding(SpecialRegisters.LO, alu_in_a, fw_emd_alu_result, fw_emd_inst);
boolean fw_ex = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
stat.countALUForward(fw_ex, fw_mem, fw_wb);
break;
}
case HI:
{
forwarding(SpecialRegisters.HI, alu_in_a, fw_wbd_alu_result, fw_wbd_inst);
boolean fw_wb = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
forwarding(SpecialRegisters.HI, alu_in_a, fw_mwd_alu_result, fw_mwd_inst);
boolean fw_mem = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
forwarding(SpecialRegisters.HI, alu_in_a, fw_emd_alu_result, fw_emd_inst);
boolean fw_ex = (old_alu_in_a != alu_in_a.getValue()) ? true : false;
stat.countALUForward(fw_ex, fw_mem, fw_wb);
break;
}
default:
// do nothing
}
if(old_alu_in_a != alu_in_a.getValue())
{
logger.debug("{FW} PC: " + pc.getValueAsHexString() + " forwarding changed value for ALU port A " + inst.getALUPortA() + " from: 0x" + Integer.toHexString(old_alu_in_a) + " to: " + alu_in_a.getValueAsHexString());
}
// for ALU PORT B
int old_alu_in_b = alu_in_b.getValue();
switch (inst.getALUPortB())
{
case RT:
{
forwarding(inst.getRt(), alu_in_b, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
boolean fw_wb = (old_alu_in_b != alu_in_b.getValue())?true:false;
forwarding(inst.getRt(), alu_in_b, fw_mwd_alu_result[0], fw_mwd_ld_result, fw_mwd_inst);
boolean fw_mem = (old_alu_in_b != alu_in_b.getValue())?true:false;
forwarding(inst.getRt(), alu_in_b, fw_emd_alu_result[0], fw_emd_ld_result, fw_emd_inst);
boolean fw_ex = (old_alu_in_b != alu_in_b.getValue())?true:false;
stat.countALUForward(fw_ex, fw_mem, fw_wb);
break;
}
default:
// do nothing
}
if(old_alu_in_b != alu_in_b.getValue())
{
logger.debug("{FW} PC: " + pc.getValueAsHexString() + " forwarding changed value for ALU port B " + inst.getALUPortB() + " from: 0x" + Integer.toHexString(old_alu_in_b) + " to: " + alu_in_b.getValueAsHexString());
}
// for BRANCH CONTROL PORT A
int old_branch_ctrl_in_a = branch_ctrl_in_a.getValue();
switch(inst.getBrachControlPortA())
{
case RS:
{
forwarding(inst.getRs(), branch_ctrl_in_a, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
boolean fw_wb = (old_branch_ctrl_in_a != branch_ctrl_in_a.getValue())?true:false;
forwarding(inst.getRs(), branch_ctrl_in_a, fw_mwd_alu_result[0], fw_mwd_ld_result, fw_mwd_inst);
boolean fw_mem = (old_branch_ctrl_in_a != branch_ctrl_in_a.getValue())?true:false;
forwarding(inst.getRs(), branch_ctrl_in_a, fw_emd_alu_result[0], fw_emd_ld_result, fw_emd_inst);
boolean fw_ex = (old_branch_ctrl_in_a != branch_ctrl_in_a.getValue())?true:false;
stat.countBCRTLForward(fw_ex, fw_mem, fw_wb);
break;
}
default:
// do nothing
}
if(old_branch_ctrl_in_a != branch_ctrl_in_a.getValue())
{
logger.debug("{FW} PC: " + pc.getValueAsHexString() + " forwarding changed value for BCTRL port A " + inst.getBrachControlPortA() + " from: 0x" + Integer.toHexString(old_branch_ctrl_in_a) + " to: " + branch_ctrl_in_a.getValueAsHexString());
}
// for BRANCH CONTROL PORT B
int old_branch_ctrl_in_b = branch_ctrl_in_b.getValue();
switch(inst.getBrachControlPortB())
{
case RT:
forwarding(inst.getRt(), branch_ctrl_in_b, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
boolean fw_wb = (old_branch_ctrl_in_b != branch_ctrl_in_b.getValue())?true:false;
forwarding(inst.getRt(), branch_ctrl_in_b, fw_mwd_alu_result[0], fw_mwd_ld_result, fw_mwd_inst);
boolean fw_mem = (old_branch_ctrl_in_b != branch_ctrl_in_b.getValue())?true:false;
forwarding(inst.getRt(), branch_ctrl_in_b, fw_emd_alu_result[0], fw_emd_ld_result, fw_emd_inst);
boolean fw_ex = (old_branch_ctrl_in_b != branch_ctrl_in_b.getValue())?true:false;
stat.countBCRTLForward(fw_ex, fw_mem, fw_wb);
break;
default:
// do nothing
}
if(old_branch_ctrl_in_b != branch_ctrl_in_b.getValue())
{
logger.debug("{FW} PC: " + pc.getValueAsHexString() + " forwarding changed value for BCTRL port B " + inst.getBrachControlPortB() + " from: 0x" + Integer.toHexString(old_branch_ctrl_in_b) + " to: " + branch_ctrl_in_b.getValueAsHexString());
}
// for STORE value
int old_store_value = store_value.getValue();
if (inst.getStore())
{
forwarding(inst.getRt(), store_value, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
boolean fw_wb = (old_store_value != store_value.getValue())?true:false;
forwarding(inst.getRt(), store_value, fw_mwd_alu_result[0], fw_mwd_ld_result, fw_mwd_inst);
boolean fw_mem = (old_store_value != store_value.getValue())?true:false;
forwarding(inst.getRt(), store_value, fw_emd_alu_result[0], fw_emd_ld_result, fw_emd_inst);
boolean fw_ex = (old_store_value != store_value.getValue())?true:false;
stat.countSTOREForward(fw_ex, fw_mem, fw_wb);
}
if(old_store_value != store_value.getValue())
{
logger.debug("{FW} PC: " + pc.getValueAsHexString() + " forwarding changed store_value for RT from: 0x" + Integer.toHexString(old_store_value) + " to: " + store_value.getValueAsHexString());
}
// DATA FORWARDING END
}
else
{
// PSEUDO DATA FORWARDING
// the DLX pipeline needs two bubbles for data dependencies, because the
// register set is pipelined:
// - first part of a cycle, the write back stage writes a register
// - second part of cycle, the decode reads a register
// This behavior is emulated in this simulator by forwarding the data that
// is at the input ports of the write back stage.
//////////
// forward already calculated results from the WB stage
// for ALU PORT A
int old_alu_in_a = alu_in_a.getValue();
switch (inst.getALUPortA())
{
case RS:
{
forwarding(inst.getRs(), alu_in_a, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
break;
}
case RT:
{
forwarding(inst.getRt(), alu_in_a, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
break;
}
case LO:
{
forwarding(SpecialRegisters.LO, alu_in_a, fw_wbd_alu_result, fw_wbd_inst);
break;
}
case HI:
{
forwarding(SpecialRegisters.HI, alu_in_a, fw_wbd_alu_result, fw_wbd_inst);
break;
}
default:
// do nothing
}
if(old_alu_in_a != alu_in_a.getValue())
{
logger.debug("{RS/WB-FW} PC: " + pc.getValueAsHexString() + " forwarding changed value for ALU port A " + inst.getALUPortA() + " from: 0x" + Integer.toHexString(old_alu_in_a) + " to: " + alu_in_a.getValueAsHexString());
}
// for ALU PORT B
int old_alu_in_b = alu_in_b.getValue();
switch (inst.getALUPortB())
{
case RT:
{
forwarding(inst.getRt(), alu_in_b, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
break;
}
default:
// do nothing
}
if(old_alu_in_b != alu_in_b.getValue())
{
logger.debug("{RS/WB-FW} PC: " + pc.getValueAsHexString() + " forwarding changed value for ALU port B " + inst.getALUPortB() + " from: 0x" + Integer.toHexString(old_alu_in_b) + " to: " + alu_in_b.getValueAsHexString());
}
// for BRANCH CONTROL PORT A
int old_branch_ctrl_in_a = branch_ctrl_in_a.getValue();
switch(inst.getBrachControlPortA())
{
case RS:
{
forwarding(inst.getRs(), branch_ctrl_in_a, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
break;
}
default:
// do nothing
}
if(old_branch_ctrl_in_a != branch_ctrl_in_a.getValue())
{
logger.debug("{RS/WB-FW} PC: " + pc.getValueAsHexString() + " forwarding changed value for BCTRL port A " + inst.getBrachControlPortA() + " from: 0x" + Integer.toHexString(old_branch_ctrl_in_a) + " to: " + branch_ctrl_in_a.getValueAsHexString());
}
// for BRANCH CONTROL PORT B
int old_branch_ctrl_in_b = branch_ctrl_in_b.getValue();
switch(inst.getBrachControlPortB())
{
case RT:
forwarding(inst.getRt(), branch_ctrl_in_b, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
break;
default:
// do nothing
}
if(old_branch_ctrl_in_b != branch_ctrl_in_b.getValue())
{
logger.debug("{RS/WB-FW} PC: " + pc.getValueAsHexString() + " forwarding changed value for BCTRL port B " + inst.getBrachControlPortB() + " from: 0x" + Integer.toHexString(old_branch_ctrl_in_b) + " to: " + branch_ctrl_in_b.getValueAsHexString());
}
// for STORE value
int old_store_value = store_value.getValue();
if (inst.getStore())
{
forwarding(inst.getRt(), store_value, fw_wbd_alu_result[0], fw_wbd_ld_result, fw_wbd_inst);
}
if(old_store_value != store_value.getValue())
{
logger.debug("{RS/WB-FW} PC: " + pc.getValueAsHexString() + " forwarding changed store_value for RT from: 0x" + Integer.toHexString(old_store_value) + " to: " + store_value.getValueAsHexString());
}
// DATA FORWARDING END
}
// ALU OPERATION BEGIN
uint32[] alu_out = alu.doOperation(inst.getALUFunction(),
alu_in_a, alu_in_b);
uint32 alu_outLO = alu_out[0];
// uint32 alu_outHI = alu_out[1];
logger.debug("PC: " + pc.getValueAsHexString() + " ALU calculated: "
+ alu_outLO.getValue() + "(" + alu_outLO.getValueAsHexString()
+ ") by: " + alu_in_a.getValue() + "("
+ alu_in_a.getValueAsHexString() + ") " + inst.getALUFunction()
+ " " + alu_in_b.getValue() + "(" + alu_in_b.getValueAsHexString()
+ ")");
// ALU OPERATION END
// BRANCH CONTROL
boolean jump = branch_control.checkBranch(inst, branch_ctrl_in_a, branch_ctrl_in_b);
// BRANCH CONTROL END
// count the jumps if there is a branch
if(inst.getBranch()==true)
{
if(jump)
{
stat.countJumpTaken();
}
else
{
stat.countJumpNotTaken();
}
if(inst.getBranchLikely())
{
stat.countJumpLikely();
}
if(inst.getBranchAndLink())
{
stat.countJumpLink();
}
}
// check if branch prediction was correct
BranchPredictionModuleExecuteData bpmed = branchprediction_execute_latch.element();
boolean mispredicted_branch = false;
if(inst.getBranch()== true)
{
if(bpmed.getPc().getValue() != pc.getValue())
{
throw new ExecuteStageException("Wrong PC :" + bpmed.getPc().getValueAsHexString() + " != " + pc.getValueAsHexString());
}
// either the branch direction was falsely predicted
// or the branch target is wrong (but only if the jump is respectively was predicted to be taken)
if((bpmed.getDoSpeculativeJump() != jump) || ((jump == true) && (bpmed.getDoSpeculativeJump() == true) && (bpmed.getBranchTgt().getValue() != alu_outLO.getValue())))
{
mispredicted_branch = true;
}
}
// to MEM STAGE
ExecuteMemoryData emd = new ExecuteMemoryData(inst, pc, alu_out, store_value, jump);
// to FETCH STAGE
ExecuteFetchData efd = new ExecuteFetchData(inst, pc, alu_outLO, jump, mispredicted_branch);
// to BRANCH PREDICTION MODULE
ExecuteBranchPredictionData ebd = new ExecuteBranchPredictionData(inst, pc, alu_outLO, jump);
// MIPS ISA flavour always is allowed to stall, DLX only if enabled
if((ArchCfg.isa_type == ISAType.MIPS) || (ArchCfg.use_load_stall_bubble == true))
{
// check if the instruction before was a load that writes into a src register,
// if so the fetch, decode, and execute stages have to be stalled for 1 cycle to let this instruction enter the memory stage
if(fw_emd_inst.getLoad())
{
boolean do_stall=false;
byte ld_tgt_register = 0;
if(fw_emd_inst.getWriteRd())
{
ld_tgt_register = (byte)fw_emd_inst.getRd().getValue();
}
else if(fw_emd_inst.getWriteRt())
{
ld_tgt_register = (byte)fw_emd_inst.getRt().getValue();
}
else
{
throw new ExecuteStageException("Missing load target register.");
}
if(inst.getReadRs() && (inst.getRs().getValue() == ld_tgt_register))
{
do_stall = true;
}
if(inst.getReadRt() && (inst.getRt().getValue() == ld_tgt_register))
{
do_stall = true;
}
if(do_stall)
{
stall_out[PipelineConstants.FETCH_STAGE] = true;
stall_out[PipelineConstants.DECODE_STAGE] = true;
stall_out[PipelineConstants.EXECUTE_STAGE] = true;
}
}
}
return new ExecuteOutputData(emd, efd, ebd, stall_out);
}
private void forwarding(SpecialRegisters reg_read, uint32 alu_in, uint32[] old_alu_result, Instruction old_inst)
{
if (old_inst.getWriteLO() && (reg_read == SpecialRegisters.LO))
{
logger.debug("{FW} using " + old_alu_result[0].getValueAsHexString() + " for register " + reg_read + " instead of value: " + alu_in.getValueAsHexString());
alu_in.setValue(old_alu_result[0]);
}
if (old_inst.getWriteHI() && (reg_read == SpecialRegisters.HI))
{
logger.debug("{FW} using " + old_alu_result[1].getValueAsHexString() + " for register " + reg_read + " instead of value: " + alu_in.getValueAsHexString());
alu_in.setValue(old_alu_result[1]);
}
}
private void forwarding(uint8 reg_read, uint32 alu_in, uint32 old_alu_result, uint32 old_ld_result, Instruction old_inst)
{
if (old_inst.getWriteRd() && (reg_read.getValue() == old_inst.getRd().getValue()))
{
if(old_inst.getRd().getValue() != 0)
{
if(old_inst.getLoad())
{
logger.debug("{FW} using LD result " + old_ld_result.getValueAsHexString() + " for register " + reg_read.getValue() + "/" + ArchCfg.getRegisterDescription(reg_read.getValue()) + " instead of value: " + alu_in.getValueAsHexString());
alu_in.setValue(old_ld_result);
}
else
{
logger.debug("{FW} using ALU result " + old_alu_result.getValueAsHexString() + " for register " + reg_read.getValue() + "/" + ArchCfg.getRegisterDescription(reg_read.getValue()) + " instead of value: " + alu_in.getValueAsHexString());
alu_in.setValue(old_alu_result);
}
}
else
{
logger.info("{FW} suppressing forwarding of register 0/" + ArchCfg.getRegisterDescription(0));
}
}
if (old_inst.getWriteRt() && (reg_read.getValue() == old_inst.getRt().getValue()))
{
if(old_inst.getRt().getValue() != 0)
{
if(old_inst.getLoad())
{
logger.debug("{FW} using LD result " + old_ld_result.getValueAsHexString() + " for register " + reg_read.getValue() + "/" + ArchCfg.getRegisterDescription(reg_read.getValue()) + " instead of value: " + alu_in.getValueAsHexString());
alu_in.setValue(old_ld_result);
}
else
{
logger.debug("{FW} using ALU result " + old_alu_result.getValueAsHexString() + " for register " + reg_read.getValue() + "/" + ArchCfg.getRegisterDescription(reg_read.getValue()) + " instead of value: " + alu_in.getValueAsHexString());
alu_in.setValue(old_alu_result);
}
}
else
{
logger.info("{FW} suppressing forwarding of register 0/" + ArchCfg.getRegisterDescription(0));
}
}
}
}