/******************************************************************************* * 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.branchPrediction; import org.apache.log4j.Logger; import openDLX.datatypes.BranchPredictorState; import openDLX.datatypes.BranchPredictorType; import openDLX.datatypes.BranchTargetBufferLookupResult; import openDLX.datatypes.uint32; import openDLX.exception.BranchPredictionException; /** * Direct-mapped branch target buffer */ public class BranchTargetBuffer { /// Logging facility private static Logger logger = Logger.getLogger("BTB"); /// Number of entries of the branch target buffer private int btb_size; /// Configuration that 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) private boolean reset_predictor_on_overwrite; /// The type of the used branch predictor private BranchPredictorType predictor_type; /// The branch predictors initial state (on startup) private BranchPredictorState predictor_initial_state; /// Determines if a branch target buffer entry was already used and thus contains a branch address, branch target, and a prediction (encoded by the predictors internal state) private boolean branch_entry_valid[]; /// Holds the branch address (the pc of the branch instruction) for each branch target buffer entry (if branch_entry_valid is false the entry was not used yet and thus is invalid) private uint32 branch_addresses[]; /// Contains the branch target address of each branch target buffer entry (each branch has at least one branch target, which is calculated by the ALU during the execution stage) /// Register-indirect jumps may jump to different addresses (like for a returning jump at the end of the function), thus on branch prediction the the predicted target has also be taken into account to decide if a branch was correctly predicted. private uint32 branch_targets[]; /// The branch predictors per entry of the branch target buffer private BranchPredictor branch_predictors[]; private final boolean throwExceptionForUntestedFeatures = true; /** * Constructor * @param size Number of entries of the branch target buffer * @param bp_type Type of the used predictors * @param bp_init_state Initial state of the predictors * @param resetPredictorOnOverwrite Boolean that 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) * @throws BranchPredictionException */ public BranchTargetBuffer(int size, BranchPredictorType bp_type, BranchPredictorState bp_init_state, boolean resetPredictorOnOverwrite) throws BranchPredictionException { // enforce that the BTB size is a power of two if(Integer.bitCount(size)!=1) { throw new BranchPredictionException("The BTB size has to be a power of two!"); } if(bp_type == BranchPredictorType.UNKNOWN) { throw new BranchPredictionException("Unknown branch prediction type: " + bp_type); } btb_size = size; reset_predictor_on_overwrite = resetPredictorOnOverwrite; predictor_type = bp_type; predictor_initial_state = bp_init_state; initialize(); } /** * Initializes the branch target buffer. * Creates arrays and initializes branch predictors. * @throws BranchPredictionException */ private void initialize() throws BranchPredictionException { branch_entry_valid = new boolean[btb_size]; branch_addresses = new uint32[btb_size]; branch_targets = new uint32[btb_size]; branch_predictors = new BranchPredictor[btb_size]; logger.info("Initialising the predictors with: " + predictor_initial_state); for(int i = 0; i < btb_size; i++) { branch_entry_valid[i] = false; branch_addresses[i] = new uint32(0); branch_targets[i] = new uint32(0); switch(predictor_type) { case S_ALWAYS_NOT_TAKEN: branch_predictors[i] = new StaticBranchPredictor(false); break; case S_ALWAYS_TAKEN: branch_predictors[i] = new StaticBranchPredictor(true); break; case S_BACKWARD_TAKEN: logger.error("Branch predictor S_BACKWARD_TAKEN currently not implemented."); if(throwExceptionForUntestedFeatures) { throw new BranchPredictionException("Branch predictor S_BACKWARD_TAKEN currently not implemented."); } break; case D_1BIT: branch_predictors[i] = new OneBitBranchPredictor(predictor_initial_state); break; case D_2BIT_SATURATION: branch_predictors[i] = new TwoBitBranchPredictorSaturation(predictor_initial_state); break; case D_2BIT_HYSTERESIS: branch_predictors[i] = new TwoBitBranchPredictorHysteresis(predictor_initial_state); break; default: logger.error("Unsupported type of branch predictor."); } } } /** * Updates the branch target buffer after calculation of the branch address by the ALU. * @param branch_pc The program counter of the branch instruction * @param branch_tgt The calculated target address of the branch * @param taken True if the BranchControl module decided that the branch is taken, false otherwise * @throws BranchPredictionException */ public void updateOnBranch(uint32 branch_pc, uint32 branch_tgt, boolean taken) throws BranchPredictionException { int branch_idx = getIndexForBranchPc(branch_pc); if((branch_entry_valid[branch_idx]) && (branch_addresses[branch_idx].getValue() == branch_pc.getValue()) && (branch_targets[branch_idx].getValue() == branch_tgt.getValue())) { // BTB hit // just update the predictor branch_predictors[branch_idx].updateState(taken); } else { // BTB miss // caused by: (1) access of unused entry, (2) access an entry of another branch, or (3) access of the entry of the very same branch but with another target // create new BTB entry (eventually overwrite the old one) branch_entry_valid[branch_idx] = true; branch_addresses[branch_idx].setValue(branch_pc); branch_targets[branch_idx].setValue(branch_tgt); if(reset_predictor_on_overwrite) { branch_predictors[branch_idx].reset(); } // update the predictor branch_predictors[branch_idx].updateState(taken); } } /** * Looks up the prediction of the branch target buffer for a specific branch. * Notice that depending on when the method is called the branch prediction result my differ. For a branch prediction the lookupBranch() function has to be called at the decode stage before the branch target is calculated. * @param branch_pc The pc of the branch that is to be looked up * @return Returns the result of the lookup: On branch target buffer hit, either HIT_PREDICT_TAKEN or HIT_PREDICT_NOT_TAKEN is returned (depending on the prediction). On branch target buffer miss, MISS is returned. */ public BranchTargetBufferLookupResult lookupBranch(uint32 branch_pc) { int branch_idx = getIndexForBranchPc(branch_pc); if((branch_entry_valid[branch_idx]) && (branch_addresses[branch_idx].getValue() == branch_pc.getValue())) { // BTB hit if(branch_predictors[branch_idx].predictsTaken()) { // predictor says taken return BranchTargetBufferLookupResult.HIT_PREDICT_TAKEN; } else { // predictor says not taken return BranchTargetBufferLookupResult.HIT_PREDICT_NOT_TAKEN; } } else { // BTB miss if(branch_entry_valid[branch_idx]) { // capacity miss } else { // cold / compulsory miss } return BranchTargetBufferLookupResult.MISS; } } /** * Checks if a prediction was performed correctly. The check has to be performed _before_ the branch target buffer is updated for the specific branch (by updateOnBranch()) * - Notice: that a jump that is predicted taken, but to another jump target, HAS to be considered as mispredicted. * - Notice: on a branch target buffer miss the prediction is correct, if the predictor predicts that the jump is not taken and it is not taken. * @throws BranchPredictionException * @returns True if the prediction direction and if jumping the branch target is similar for the given branch, else false. */ public boolean checkPrediction(uint32 branch_pc, uint32 branch_tgt, boolean jumped) throws BranchPredictionException { boolean result = false; switch(lookupBranch(branch_pc)) { case HIT_PREDICT_TAKEN: result = ((jumped == true) && (branch_tgt.getValue() == getStoredBranchTarget(branch_pc).getValue())); break; case HIT_PREDICT_NOT_TAKEN: result = (jumped == false); break; case MISS: // The interpretation of non-mapped jumps as correct, iff the jump is not taken and the predictor also predicted this. // Anyhow, this is not correct because actually no misprediction has occurred. // result = ((jumped == false)&&(jumped == getPredictorsBranchDecision(branch_pc))); // Since a btb miss results in a predict not taken, a jump that is actually not taken it is interpreted as correct prediction on btb miss. result = (jumped == false); break; default: throw new BranchPredictionException("Unknown branch prediction result."); } return result; } /** * Returns the branch target of a branch stored in the branch target buffer * @param branch_pc The program counter of the branch * @return The branch target of a branch stored in the branch target buffer */ private uint32 getStoredBranchTarget(uint32 branch_pc) { return branch_targets[getIndexForBranchPc(branch_pc)]; } /** * Returns the predictor state of the selected predictor for a branch in the branch target buffer * @param branch_pc The program counter of the branch * @return the predictor state of the selected predictor for a branch in the branch target buffer */ public BranchPredictorState getPredictorState(uint32 branch_pc) { return branch_predictors[getIndexForBranchPc(branch_pc)].getState(); } /** * Returns the index where a branch is stored in the branch target buffer * Implements the direct-mapped organization of the branch target buffer * @return the index where a branch is stored in the branch target buffer */ public int getIndexForBranchPc(uint32 branch_pc) { return branch_pc.getValue() & (btb_size - 1); } /** * Returns the branch target address of a branch target buffer entry selected by a program counter * @param branch_pc The program counter of a branch to select the entry of which the stored branch target is to be returned * @return The target of the branch entry selected by a program counter stored in the branch target buffer */ public uint32 getBranchTarget(uint32 branch_pc) { return new uint32(branch_targets[getIndexForBranchPc(branch_pc)]); } }