mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 13:47:13 -04:00
Add new branch unit
This commit is contained in:
parent
c56922a0f7
commit
9a312d5b8b
1 changed files with 128 additions and 0 deletions
128
src/branch_unit.sv
Normal file
128
src/branch_unit.sv
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue