Add new branch unit

This commit is contained in:
Florian Zaruba 2017-05-21 11:03:36 +02:00
parent c56922a0f7
commit 9a312d5b8b

128
src/branch_unit.sv Normal file
View file

@ -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