diff --git a/src/branch_unit.sv b/src/branch_unit.sv new file mode 100644 index 000000000..65eb6627d --- /dev/null +++ b/src/branch_unit.sv @@ -0,0 +1,128 @@ +// Author: Florian Zaruba, ETH Zurich +// Date: 09.05.2017 +// Description: Branch target calculation and comparison +// +// +// Copyright (C) 2017 ETH Zurich, University of Bologna +// All rights reserved. +// +// This code is under development and not yet released to the public. +// Until it is released, the code is under the copyright of ETH Zurich and +// the University of Bologna, and may contain confidential and/or unpublished +// work. Any reuse/redistribution is strictly forbidden without written +// permission from ETH Zurich. +// +// Bug fixes and contributions will eventually be released under the +// SolderPad open hardware license in the context of the PULP platform +// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the +// University of Bologna. +// +import ariane_pkg::*; + +module branch_unit ( + input fu_op operator_i, + input logic [63:0] operand_a_i, + input logic [63:0] operand_b_i, + input logic [63:0] operand_c_i, + input logic [63:0] imm_i, + input logic [63:0] pc_i, + input logic is_compressed_instr_i, + input logic fu_valid_i, // any functional unit is valid, check that there is no accidental mis-predict + input logic valid_i, + + input branchpredict_sbe branch_predict_i, // this is the address we predicted + output branchpredict resolved_branch_o, // this is the actual address we are targeting + output logic resolve_branch_o, // to ID to clear that we resolved the branch and we can + // accept new entries to the scoreboard + output exception branch_ex_o // branch exception out +); + logic [63:0] target_address; + logic [63:0] next_pc; + logic comparison_result; // result of comparison + logic sgn; // sign extend + + always_comb begin : branch_resolve + // by default e.g.: when this is a jump, the branch is taken + // so set the comparison result to 1 + comparison_result = 1'b1; + // sign switch + sgn = 1'b1; + // if this is an unsigned operation clear the sign bit + // this should ease data-path extraction + if (operator_i inside {LTU, GEU}) + sgn = 1'b0; + // get the right comparison result + case (operator_i) + EQ: comparison_result = operand_a_i == operand_b_i; + NE: comparison_result = operand_a_i != operand_b_i; + LTS: comparison_result = ($signed({sgn & operand_a_i[63], operand_a_i}) < $signed({sgn & operand_b_i[63], operand_b_i})); + GES: comparison_result = ($signed({sgn & operand_a_i[63], operand_a_i}) >= $signed({sgn & operand_b_i[63], operand_b_i})); + default: comparison_result = 1'b1; + endcase + end + // here we handle the various possibilities of mis-predicts + always_comb begin : mispredict_handler + target_address = 64'b0; + resolved_branch_o.pc = 64'b0; + resolved_branch_o.target_address = 64'b0; + resolved_branch_o.is_taken = 1'b0; + resolved_branch_o.valid = valid_i; + resolved_branch_o.is_mispredict = 1'b0; + resolved_branch_o.is_lower_16 = 1'b0; + resolve_branch_o = 1'b0; + // calculate next PC, depending on whether the instruction is compressed or not this may be different + next_pc = pc_i + ((is_compressed_instr_i) ? 64'h2 : 64'h4); + // calculate target address simple 64 bit addition + target_address = $unsigned($signed(operand_c_i) + $signed(imm_i)); + + if (valid_i) begin + // save PC - we need this to get the target row in the branch target buffer + // we play this trick with the branch instruction which wraps a byte boundary: + // |---------- Place the prediction on this PC + // \/ + // ____________________________________________________ + // |branch [15:0] | branch[31:16] | compressed 1[15:0] | + // |____________________________________________________ + // This will relief the prefetcher to re-fetch partially fetched unaligned branch instructions e.g.: + // we don't have a back arch between prefetcher and decoder/instruction FIFO. + resolved_branch_o.pc = (is_compressed_instr_i || pc_i[1] == 1'b0) ? pc_i : ({pc_i[63:2], 2'b0} + 64'h4); + // save if the branch instruction was in the lower 16 bit of the instruction word + // the first case is a compressed instruction which is in slot 0 + // the other case is a misaligned uncompressed instruction which we only predict in the next cycle (see notes above) + resolved_branch_o.is_lower_16 = (is_compressed_instr_i && pc_i[1] == 1'b0) || (!is_compressed_instr_i && pc_i[1] == 1'b1); + // write target address which goes to pc gen + resolved_branch_o.target_address = (comparison_result) ? target_address : next_pc; + resolved_branch_o.is_taken = comparison_result; + // we've detected a branch in ID with the following parameters + // we mis-predicted e.g.: the predicted address is unequal to the actual address + if (target_address[0] == 1'b0) begin + // TODO in case of branch which is not taken it is not necessary to check for the address + if (target_address != branch_predict_i.predict_address // we mis-predicted the address of the branch + || branch_predict_i.predict_taken != comparison_result // we mis-predicted the outcome of the branch + || branch_predict_i.valid == 1'b0 // this means branch-prediction thought it was no + // branch but in reality it was one + ) begin + resolved_branch_o.is_mispredict = 1'b1; + end + end + // to resolve the branch in ID -> only do this if this was indeed a branch (hence vald_i is asserted) + resolve_branch_o = 1'b1; + // the other case would be that this instruction was no branch but branch prediction thought that it was one + // this is essentially also a mis-predict + end else if (fu_valid_i && branch_predict_i.valid) begin + // re-set the branch to the next PC + resolved_branch_o.is_mispredict = 1'b1; + resolved_branch_o.target_address = next_pc; + end + end + // use ALU exception signal for storing instruction fetch exceptions if + // the target address is not aligned to a 2 byte boundary + always_comb begin : exception_handling + branch_ex_o.cause = INSTR_ADDR_MISALIGNED; + branch_ex_o.valid = 1'b0; + branch_ex_o.tval = pc_i; + // only throw exception if this is indeed a branch + if (valid_i && target_address[0] != 1'b0) + branch_ex_o.valid = 1'b1; + end +endmodule \ No newline at end of file