/******************************************************************************* * 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.Properties; import java.util.Queue; import org.apache.log4j.Logger; import openDLX.branchPrediction.BranchTargetBuffer; import openDLX.datatypes.ArchCfg; import openDLX.datatypes.BranchPredictionModuleExecuteData; import openDLX.datatypes.BranchPredictionModuleFetchData; import openDLX.datatypes.BranchPredictionModuleOutputData; import openDLX.datatypes.BranchPredictorState; import openDLX.datatypes.BranchPredictorType; import openDLX.datatypes.BranchTargetBufferLookupResult; import openDLX.datatypes.ExecuteBranchPredictionData; import openDLX.datatypes.FetchDecodeData; import openDLX.datatypes.Instruction; import openDLX.datatypes.uint32; import openDLX.exception.BranchPredictionException; import openDLX.exception.PipelineException; import openDLX.util.Statistics; /** * @brief Module to encapsulate the branch predictor in the pipeline */ public class BranchPredictionModule { /// Logging facility private static Logger logger = Logger.getLogger("BP_MODULE"); /// The actual branch target buffer with branch predictors private BranchTargetBuffer btb; /// Central module for simulation statistics private Statistics stat; /// Input latch for the branch predictor module (table update part) private Queue<ExecuteBranchPredictionData> execute_branchprediction_latch; /// Input latch for the branch predictor module (table lookup part) private Queue<FetchDecodeData> fetch_branchprediction_latch; /** * @brief Constructor * @param config Configuration object, containing the branch predictor configuration. * Currently the configuration entries:\n * - btb_size - determine the size of the branch target buffer * - btb_predictor - set the used predictor type (BranchPredictorType) * - btb_predictor_initial_state - defines the initial state of the predictors (BranchPredictorState) * - btb_predictor_reset_on_overwrite - defines the behavior on overwriting a branch target buffer entry by another branch (if true, which is not recommended, the predictor is reset to the initial state on branch target buffer miss)\n * are supported. * \sa BranchPredictorType, BranchPredictorState * @throws PipelineException */ public BranchPredictionModule(Properties config) throws PipelineException { // obtain settings for the BTB int btb_size = 1; if(config.getProperty("btb_size")!=null) { btb_size = Integer.decode(config.getProperty("btb_size")); } // get the predictor type, default value is S_ALWAYS_NOT_TAKEN BranchPredictorType btb_predictor = BranchPredictorType.S_ALWAYS_NOT_TAKEN; if(config.getProperty("btb_predictor")!=null) { btb_predictor = getBranchPredictorTypeFromString(config.getProperty("btb_predictor")); } // also set the architecture variable (just for completeness) ArchCfg.branch_predictor_type = btb_predictor; // get the predictor initial state, default value is PREDICT_NOT_TAKEN // Notice each predictor may have a different set of supported predictor states. BranchPredictorState btb_predictor_initial_state = BranchPredictorState.PREDICT_NOT_TAKEN; if(config.getProperty("btb_predictor_initial_state")!=null) { btb_predictor_initial_state = getBranchPredictorInitialStateFromString(config.getProperty("btb_predictor_initial_state")); } // get the behavior if a btb entry is overwritten, the recommended default is false boolean btb_predictor_reset_on_overwrite = false; if(config.getProperty("btb_predictor_reset_on_overwrite")!=null) { if(Integer.decode(config.getProperty("btb_predictor_reset_on_overwrite"))==0) { btb_predictor_reset_on_overwrite = false; } else { btb_predictor_reset_on_overwrite = true; } } btb = new BranchTargetBuffer(btb_size, btb_predictor, btb_predictor_initial_state, btb_predictor_reset_on_overwrite); // get statistics object and set btb config stat = Statistics.getInstance(); stat.setBTBConfig(btb_size, btb_predictor); } /** * Parses the branch predictor selection string of the configuration file and returns the result as BranchPredictorType * @param bp_type The string selecting the used branch predictor * @return The selected branch predictor as BranchPredictorType */ public static BranchPredictorType getBranchPredictorTypeFromString(String bp_type) { if(bp_type.compareTo(BranchPredictorType.S_ALWAYS_NOT_TAKEN.toString()) == 0) { return BranchPredictorType.S_ALWAYS_NOT_TAKEN; } else if(bp_type.compareTo(BranchPredictorType.S_ALWAYS_TAKEN.toString()) == 0) { return BranchPredictorType.S_ALWAYS_TAKEN; } else if(bp_type.compareTo(BranchPredictorType.S_BACKWARD_TAKEN.toString()) == 0) { return BranchPredictorType.S_BACKWARD_TAKEN; } else if(bp_type.compareTo(BranchPredictorType.D_1BIT.toString()) == 0) { return BranchPredictorType.D_1BIT; } else if(bp_type.compareTo(BranchPredictorType.D_2BIT_SATURATION.toString()) == 0) { return BranchPredictorType.D_2BIT_SATURATION; } else if(bp_type.compareTo(BranchPredictorType.D_2BIT_HYSTERESIS.toString()) == 0) { return BranchPredictorType.D_2BIT_HYSTERESIS; } return BranchPredictorType.UNKNOWN; } /** * Parses the branch predictor selection string of the gui options window and returns the result as BranchPredictorType * @param bp_type The string selecting the used branch predictor * @return The selected branch predictor as BranchPredictorType */ public static BranchPredictorType getBranchPredictorTypeFromGuiString(String bp_type) { if(bp_type.compareTo(BranchPredictorType.S_ALWAYS_NOT_TAKEN.toGuiString()) == 0) { return BranchPredictorType.S_ALWAYS_NOT_TAKEN; } else if(bp_type.compareTo(BranchPredictorType.S_ALWAYS_TAKEN.toGuiString()) == 0) { return BranchPredictorType.S_ALWAYS_TAKEN; } else if(bp_type.compareTo(BranchPredictorType.S_BACKWARD_TAKEN.toGuiString()) == 0) { return BranchPredictorType.S_BACKWARD_TAKEN; } else if(bp_type.compareTo(BranchPredictorType.D_1BIT.toGuiString()) == 0) { return BranchPredictorType.D_1BIT; } else if(bp_type.compareTo(BranchPredictorType.D_2BIT_SATURATION.toGuiString()) == 0) { return BranchPredictorType.D_2BIT_SATURATION; } else if(bp_type.compareTo(BranchPredictorType.D_2BIT_HYSTERESIS.toGuiString()) == 0) { return BranchPredictorType.D_2BIT_HYSTERESIS; } return BranchPredictorType.UNKNOWN; } /** * Parses the branch predictor initial state setting string of the configuration file and returns the result as BranchPredictorState * @param bp_initial_state The string selecting the initial branch predictor state * @return The initial branch predictor state as BranchPredictorState */ public static BranchPredictorState getBranchPredictorInitialStateFromString(String bp_initial_state) { if(bp_initial_state.compareTo(BranchPredictorState.PREDICT_NOT_TAKEN.toString())==0) { return BranchPredictorState.PREDICT_NOT_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_TAKEN.toString())==0) { return BranchPredictorState.PREDICT_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_WEAKLY_NOT_TAKEN.toString())==0) { return BranchPredictorState.PREDICT_WEAKLY_NOT_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_STRONGLY_NOT_TAKEN.toString())==0) { return BranchPredictorState.PREDICT_STRONGLY_NOT_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_WEAKLY_TAKEN.toString())==0) { return BranchPredictorState.PREDICT_WEAKLY_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_STRONGLY_TAKEN.toString())==0) { return BranchPredictorState.PREDICT_STRONGLY_TAKEN; } return BranchPredictorState.UNKNOWN; } /** * Parses the branch predictor initial state setting string of gui options window and returns the result as BranchPredictorState * @param bp_initial_state The string selecting the initial branch predictor state * @return The initial branch predictor state as BranchPredictorState */ public static BranchPredictorState getBranchPredictorInitialStateFromGuiString(String bp_initial_state) { if(bp_initial_state.compareTo(BranchPredictorState.PREDICT_NOT_TAKEN.toGuiString())==0) { return BranchPredictorState.PREDICT_NOT_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_TAKEN.toGuiString())==0) { return BranchPredictorState.PREDICT_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_WEAKLY_NOT_TAKEN.toGuiString())==0) { return BranchPredictorState.PREDICT_WEAKLY_NOT_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_STRONGLY_NOT_TAKEN.toGuiString())==0) { return BranchPredictorState.PREDICT_STRONGLY_NOT_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_WEAKLY_TAKEN.toGuiString())==0) { return BranchPredictorState.PREDICT_WEAKLY_TAKEN; } else if (bp_initial_state.compareTo(BranchPredictorState.PREDICT_STRONGLY_TAKEN.toGuiString())==0) { return BranchPredictorState.PREDICT_STRONGLY_TAKEN; } return BranchPredictorState.UNKNOWN; } /** * Sets the input latch for the synchronous operation of the branch prediction module * @param executeBranchpredictionLatch The input latch containing all necessary information for the update part of the branch prediction (altering the predictors) * @param fetchBranchPredictionLatch The input latch containing the necessary information for the lookup part of the branch prediction (predicting jumps) */ public void setInputLatches(Queue<ExecuteBranchPredictionData> executeBranchpredictionLatch, Queue<FetchDecodeData> fetchBranchPredictionLatch) { execute_branchprediction_latch = executeBranchpredictionLatch; fetch_branchprediction_latch = fetchBranchPredictionLatch; } public BranchPredictionModuleOutputData doCycle() throws BranchPredictionException { // lookup for jump target BranchPredictionModuleOutputData bpmod = lookupTables(); // update prediction tables updateTables(); return bpmod; } /** * The synchronous operation of the branch prediction module * @throws BranchPredictionException */ public void updateTables() throws BranchPredictionException { ExecuteBranchPredictionData ebd = execute_branchprediction_latch.element(); Instruction inst = ebd.getInst(); uint32 branch_pc = ebd.getBranchPc(); uint32 branch_tgt = ebd.getBranchTgt(); boolean jump = ebd.getJumpTaken(); if(inst.getBranch()) { logger.info("Jump from " + branch_pc.getValueAsHexString() + " to " + branch_tgt.getValueAsHexString() + " that is |" + ((jump)?("taken"):("not taken")) + "| was predicted: |" + ((btb.checkPrediction(branch_pc, branch_tgt, jump)?("correctly"):("not correctly"))) + "| BTB said: |" + btb.lookupBranch(branch_pc) + "| BTB entry: |" + btb.getIndexForBranchPc(branch_pc) + "| predictor state: |" + btb.getPredictorState(branch_pc) + "|"); stat.countBranchInformation(branch_pc, btb.getIndexForBranchPc(branch_pc), branch_tgt, jump, btb.lookupBranch(branch_pc), btb.checkPrediction(branch_pc, branch_tgt, jump)); stat.countPredictions(btb.checkPrediction(branch_pc, branch_tgt, jump)); stat.countBTBAccesses(btb.lookupBranch(branch_pc)); btb.updateOnBranch(branch_pc, branch_tgt, jump); } } public BranchPredictionModuleOutputData lookupTables() { FetchDecodeData fdd = fetch_branchprediction_latch.element(); uint32 pc = fdd.getPc(); boolean do_speculative_jump = false; uint32 branch_tgt = new uint32(0); BranchTargetBufferLookupResult result = btb.lookupBranch(pc); if(result == BranchTargetBufferLookupResult.HIT_PREDICT_TAKEN) { do_speculative_jump = true; branch_tgt = btb.getBranchTarget(pc); } if((result == BranchTargetBufferLookupResult.HIT_PREDICT_NOT_TAKEN) || (result == BranchTargetBufferLookupResult.HIT_PREDICT_TAKEN)) { logger.debug("instruction at: " + pc.getValueAsHexString() + " found in BTB and is predicted as " + ((do_speculative_jump)?("taken to addr: " + branch_tgt.getValueAsHexString()):("not taken"))); } else if(result == BranchTargetBufferLookupResult.MISS) { logger.debug("instruction at: " + pc.getValueAsHexString() + " was not found in BTB"); } BranchPredictionModuleFetchData bpmfd = new BranchPredictionModuleFetchData(do_speculative_jump, pc, branch_tgt); BranchPredictionModuleExecuteData bpmed = new BranchPredictionModuleExecuteData(do_speculative_jump, pc, branch_tgt); return new BranchPredictionModuleOutputData(bpmfd, bpmed); } }