[rtl] Introduce static branch prediction

This commit is contained in:
Greg Chadwick 2020-07-10 09:50:56 +01:00
parent b1531f2e38
commit 6123ac7719
20 changed files with 593 additions and 104 deletions

View file

@ -95,6 +95,7 @@ jobs:
- small
- experimental-maxperf-pmp
- experimental-maxperf-pmp-bmfull
- experimental-branch-predictor
# Run lint on simple system
- bash: |

View file

@ -60,6 +60,12 @@ parameters:
default: 0
description: "Enables third pipeline stage (EXPERIMENTAL)"
BranchPredictor:
datatype: int
paramtype: vlogparam
default: 0
description: "Enables static branch prediction (EXPERIMENTAL)"
PMPEnable:
datatype: int
default: 0
@ -91,6 +97,7 @@ targets:
- RegFile
- BranchTargetALU
- WritebackStage
- BranchPredictor
- PMPEnable
- PMPGranularity
- PMPNumRegions

View file

@ -24,6 +24,7 @@ module ibex_riscv_compliance (
parameter ibex_pkg::regfile_e RegFile = ibex_pkg::RegFileFF;
parameter bit BranchTargetALU = 1'b0;
parameter bit WritebackStage = 1'b0;
parameter bit BranchPredictor = 1'b0;
logic clk_sys, rst_sys_n;
@ -119,6 +120,7 @@ module ibex_riscv_compliance (
.RegFile (RegFile ),
.BranchTargetALU (BranchTargetALU ),
.WritebackStage (WritebackStage ),
.BranchPredictor (BranchPredictor ),
.DmHaltAddr (32'h00000000 ),
.DmExceptionAddr (32'h00000000 )
) u_core (

View file

@ -34,6 +34,7 @@ ${PRJ_DIR}/rtl/ibex_pkg.sv
${PRJ_DIR}/rtl/ibex_tracer_pkg.sv
${PRJ_DIR}/rtl/ibex_tracer.sv
${PRJ_DIR}/rtl/ibex_alu.sv
${PRJ_DIR}/rtl/ibex_branch_predict.sv
${PRJ_DIR}/rtl/ibex_compressed_decoder.sv
${PRJ_DIR}/rtl/ibex_controller.sv
${PRJ_DIR}/rtl/ibex_cs_registers.sv

View file

@ -54,6 +54,7 @@ module core_ibex_tb_top;
parameter ibex_pkg::regfile_e RegFile = `IBEX_CFG_RegFile;
parameter bit BranchTargetALU = 1'b0;
parameter bit WritebackStage = 1'b0;
parameter bit BranchPredictor = 1'b0;
ibex_core_tracing #(
.DmHaltAddr (`BOOT_ADDR + 'h0 ),
@ -66,7 +67,8 @@ module core_ibex_tb_top;
.RV32B (RV32B ),
.RegFile (RegFile ),
.BranchTargetALU (BranchTargetALU ),
.WritebackStage (WritebackStage )
.WritebackStage (WritebackStage ),
.BranchPredictor (BranchPredictor )
) dut (
.clk_i (clk ),
.rst_ni (rst_n ),

View file

@ -74,6 +74,12 @@ parameters:
paramtype: vlogparam
description: "Enables security hardening features (EXPERIMENTAL) [0/1]"
BranchPredictor:
datatype: int
paramtype: vlogparam
default: 0
description: "Enables static branch prediction (EXPERIMENTAL)"
PMPEnable:
datatype: int
default: 0
@ -107,6 +113,7 @@ targets:
- BranchTargetALU
- WritebackStage
- SecureIbex
- BranchPredictor
- PMPEnable
- PMPGranularity
- PMPNumRegions

View file

@ -45,6 +45,7 @@ module ibex_simple_system (
parameter ibex_pkg::regfile_e RegFile = `RegFile;
parameter bit BranchTargetALU = 1'b0;
parameter bit WritebackStage = 1'b0;
parameter bit BranchPredictor = 1'b0;
parameter SRAMInitFile = "";
logic clk_sys = 1'b0, rst_sys_n;
@ -170,6 +171,7 @@ module ibex_simple_system (
.RegFile ( RegFile ),
.BranchTargetALU ( BranchTargetALU ),
.WritebackStage ( WritebackStage ),
.BranchPredictor ( BranchPredictor ),
.DmHaltAddr ( 32'h00100000 ),
.DmExceptionAddr ( 32'h00100000 )
) u_core (

View file

@ -14,6 +14,7 @@ small:
RegFile : "ibex_pkg::RegFileFF"
BranchTargetALU : 0
WritebackStage : 0
BranchPredictor : 0
PMPEnable : 0
PMPGranularity : 0
PMPNumRegions : 4
@ -32,6 +33,7 @@ experimental-maxperf:
RegFile : "ibex_pkg::RegFileFF"
BranchTargetALU : 1
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 0
PMPGranularity : 0
PMPNumRegions : 4
@ -44,6 +46,7 @@ experimental-maxperf-pmp:
RegFile : "ibex_pkg::RegFileFF"
BranchTargetALU : 1
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 1
PMPGranularity : 0
PMPNumRegions : 16
@ -56,6 +59,7 @@ experimental-maxperf-pmp-bmbalanced:
RegFile : "ibex_pkg::RegFileFF"
BranchTargetALU : 1
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 1
PMPGranularity : 0
PMPNumRegions : 16
@ -68,7 +72,24 @@ experimental-maxperf-pmp-bmfull:
RegFile : "ibex_pkg::RegFileFF"
BranchTargetALU : 1
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 1
PMPGranularity : 0
PMPNumRegions : 16
# experimental-maxperf with branch predictor switched on, this exists to allow
# easy use of Ibex with the branch predictor in particular for CI runs. The
# branch predictor will be enabled in all the 'maxperf' configs after further
# development.
experimental-branch-predictor:
RV32E : 0
RV32M : "ibex_pkg::RV32MSingleCycle"
RV32B : "ibex_pkg::RV32BNone"
RegFile : "ibex_pkg::RegFileFF"
BranchTargetALU : 1
WritebackStage : 1
BranchPredictor : 1
PMPEnable : 0
PMPGranularity : 0
PMPNumRegions : 4

View file

@ -15,6 +15,7 @@ filesets:
- lowrisc:ibex:ibex_icache
files:
- rtl/ibex_alu.sv
- rtl/ibex_branch_predict.sv
- rtl/ibex_compressed_decoder.sv
- rtl/ibex_controller.sv
- rtl/ibex_cs_registers.sv
@ -111,6 +112,12 @@ parameters:
paramtype: vlogparam
description: "Enables third pipeline stage (EXPERIMENTAL) [0/1]"
BranchPredictor:
datatype: int
paramtype: vlogparam
default: 0
description: "Enables static branch prediction (EXPERIMENTAL)"
SecureIbex:
datatype: int
default: 0

View file

@ -71,6 +71,12 @@ parameters:
paramtype: vlogparam
description: "Enables third pipeline stage (EXPERIMENTAL) [0/1]"
BranchPredictor:
datatype: int
paramtype: vlogparam
default: 0
description: "Enables static branch prediction (EXPERIMENTAL)"
SecureIbex:
datatype: int
default: 0
@ -114,6 +120,7 @@ targets:
- RegFile
- BranchTargetALU
- WritebackStage
- BranchPredictor
- SecureIbex
- PMPEnable
- PMPGranularity

View file

@ -12,15 +12,24 @@
`verilator_config
lint_off -rule PINCONNECTEMPTY
// We have some boolean top-level parameters in e.g. ibex_core_tracing.sv.
// When building with fusesoc, these get set with defines like
// -GRV32M=1 (rather than -GRV32M=1'b1), leading to warnings like:
//
// Operator VAR '<varname>' expects 1 bits on the Initial value, but
// Initial value's CONST '32'h1' generates 32 bits.
//
// This signoff rule ignores errors like this. Note that it only
// matches when you set a 1-bit value to a literal 1, so it won't hide
// silly mistakes like setting it to 2.
//
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv"
-match "*expects 1 bits*Initial value's CONST '32'h1'*"
// Operator expects 1 bit on initial value but initial value's CONST generates
// 32 bits
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'RV32M'*"
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'RV32E'*"
// 32 bits, need a specific RV32B waiver as it uses enums so the above catch-all
// waiver doesn't work.
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'RV32B'*"
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'BranchTargetALU'*"
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'WritebackStage'*"
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'SecureIbex'*"
lint_off -rule WIDTH -file "*/rtl/ibex_core_tracing.sv" -match "*'PMPEnable'*"
// Bits of signal are not used: shift_amt_compl[5]
// cleaner to write all bits even if not all are used
@ -134,12 +143,14 @@ lint_off -rule UNUSED -file "*/rtl/ibex_register_file_fpga.sv" -match "*test_en_
lint_off -rule UNUSED -file "*/rtl/ibex_pmp.sv" -match "*clk_i*"
lint_off -rule UNUSED -file "*/rtl/ibex_compressed_decoder.sv" -match "*clk_i*"
lint_off -rule UNUSED -file "*/rtl/ibex_decoder.sv" -match "*clk_i*"
lint_off -rule UNUSED -file "*/rtl/ibex_branch_predict.sv" -match "*clk_i*"
// Signal is not used: rst_ni
// leaving clk and reset connected in-case we want to add assertions
lint_off -rule UNUSED -file "*/rtl/ibex_pmp.sv" -match "*rst_ni*"
lint_off -rule UNUSED -file "*/rtl/ibex_compressed_decoder.sv" -match "*rst_ni*"
lint_off -rule UNUSED -file "*/rtl/ibex_decoder.sv" -match "*rst_ni*"
lint_off -rule UNUSED -file "*/rtl/ibex_branch_predict.sv" -match "*rst_ni*"
// Bits of signal are not used: instr_alu[24:15,11:7]
// instr flops are duplicated to reduce fan-out, neater to just leave unused

100
rtl/ibex_branch_predict.sv Normal file
View file

@ -0,0 +1,100 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/**
* Branch Predictor
*
* This implements static branch prediction. It takes an instruction and it's PC and determines if
* it's a branch or a jump and calculates its target. For jumps it will always predict taken. For
* branches it will predict taken if the PC offset is negative.
*
* This handles both compressed and uncompressed instructions. Compressed instructions must be in
* the lower 16-bits of instr.
*
* The predictor is entirely combinitorial but takes clk/rst_n signals for use by assertions.
*/
`include "prim_assert.sv"
module ibex_branch_predict (
input logic clk_i,
input logic rst_ni,
// Instruction from fetch stage
input logic [31:0] fetch_rdata_i,
input logic [31:0] fetch_pc_i,
input logic fetch_valid_i,
// Prediction for supplied instruction
output logic predict_branch_taken_o,
output logic [31:0] predict_branch_pc_o
);
import ibex_pkg::*;
logic [31:0] imm_j_type;
logic [31:0] imm_b_type;
logic [31:0] imm_cj_type;
logic [31:0] imm_cb_type;
logic [31:0] branch_imm;
logic [31:0] instr;
logic instr_j;
logic instr_b;
logic instr_cj;
logic instr_cb;
logic instr_b_taken;
// Provide short internal name for fetch_rdata_i due to reduce line wrapping
assign instr = fetch_rdata_i;
// Extract and sign-extend to 32-bit the various immediates that may be used to calculate the
// target
// Uncompressed immediates
assign imm_j_type = { {12{instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0 };
assign imm_b_type = { {19{instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0 };
// Compressed immediates
assign imm_cj_type = { {20{instr[12]}}, instr[12], instr[8], instr[10:9], instr[6], instr[7],
instr[2], instr[11], instr[5:3], 1'b0 };
assign imm_cb_type = { {23{instr[12]}}, instr[12], instr[6:5], instr[2], instr[11:10],
instr[4:3], 1'b0};
// Determine if the instruction is a branch or a jump
// Uncompressed branch/jump
assign instr_b = opcode_e'(instr[6:0]) == OPCODE_BRANCH;
assign instr_j = opcode_e'(instr[6:0]) == OPCODE_JAL;
// Compressed branch/jump
assign instr_cb = (instr[1:0] == 2'b01) & ((instr[15:13] == 3'b110) | (instr[15:13] == 3'b111));
assign instr_cj = (instr[1:0] == 2'b01) & ((instr[15:13] == 3'b101) | (instr[15:13] == 3'b001));
// Select out the branch offset for target calculation based upon the instruction type
always_comb begin
branch_imm = imm_b_type;
unique case (1'b1)
instr_j : branch_imm = imm_j_type;
instr_b : branch_imm = imm_b_type;
instr_cj : branch_imm = imm_cj_type;
instr_cb : branch_imm = imm_cb_type;
default : ;
endcase
end
`ASSERT_IF(BranchInsTypeOnehot, $onehot0({instr_j, instr_b, instr_cj, instr_cb}), fetch_valid_i);
// Determine branch prediction, taken if offset is negative
assign instr_b_taken = (instr_b & imm_b_type[31]) | (instr_cb & imm_cb_type[31]);
// Always predict jumps taken otherwise take prediction from `instr_b_taken`
assign predict_branch_taken_o = fetch_valid_i & (instr_j | instr_cj | instr_b_taken);
// Calculate target
assign predict_branch_pc_o = fetch_pc_i + branch_imm;
endmodule

View file

@ -10,7 +10,8 @@
`include "prim_assert.sv"
module ibex_controller #(
parameter bit WritebackStage = 0
parameter bit WritebackStage = 0,
parameter bit BranchPredictor = 0
) (
input logic clk_i,
input logic rst_ni,
@ -26,14 +27,16 @@ module ibex_controller #(
input logic ebrk_insn_i, // decoder has EBREAK instr
input logic csr_pipe_flush_i, // do CSR-related pipeline flush
// from IF-ID pipeline stage
input logic instr_valid_i, // instr from IF-ID reg is valid
input logic [31:0] instr_i, // instr from IF-ID reg, for mtval
input logic [15:0] instr_compressed_i, // instr from IF-ID reg, for mtval
input logic instr_is_compressed_i, // instr from IF-ID reg is compressed
input logic instr_fetch_err_i, // instr from IF-ID reg has error
input logic instr_fetch_err_plus2_i, // instr from IF-ID reg error is x32
input logic [31:0] pc_id_i, // instr from IF-ID reg address
// instr from IF-ID pipeline stage
input logic instr_valid_i, // instr is valid
input logic [31:0] instr_i, // instr data (uncompressed if compressed
// otherwise raw data) for mtval
input logic [15:0] instr_compressed_i, // instr compressed data for mtval
input logic instr_is_compressed_i, // instr is compressed
input logic instr_bp_taken_i, // instr was predicted taken branch
input logic instr_fetch_err_i, // instr has error
input logic instr_fetch_err_plus2_i, // instr error is x32
input logic [31:0] pc_id_i, // instr address
// to IF-ID pipeline stage
output logic instr_valid_clear_o, // kill instr in IF-ID reg
@ -47,6 +50,8 @@ module ibex_controller #(
output logic pc_set_spec_o, // speculative branch
output ibex_pkg::pc_sel_e pc_mux_o, // IF stage fetch address selector
// (boot, normal, exception...)
output logic nt_branch_mispredict_o, // Not-taken branch in ID/EX was
// mispredicted (predicted taken)
output ibex_pkg::exc_pc_sel_e exc_pc_mux_o, // IF stage selector for exception PC
output ibex_pkg::exc_cause_e exc_cause_o, // for IF stage, CSRs
@ -59,6 +64,7 @@ module ibex_controller #(
// jump/branch signals
input logic branch_set_i, // branch taken set signal
input logic branch_set_spec_i, // speculative branch signal
input logic branch_not_set_i, // branch was not taken
input logic jump_set_i, // jump taken set signal
// interrupt signals
@ -369,27 +375,28 @@ module ibex_controller #(
pc_mux_o = PC_BOOT;
pc_set_o = 1'b0;
pc_set_spec_o = 1'b0;
nt_branch_mispredict_o = 1'b0;
exc_pc_mux_o = EXC_PC_IRQ;
exc_cause_o = EXC_CAUSE_INSN_ADDR_MISA; // = 6'h00
exc_pc_mux_o = EXC_PC_IRQ;
exc_cause_o = EXC_CAUSE_INSN_ADDR_MISA; // = 6'h00
ctrl_fsm_ns = ctrl_fsm_cs;
ctrl_fsm_ns = ctrl_fsm_cs;
ctrl_busy_o = 1'b1;
ctrl_busy_o = 1'b1;
halt_if = 1'b0;
retain_id = 1'b0;
flush_id = 1'b0;
debug_csr_save_o = 1'b0;
debug_cause_o = DBG_CAUSE_EBREAK;
debug_mode_d = debug_mode_q;
nmi_mode_d = nmi_mode_q;
debug_csr_save_o = 1'b0;
debug_cause_o = DBG_CAUSE_EBREAK;
debug_mode_d = debug_mode_q;
nmi_mode_d = nmi_mode_q;
perf_tbranch_o = 1'b0;
perf_jump_o = 1'b0;
perf_tbranch_o = 1'b0;
perf_jump_o = 1'b0;
controller_run_o = 1'b0;
controller_run_o = 1'b0;
unique case (ctrl_fsm_cs)
RESET: begin
@ -493,16 +500,29 @@ module ibex_controller #(
end
end
if ((branch_set_i || jump_set_i) && !special_req_branch) begin
pc_set_o = 1'b1;
if (!special_req_branch) begin
if ((branch_set_i || jump_set_i)) begin
// Only set the PC if the branch predictor hasn't already done the branch for us
pc_set_o = BranchPredictor ? ~instr_bp_taken_i : 1'b1;
perf_tbranch_o = branch_set_i;
perf_jump_o = jump_set_i;
perf_tbranch_o = branch_set_i;
perf_jump_o = jump_set_i;
end
if (BranchPredictor) begin
if (instr_bp_taken_i & branch_not_set_i) begin
// If the instruction is a branch that was predicted to be taken but was not taken
// signal a mispredict.
nt_branch_mispredict_o = 1'b1;
end
end
end
// pc_set signal excluding branch taken condition
if ((branch_set_spec_i || jump_set_i) && !special_req_branch) begin
pc_set_spec_o = 1'b1;
// Only speculatively set the PC if the branch predictor hasn't already done the branch
// for us
pc_set_spec_o = BranchPredictor ? ~instr_bp_taken_i : 1'b1;
end
// If entering debug mode or handling an IRQ the core needs to wait
@ -809,6 +829,8 @@ module ibex_controller #(
// Assertions //
////////////////
`ASSERT(AlwaysInstrClearOnMispredict, nt_branch_mispredict_o -> instr_valid_clear_o);
// Selectors must be known/valid.
`ASSERT(IbexCtrlStateValid, ctrl_fsm_cs inside {
RESET, BOOT_SET, WAIT_SLEEP, SLEEP, FIRST_FETCH, DECODE, FLUSH,

View file

@ -26,6 +26,7 @@ module ibex_core #(
parameter bit WritebackStage = 1'b0,
parameter bit ICache = 1'b0,
parameter bit ICacheECC = 1'b0,
parameter bit BranchPredictor = 1'b0,
parameter bit DbgTriggerEn = 1'b0,
parameter bit SecureIbex = 1'b0,
parameter int unsigned DmHaltAddr = 32'h1A110800,
@ -127,6 +128,7 @@ module ibex_core #(
// ease fan-out)
logic [15:0] instr_rdata_c_id; // Compressed instruction sampled inside IF stage
logic instr_is_compressed_id;
logic instr_bp_taken_id;
logic instr_fetch_err; // Bus error on instr fetch
logic instr_fetch_err_plus2; // Instruction error is misaligned
logic illegal_c_insn_id; // Illegal compressed instruction sent to ID stage
@ -150,6 +152,7 @@ module ibex_core #(
logic instr_valid_clear;
logic pc_set;
logic pc_set_spec;
logic nt_branch_mispredict;
pc_sel_e pc_mux_id; // Mux selector for next PC
exc_pc_sel_e exc_pc_mux_id; // Mux selector for exception PC
exc_cause_e exc_cause; // Exception cause
@ -393,7 +396,8 @@ module ibex_core #(
.DummyInstructions ( DummyInstructions ),
.ICache ( ICache ),
.ICacheECC ( ICacheECC ),
.SecureIbex ( SecureIbex )
.SecureIbex ( SecureIbex ),
.BranchPredictor ( BranchPredictor )
) if_stage_i (
.clk_i ( clk ),
.rst_ni ( rst_ni ),
@ -417,6 +421,7 @@ module ibex_core #(
.instr_rdata_alu_id_o ( instr_rdata_alu_id ),
.instr_rdata_c_id_o ( instr_rdata_c_id ),
.instr_is_compressed_id_o ( instr_is_compressed_id ),
.instr_bp_taken_o ( instr_bp_taken_id ),
.instr_fetch_err_o ( instr_fetch_err ),
.instr_fetch_err_plus2_o ( instr_fetch_err_plus2 ),
.illegal_c_insn_id_o ( illegal_c_insn_id ),
@ -429,6 +434,7 @@ module ibex_core #(
.pc_set_i ( pc_set ),
.pc_set_spec_i ( pc_set_spec ),
.pc_mux_i ( pc_mux_id ),
.nt_branch_mispredict_i ( nt_branch_mispredict ),
.exc_pc_mux_i ( exc_pc_mux_id ),
.exc_cause ( exc_cause ),
.dummy_instr_en_i ( dummy_instr_en ),
@ -472,7 +478,8 @@ module ibex_core #(
.BranchTargetALU ( BranchTargetALU ),
.DataIndTiming ( DataIndTiming ),
.SpecBranch ( SpecBranch ),
.WritebackStage ( WritebackStage )
.WritebackStage ( WritebackStage ),
.BranchPredictor ( BranchPredictor )
) id_stage_i (
.clk_i ( clk ),
.rst_ni ( rst_ni ),
@ -487,6 +494,7 @@ module ibex_core #(
.instr_rdata_alu_i ( instr_rdata_alu_id ),
.instr_rdata_c_i ( instr_rdata_c_id ),
.instr_is_compressed_i ( instr_is_compressed_id ),
.instr_bp_taken_i ( instr_bp_taken_id ),
// Jumps and branches
.branch_decision_i ( branch_decision ),
@ -499,6 +507,7 @@ module ibex_core #(
.pc_set_o ( pc_set ),
.pc_set_spec_o ( pc_set_spec ),
.pc_mux_o ( pc_mux_id ),
.nt_branch_mispredict_o ( nt_branch_mispredict ),
.exc_pc_mux_o ( exc_pc_mux_id ),
.exc_cause_o ( exc_cause ),
.icache_inval_o ( icache_inval ),

View file

@ -20,6 +20,7 @@ module ibex_core_tracing #(
parameter bit WritebackStage = 1'b0,
parameter bit ICache = 1'b0,
parameter bit ICacheECC = 1'b0,
parameter bit BranchPredictor = 1'b0,
parameter bit DbgTriggerEn = 1'b0,
parameter bit SecureIbex = 1'b0,
parameter int unsigned DmHaltAddr = 32'h1A110800,
@ -115,6 +116,7 @@ module ibex_core_tracing #(
.BranchTargetALU ( BranchTargetALU ),
.ICache ( ICache ),
.ICacheECC ( ICacheECC ),
.BranchPredictor ( BranchPredictor ),
.DbgTriggerEn ( DbgTriggerEn ),
.WritebackStage ( WritebackStage ),
.SecureIbex ( SecureIbex ),

View file

@ -32,6 +32,7 @@ module ibex_fetch_fifo #(
output logic out_valid_o,
input logic out_ready_i,
output logic [31:0] out_addr_o,
output logic [31:0] out_addr_next_o,
output logic [31:0] out_rdata_o,
output logic out_err_o,
output logic out_err_plus2_o
@ -55,6 +56,7 @@ module ibex_fetch_fifo #(
logic aligned_is_compressed, unaligned_is_compressed;
logic addr_incr_two;
logic [31:1] instr_addr_next;
logic [31:1] instr_addr_d, instr_addr_q;
logic instr_addr_en;
logic unused_addr_in;
@ -140,10 +142,12 @@ module ibex_fetch_fifo #(
assign addr_incr_two = instr_addr_q[1] ? unaligned_is_compressed :
aligned_is_compressed;
assign instr_addr_next = (instr_addr_q[31:1] +
// Increment address by 4 or 2
{29'd0,~addr_incr_two,addr_incr_two});
assign instr_addr_d = clear_i ? in_addr_i[31:1] :
(instr_addr_q[31:1] +
// Increment address by 4 or 2
{29'd0,~addr_incr_two,addr_incr_two});
instr_addr_next;
always_ff @(posedge clk_i) begin
if (instr_addr_en) begin
@ -151,8 +155,11 @@ module ibex_fetch_fifo #(
end
end
assign out_addr_o[31:1] = instr_addr_q[31:1];
assign out_addr_o[0] = 1'b0;
// Output both PC of current instruction and instruction following. PC of instruction following is
// required for the branch predictor. It's used to fetch the instruction following a branch that
// was not-taken but (mis)predicted taken.
assign out_addr_next_o = {instr_addr_next, 1'b0};
assign out_addr_o = {instr_addr_q, 1'b0};
// The LSB of the address is unused, since all addresses are halfword aligned
assign unused_addr_in = in_addr_i[0];

View file

@ -23,7 +23,8 @@ module ibex_id_stage #(
parameter bit DataIndTiming = 1'b0,
parameter bit BranchTargetALU = 0,
parameter bit SpecBranch = 0,
parameter bit WritebackStage = 0
parameter bit WritebackStage = 0,
parameter bit BranchPredictor = 0
) (
input logic clk_i,
input logic rst_ni,
@ -37,6 +38,7 @@ module ibex_id_stage #(
input logic [31:0] instr_rdata_alu_i, // from IF-ID pipeline registers
input logic [15:0] instr_rdata_c_i, // from IF-ID pipeline registers
input logic instr_is_compressed_i,
input logic instr_bp_taken_i,
output logic instr_req_o,
output logic instr_first_cycle_id_o,
output logic instr_valid_clear_o, // kill instr in IF-ID reg
@ -50,6 +52,7 @@ module ibex_id_stage #(
output logic pc_set_o,
output logic pc_set_spec_o,
output ibex_pkg::pc_sel_e pc_mux_o,
output logic nt_branch_mispredict_o,
output ibex_pkg::exc_pc_sel_e exc_pc_mux_o,
output ibex_pkg::exc_cause_e exc_cause_o,
@ -195,6 +198,7 @@ module ibex_id_stage #(
logic branch_in_dec;
logic branch_spec, branch_set_spec;
logic branch_set, branch_set_d;
logic branch_not_set;
logic branch_taken;
logic jump_in_dec;
logic jump_set_dec;
@ -519,7 +523,8 @@ module ibex_id_stage #(
assign illegal_insn_o = instr_valid_i & (illegal_insn_dec | illegal_csr_insn_i);
ibex_controller #(
.WritebackStage ( WritebackStage )
.WritebackStage ( WritebackStage ),
.BranchPredictor ( BranchPredictor )
) controller_i (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
@ -540,6 +545,7 @@ module ibex_id_stage #(
.instr_i ( instr_rdata_i ),
.instr_compressed_i ( instr_rdata_c_i ),
.instr_is_compressed_i ( instr_is_compressed_i ),
.instr_bp_taken_i ( instr_bp_taken_i ),
.instr_fetch_err_i ( instr_fetch_err_i ),
.instr_fetch_err_plus2_i ( instr_fetch_err_plus2_i ),
.pc_id_i ( pc_id_i ),
@ -554,6 +560,7 @@ module ibex_id_stage #(
.pc_set_o ( pc_set_o ),
.pc_set_spec_o ( pc_set_spec_o ),
.pc_mux_o ( pc_mux_o ),
.nt_branch_mispredict_o ( nt_branch_mispredict_o ),
.exc_pc_mux_o ( exc_pc_mux_o ),
.exc_cause_o ( exc_cause_o ),
@ -566,6 +573,7 @@ module ibex_id_stage #(
// jump/branch control
.branch_set_i ( branch_set ),
.branch_set_spec_i ( branch_set_spec ),
.branch_not_set_i ( branch_not_set ),
.jump_set_i ( jump_set ),
// interrupt signals
@ -687,11 +695,12 @@ module ibex_id_stage #(
end
// Holding branch_set/jump_set high for more than one cycle may not cause a functional issue but
// could generate needless prefetch buffer flushes and instruction fetches. ID/EX is designed such
// that this shouldn't ever happen.
`ASSERT(NeverDoubleBranch, branch_set |=> ~branch_set)
`ASSERT(NeverDoubleJump, jump_set |=> ~jump_set)
// Unless the first branch/jump was predicted holding branch_set/jump_set high for more than one
// cycle may not cause a functional issue but could generate needless prefetch buffer flushes and
// instruction fetches. ID/EX is designed such that this shouldn't ever happen for non-predicted
// branches.
`ASSERT(NeverDoubleBranch, branch_set & ~instr_bp_taken_i |=> ~branch_set)
`ASSERT(NeverDoubleJump, jump_set & ~instr_bp_taken_i |=> ~jump_set)
///////////////
// ID-EX FSM //
@ -722,6 +731,7 @@ module ibex_id_stage #(
stall_alu = 1'b0;
branch_set_d = 1'b0;
branch_spec = 1'b0;
branch_not_set = 1'b0;
jump_set = 1'b0;
perf_branch_o = 1'b0;
@ -757,6 +767,11 @@ module ibex_id_stage #(
MULTI_CYCLE : FIRST_CYCLE;
stall_branch = (~BranchTargetALU & branch_decision_i) | data_ind_timing_i;
branch_set_d = branch_decision_i | data_ind_timing_i;
if (BranchPredictor) begin
branch_not_set = ~branch_decision_i;
end
// Speculative branch (excludes branch_decision_i)
branch_spec = SpecBranch ? 1'b1 : branch_decision_i;
perf_branch_o = 1'b1;

View file

@ -18,7 +18,8 @@ module ibex_if_stage #(
parameter bit DummyInstructions = 1'b0,
parameter bit ICache = 1'b0,
parameter bit ICacheECC = 1'b0,
parameter bit SecureIbex = 1'b0
parameter bit SecureIbex = 1'b0,
parameter bit BranchPredictor = 1'b0
) (
input logic clk_i,
input logic rst_ni,
@ -46,6 +47,8 @@ module ibex_if_stage #(
// instr_is_compressed_id_o = 1'b1
output logic instr_is_compressed_id_o, // compressed decoder thinks this
// is a compressed instr
output logic instr_bp_taken_o, // instruction was predicted to be
// a taken branch
output logic instr_fetch_err_o, // bus error on fetch
output logic instr_fetch_err_plus2_o, // bus error misaligned
output logic illegal_c_insn_id_o, // compressed decoder thinks this
@ -59,6 +62,8 @@ module ibex_if_stage #(
input logic pc_set_i, // set the PC to a new value
input logic pc_set_spec_i,
input ibex_pkg::pc_sel_e pc_mux_i, // selector for PC multiplexer
input logic nt_branch_mispredict_i, // Not-taken branch in ID/EX was
// mispredicted (predicted taken)
input ibex_pkg::exc_pc_sel_e exc_pc_mux_i, // selects ISR address
input ibex_pkg::exc_cause_e exc_cause, // selects ISR address for
// vectorized interrupt lines
@ -96,6 +101,8 @@ module ibex_if_stage #(
// prefetch buffer related signals
logic prefetch_busy;
logic branch_req;
logic branch_spec;
logic predicted_branch;
logic [31:0] fetch_addr_n;
logic fetch_valid;
@ -105,6 +112,11 @@ module ibex_if_stage #(
logic fetch_err;
logic fetch_err_plus2;
logic if_instr_valid;
logic [31:0] if_instr_rdata;
logic [31:0] if_instr_addr;
logic if_instr_err;
logic [31:0] exc_pc;
logic [5:0] irq_id;
@ -113,13 +125,18 @@ module ibex_if_stage #(
logic if_id_pipe_reg_we; // IF-ID pipeline reg write enable
// Dummy instruction signals
logic fetch_valid_out;
//logic fetch_valid_out;
logic stall_dummy_instr;
logic [31:0] instr_out;
logic instr_is_compressed_out;
logic illegal_c_instr_out;
logic instr_err_out;
logic predict_branch_taken;
logic [31:0] predict_branch_pc;
ibex_pkg::pc_sel_e pc_mux_internal;
logic [7:0] unused_boot_addr;
logic [7:0] unused_csr_mtvec;
@ -141,14 +158,24 @@ module ibex_if_stage #(
endcase
end
// The Branch predictor can provide a new PC which is internal to if_stage. Only override the mux
// select to choose this if the core isn't already trying to set a PC.
assign pc_mux_internal =
(BranchPredictor && predict_branch_taken && !pc_set_i) ? PC_BP : pc_mux_i;
// fetch address selection mux
always_comb begin : fetch_addr_mux
unique case (pc_mux_i)
unique case (pc_mux_internal)
PC_BOOT: fetch_addr_n = { boot_addr_i[31:8], 8'h80 };
PC_JUMP: fetch_addr_n = branch_target_ex_i;
PC_EXC: fetch_addr_n = exc_pc; // set PC to exception handler
PC_ERET: fetch_addr_n = csr_mepc_i; // restore PC when returning from EXC
PC_DRET: fetch_addr_n = csr_depc_i;
// Without branch predictor will never get pc_mux_internal == PC_BP, still handle no branch
// predictor case here to ensure redundant mux logic isn't synthesised.
PC_BP: begin
fetch_addr_n = BranchPredictor ? predict_branch_pc : { boot_addr_i[31:8], 8'h80 };
end
default: fetch_addr_n = { boot_addr_i[31:8], 8'h80 };
endcase
end
@ -167,7 +194,7 @@ module ibex_if_stage #(
.req_i ( req_i ),
.branch_i ( branch_req ),
.branch_spec_i ( pc_set_spec_i ),
.branch_spec_i ( branch_spec ),
.addr_i ( {fetch_addr_n[31:1], 1'b0} ),
.ready_i ( fetch_ready ),
@ -191,32 +218,36 @@ module ibex_if_stage #(
);
end else begin : gen_prefetch_buffer
// prefetch buffer, caches a fixed number of instructions
ibex_prefetch_buffer prefetch_buffer_i (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
ibex_prefetch_buffer #(
.BranchPredictor (BranchPredictor)
) prefetch_buffer_i (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.req_i ( req_i ),
.req_i ( req_i ),
.branch_i ( branch_req ),
.branch_spec_i ( pc_set_spec_i ),
.addr_i ( {fetch_addr_n[31:1], 1'b0} ),
.branch_i ( branch_req ),
.branch_spec_i ( branch_spec ),
.predicted_branch_i ( predicted_branch ),
.branch_mispredict_i ( nt_branch_mispredict_i ),
.addr_i ( {fetch_addr_n[31:1], 1'b0} ),
.ready_i ( fetch_ready ),
.valid_o ( fetch_valid ),
.rdata_o ( fetch_rdata ),
.addr_o ( fetch_addr ),
.err_o ( fetch_err ),
.err_plus2_o ( fetch_err_plus2 ),
.ready_i ( fetch_ready ),
.valid_o ( fetch_valid ),
.rdata_o ( fetch_rdata ),
.addr_o ( fetch_addr ),
.err_o ( fetch_err ),
.err_plus2_o ( fetch_err_plus2 ),
.instr_req_o ( instr_req_o ),
.instr_addr_o ( instr_addr_o ),
.instr_gnt_i ( instr_gnt_i ),
.instr_rvalid_i ( instr_rvalid_i ),
.instr_rdata_i ( instr_rdata_i ),
.instr_err_i ( instr_err_i ),
.instr_pmp_err_i ( instr_pmp_err_i ),
.instr_req_o ( instr_req_o ),
.instr_addr_o ( instr_addr_o ),
.instr_gnt_i ( instr_gnt_i ),
.instr_rvalid_i ( instr_rvalid_i ),
.instr_rdata_i ( instr_rdata_i ),
.instr_err_i ( instr_err_i ),
.instr_pmp_err_i ( instr_pmp_err_i ),
.busy_o ( prefetch_busy )
.busy_o ( prefetch_busy )
);
// ICache tieoffs
logic unused_icen, unused_icinv;
@ -224,10 +255,16 @@ module ibex_if_stage #(
assign unused_icinv = icache_inval_i;
end
assign branch_req = pc_set_i;
assign fetch_ready = id_in_ready_i & ~stall_dummy_instr;
// For predicted branches only set branch_req when the ID/EX stage is ready to accept the branch
// instruction. Otherwise the branch instruction ends up getting flush out of the IF stage by the
// branch_req and is lost. Whilst it is possible to begin fetching the predicted branch without
// flushing the branch instruction from IF this adds design complexity and for situations where
// ID/EX stage stalls are common more timely fetching of branches is likely to have limited
// performance impact.
assign branch_req = pc_set_i | predict_branch_taken;
assign branch_spec = pc_set_spec_i | predict_branch_taken;
assign pc_if_o = fetch_addr;
assign pc_if_o = if_instr_addr;
assign if_busy_o = prefetch_busy;
// compressed instruction decoding, or more precisely compressed instruction
@ -243,7 +280,7 @@ module ibex_if_stage #(
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( fetch_valid & ~fetch_err ),
.instr_i ( fetch_rdata ),
.instr_i ( if_instr_rdata ),
.instr_o ( instr_decompressed ),
.is_compressed_o ( instr_is_compressed ),
.illegal_instr_o ( illegal_c_insn )
@ -268,11 +305,11 @@ module ibex_if_stage #(
);
// Mux between actual instructions and dummy instructions
assign fetch_valid_out = insert_dummy_instr | fetch_valid;
//assign fetch_valid_out = insert_dummy_instr | if_instr_valid;
assign instr_out = insert_dummy_instr ? dummy_instr_data : instr_decompressed;
assign instr_is_compressed_out = insert_dummy_instr ? 1'b0 : instr_is_compressed;
assign illegal_c_instr_out = insert_dummy_instr ? 1'b0 : illegal_c_insn;
assign instr_err_out = insert_dummy_instr ? 1'b0 : fetch_err;
assign instr_err_out = insert_dummy_instr ? 1'b0 : if_instr_err;
// Stall the IF stage if we insert a dummy instruction. The dummy will execute between whatever
// is currently in the ID stage and whatever is valid from the prefetch buffer this cycle. The
@ -298,11 +335,11 @@ module ibex_if_stage #(
assign unused_dummy_mask = dummy_instr_mask_i;
assign unused_dummy_seed_en = dummy_instr_seed_en_i;
assign unused_dummy_seed = dummy_instr_seed_i;
assign fetch_valid_out = fetch_valid;
//assign fetch_valid_out = fetch_valid;
assign instr_out = instr_decompressed;
assign instr_is_compressed_out = instr_is_compressed;
assign illegal_c_instr_out = illegal_c_insn;
assign instr_err_out = fetch_err;
assign instr_err_out = if_instr_err;
assign stall_dummy_instr = 1'b0;
assign dummy_instr_id_o = 1'b0;
end
@ -310,9 +347,9 @@ module ibex_if_stage #(
// The ID stage becomes valid as soon as any instruction is registered in the ID stage flops.
// Note that the current instruction is squashed by the incoming pc_set_i signal.
// Valid is held until it is explicitly cleared (due to an instruction completing or an exception)
assign instr_valid_id_d = (fetch_valid_out & id_in_ready_i & ~pc_set_i) |
assign instr_valid_id_d = (if_instr_valid & id_in_ready_i & ~pc_set_i) |
(instr_valid_id_q & ~instr_valid_clear_i);
assign instr_new_id_d = fetch_valid_out & id_in_ready_i;
assign instr_new_id_d = if_instr_valid & id_in_ready_i;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
@ -338,7 +375,7 @@ module ibex_if_stage #(
instr_rdata_alu_id_o <= instr_out;
instr_fetch_err_o <= instr_err_out;
instr_fetch_err_plus2_o <= fetch_err_plus2;
instr_rdata_c_id_o <= fetch_rdata[15:0];
instr_rdata_c_id_o <= if_instr_rdata[15:0];
instr_is_compressed_id_o <= instr_is_compressed_out;
illegal_c_insn_id_o <= illegal_c_instr_out;
pc_id_o <= pc_if_o;
@ -371,18 +408,199 @@ module ibex_if_stage #(
assign pc_mismatch_alert_o = 1'b0;
end
if (BranchPredictor) begin : g_branch_predictor
logic [31:0] instr_skid_data_q;
logic [31:0] instr_skid_addr_q;
logic instr_skid_bp_taken_q;
logic instr_skid_valid_q, instr_skid_valid_d;
logic instr_skid_en;
logic instr_bp_taken_q, instr_bp_taken_d;
logic predict_branch_taken_raw;
// ID stages needs to know if branch was predicted taken so it can signal mispredicts
always_ff @(posedge clk_i) begin
if (if_id_pipe_reg_we) begin
instr_bp_taken_q <= instr_bp_taken_d;
end
end
// When branch prediction is enabled a skid buffer between the IF and ID/EX stage is introduced.
// If an instruction in IF is predicted to be a branch and ID/EX is not ready the instruction is
// moved to the skid buffer which becomes the output of the IF stage until the ID/EX stage
// accepts the instruction. The skid buffer is required as otherwise the ID/EX ready signal is
// coupled to the instr_req_o output which produces a feedthrough path from data_gnt_i ->
// instr_req_o (which needs to be avoided as for some interconnects this will result in
// a combinational loop).
assign instr_skid_en = predicted_branch & ~id_in_ready_i & ~instr_skid_valid_q;
assign instr_skid_valid_d = (instr_skid_valid_q & ~id_in_ready_i & ~stall_dummy_instr) | instr_skid_en;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
instr_skid_valid_q <= 1'b0;
end else begin
instr_skid_valid_q <= instr_skid_valid_d;
end
end
always_ff @(posedge clk_i) begin
if (instr_skid_en) begin
instr_skid_bp_taken_q <= predict_branch_taken;
instr_skid_data_q <= fetch_rdata;
instr_skid_addr_q <= fetch_addr;
end
end
ibex_branch_predict branch_predict_i (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.fetch_rdata_i ( fetch_rdata ),
.fetch_pc_i ( fetch_addr ),
.fetch_valid_i ( fetch_valid ),
.predict_branch_taken_o ( predict_branch_taken_raw ),
.predict_branch_pc_o ( predict_branch_pc )
);
// If there is an instruction in the skid buffer there must be no branch prediction.
// Instructions are only placed in the skid after they have been predicted to be a taken branch
// so with the skid valid any prediction has already occurred.
// Do not branch predict on instruction errors.
assign predict_branch_taken = predict_branch_taken_raw & ~instr_skid_valid_q & ~fetch_err;
// pc_set_i takes precendence over branch prediction
assign predicted_branch = predict_branch_taken & ~pc_set_i;
assign if_instr_valid = fetch_valid | instr_skid_valid_q;
assign if_instr_rdata = instr_skid_valid_q ? instr_skid_data_q : fetch_rdata;
assign if_instr_addr = instr_skid_valid_q ? instr_skid_addr_q : fetch_addr;
// Don't branch predict on instruction error so only instructions without errors end up in the
// skid buffer.
assign if_instr_err = ~instr_skid_valid_q & fetch_err;
assign instr_bp_taken_d = instr_skid_valid_q ? instr_skid_bp_taken_q : predict_branch_taken;
assign fetch_ready = id_in_ready_i & ~stall_dummy_instr & ~instr_skid_valid_q;
assign instr_bp_taken_o = instr_bp_taken_q;
`ASSERT(NoPredictSkid, instr_skid_valid_q |-> ~predict_branch_taken);
`ASSERT(NoPredictIllegal, predict_branch_taken |-> ~illegal_c_insn);
end else begin : g_no_branch_predictor
assign instr_bp_taken_o = 1'b0;
assign predict_branch_taken = 1'b0;
assign predicted_branch = 1'b0;
assign predict_branch_pc = 32'b0;
assign if_instr_valid = fetch_valid;
assign if_instr_rdata = fetch_rdata;
assign if_instr_addr = fetch_addr;
assign if_instr_err = fetch_err;
assign fetch_ready = id_in_ready_i & ~stall_dummy_instr;
end
////////////////
// Assertions //
////////////////
// Selectors must be known/valid.
`ASSERT_KNOWN(IbexExcPcMuxKnown, exc_pc_mux_i)
`ASSERT(IbexPcMuxValid, pc_mux_i inside {
PC_BOOT,
PC_JUMP,
PC_EXC,
PC_ERET,
PC_DRET})
if (BranchPredictor) begin : g_branch_predictor_asserts
`ASSERT_IF(IbexPcMuxValid, pc_mux_internal inside {
PC_BOOT,
PC_JUMP,
PC_EXC,
PC_ERET,
PC_DRET,
PC_BP},
pc_set_i)
`ifdef INC_ASSERT
/**
* Checks for branch prediction interface to fetch_fifo/icache
*
* The interface has two signals:
* - predicted_branch_i: When set with a branch (branch_i) indicates the branch is a predicted
* one, it should be ignored when a branch_i isn't set.
* - branch_mispredict_i: Indicates the previously predicted branch was mis-predicted and
* execution should resume with the not-taken side of the branch (i.e. continue with the PC
* that followed the predicted branch). This must be raised before the instruction that is
* made available following a predicted branch is accepted (Following a cycle with branch_i
* & predicted_branch_i, branch_mispredict_i can only be asserted before or on the same cycle
* as seeing fetch_valid & fetch_ready). When branch_mispredict_i is asserted, fetch_valid may
* be asserted in response. If fetch_valid is asserted on the same cycle as
* branch_mispredict_i this indicates the fetch_fifo/icache has the not-taken side of the
* branch immediately ready for use
*/
logic predicted_branch_live_q, predicted_branch_live_d;
logic [31:0] predicted_branch_nt_pc_q, predicted_branch_nt_pc_d;
logic [31:0] awaiting_instr_after_mispredict_q, awaiting_instr_after_mispredict_d;
logic [31:0] next_pc;
logic mispredicted, mispredicted_d, mispredicted_q;
assign next_pc = fetch_addr + (instr_is_compressed_out ? 32'd2 : 32'd4);
always_comb begin
predicted_branch_live_d = predicted_branch_live_q;
mispredicted_d = mispredicted_q;
if (branch_req & predicted_branch) begin
predicted_branch_live_d = 1'b1;
mispredicted_d = 1'b0;
end else if (predicted_branch_live_q) begin
if (fetch_valid & fetch_ready) begin
predicted_branch_live_d = 1'b0;
end else if (nt_branch_mispredict_i) begin
mispredicted_d = 1'b1;
end
end
end
always @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
predicted_branch_live_q <= 1'b0;
mispredicted_q <= 1'b0;
end else begin
predicted_branch_live_q <= predicted_branch_live_d;
mispredicted_q <= mispredicted_d;
end
end
always @(posedge clk_i) begin
if (branch_req & predicted_branch) begin
predicted_branch_nt_pc_q <= next_pc;
end
end
// Must only see mispredict after we've performed a predicted branch but before we've accepted
// any instruction (with fetch_ready & fetch_valid) that follows that predicted branch.
`ASSERT(MispredictOnlyImmediatelyAfterPredictedBranch,
nt_branch_mispredict_i |-> predicted_branch_live_q);
// Check that on mispredict we get the correct PC for the non-taken side of the branch when
// prefetch buffer/icache makes that PC available.
`ASSERT(CorrectPCOnMispredict,
predicted_branch_live_q & mispredicted_d & fetch_valid |-> fetch_addr == predicted_branch_nt_pc_q);
// Must not signal mispredict over multiple cycles but it's possible to have back to back
// mispredicts for different branches (core signals mispredict, prefetch buffer/icache immediate
// has not-taken side of the mispredicted branch ready, which itself is a predicted branch,
// following cycle core signal that that branch has mispredicted).
`ASSERT(MispredictSingleCycle,
nt_branch_mispredict_i & ~(fetch_valid & fetch_ready) |=> ~nt_branch_mispredict_i);
`endif
end else begin : g_no_branch_predictor_asserts
`ASSERT_IF(IbexPcMuxValid, pc_mux_internal inside {
PC_BOOT,
PC_JUMP,
PC_EXC,
PC_ERET,
PC_DRET},
pc_set_i)
end
// Boot address must be aligned to 256 bytes.
`ASSERT(IbexBootAddrUnaligned, boot_addr_i[7:0] == 8'h00)

View file

@ -252,7 +252,8 @@ typedef enum logic [2:0] {
PC_JUMP,
PC_EXC,
PC_ERET,
PC_DRET
PC_DRET,
PC_BP
} pc_sel_e;
// Exception PC mux selection

View file

@ -9,7 +9,9 @@
* Prefetch Buffer that caches instructions. This cuts overly long critical
* paths to the instruction cache.
*/
module ibex_prefetch_buffer (
module ibex_prefetch_buffer #(
parameter bit BranchPredictor = 1'b0
)(
input logic clk_i,
input logic rst_ni,
@ -17,6 +19,8 @@ module ibex_prefetch_buffer (
input logic branch_i,
input logic branch_spec_i,
input logic predicted_branch_i,
input logic branch_mispredict_i,
input logic [31:0] addr_i,
@ -57,20 +61,30 @@ module ibex_prefetch_buffer (
logic stored_addr_en;
logic [31:0] fetch_addr_d, fetch_addr_q;
logic fetch_addr_en;
logic [31:0] branch_mispredict_addr;
logic [31:0] instr_addr, instr_addr_w_aligned;
logic instr_or_pmp_err;
logic fifo_valid;
logic [31:0] fifo_addr;
logic fifo_ready;
logic fifo_clear;
logic [NUM_REQS-1:0] fifo_busy;
logic valid_raw;
logic [31:0] addr_next;
logic branch_or_mispredict;
////////////////////////////
// Prefetch buffer status //
////////////////////////////
assign busy_o = (|rdata_outstanding_q) | instr_req_o;
assign branch_or_mispredict = branch_i | branch_mispredict_i;
//////////////////////////////////////////////
// Fetch fifo - consumes addresses and data //
//////////////////////////////////////////////
@ -82,7 +96,7 @@ module ibex_prefetch_buffer (
// A branch will invalidate any previously fetched instructions.
// Note that the FENCE.I instruction relies on this flushing behaviour on branch. If it is
// altered the FENCE.I implementation may require changes.
assign fifo_clear = branch_i;
assign fifo_clear = branch_or_mispredict;
// Reversed version of rdata_outstanding_q which can be overlaid with fifo fill state
for (genvar i = 0; i < NUM_REQS; i++) begin : gen_rd_rev
@ -104,14 +118,15 @@ module ibex_prefetch_buffer (
.busy_o ( fifo_busy ),
.in_valid_i ( fifo_valid ),
.in_addr_i ( addr_i ),
.in_addr_i ( fifo_addr ),
.in_rdata_i ( instr_rdata_i ),
.in_err_i ( instr_or_pmp_err ),
.out_valid_o ( valid_o ),
.out_valid_o ( valid_raw ),
.out_ready_i ( ready_i ),
.out_rdata_o ( rdata_o ),
.out_addr_o ( addr_o ),
.out_addr_next_o ( addr_next ),
.out_err_o ( err_o ),
.out_err_plus2_o ( err_plus2_o )
);
@ -124,7 +139,7 @@ module ibex_prefetch_buffer (
assign branch_suppress = branch_spec_i & ~branch_i;
// Make a new request any time there is space in the FIFO, and space in the request queue
assign valid_new_req = ~branch_suppress & req_i & (fifo_ready | branch_i) &
assign valid_new_req = ~branch_suppress & req_i & (fifo_ready | branch_or_mispredict) &
~rdata_outstanding_q[NUM_REQS-1];
assign valid_req = valid_req_q | valid_new_req;
@ -141,7 +156,7 @@ module ibex_prefetch_buffer (
assign valid_req_d = valid_req & ~gnt_or_pmp_err;
// Record whether an outstanding bus request is cancelled by a branch
assign discard_req_d = valid_req_q & (branch_i | discard_req_q);
assign discard_req_d = valid_req_q & (branch_or_mispredict | discard_req_q);
////////////////
// Fetch addr //
@ -172,13 +187,40 @@ module ibex_prefetch_buffer (
end
end
if (BranchPredictor) begin : g_branch_predictor
// Where the branch predictor is present record what address followed a predicted branch. If
// that branch is predicted taken but mispredicted (so not-taken) this is used to resume on
// the not-taken code path.
logic [31:0] branch_mispredict_addr_q;
logic branch_mispredict_addr_en;
assign branch_mispredict_addr_en = branch_i & predicted_branch_i;
always_ff @(posedge clk_i) begin
if (branch_mispredict_addr_en) begin
branch_mispredict_addr_q <= addr_next;
end
end
assign branch_mispredict_addr = branch_mispredict_addr_q;
end else begin : g_no_branch_predictor
logic unused_predicted_branch;
logic [31:0] unused_addr_next;
assign unused_predicted_branch = predicted_branch_i;
assign unused_addr_next = addr_next;
assign branch_mispredict_addr = '0;
end
// 2. fetch_addr_q
// Update on a branch or as soon as a request is issued
assign fetch_addr_en = branch_i | (valid_new_req & ~valid_req_q);
assign fetch_addr_en = branch_or_mispredict | (valid_new_req & ~valid_req_q);
assign fetch_addr_d = (branch_i ? addr_i :
{fetch_addr_q[31:2], 2'b00}) +
assign fetch_addr_d = (branch_i ? addr_i :
branch_mispredict_i ? {branch_mispredict_addr[31:2], 2'b00} :
{fetch_addr_q[31:2], 2'b00}) +
// Current address + 4
{{29{1'b0}},(valid_new_req & ~valid_req_q),2'b00};
@ -189,9 +231,10 @@ module ibex_prefetch_buffer (
end
// Address mux
assign instr_addr = valid_req_q ? stored_addr_q :
branch_spec_i ? addr_i :
fetch_addr_q;
assign instr_addr = valid_req_q ? stored_addr_q :
branch_spec_i ? addr_i :
branch_mispredict_i ? branch_mispredict_addr :
fetch_addr_q;
assign instr_addr_w_aligned = {instr_addr[31:2], 2'b00};
@ -209,7 +252,7 @@ module ibex_prefetch_buffer (
// If a branch is received at any point while a request is outstanding, it must be tracked
// to ensure we discard the data once received
assign branch_discard_n[i] = (valid_req & gnt_or_pmp_err & discard_req_d) |
(branch_i & rdata_outstanding_q[i]) | branch_discard_q[i];
(branch_or_mispredict & rdata_outstanding_q[i]) | branch_discard_q[i];
// Record whether this request received a PMP error
assign rdata_pmp_err_n[i] = (valid_req & ~rdata_outstanding_q[i] & instr_pmp_err_i) |
rdata_pmp_err_q[i];
@ -223,7 +266,7 @@ module ibex_prefetch_buffer (
rdata_outstanding_q[i];
assign branch_discard_n[i] = (valid_req & gnt_or_pmp_err & discard_req_d &
rdata_outstanding_q[i-1]) |
(branch_i & rdata_outstanding_q[i]) | branch_discard_q[i];
(branch_or_mispredict & rdata_outstanding_q[i]) | branch_discard_q[i];
assign rdata_pmp_err_n[i] = (valid_req & ~rdata_outstanding_q[i] & instr_pmp_err_i &
rdata_outstanding_q[i-1]) |
rdata_pmp_err_q[i];
@ -241,6 +284,8 @@ module ibex_prefetch_buffer (
// Push a new entry to the FIFO once complete (and not cancelled by a branch)
assign fifo_valid = rvalid_or_pmp_err & ~branch_discard_q[0];
assign fifo_addr = branch_mispredict_i ? branch_mispredict_addr : addr_i;
///////////////
// Registers //
///////////////
@ -268,4 +313,6 @@ module ibex_prefetch_buffer (
assign instr_req_o = valid_req;
assign instr_addr_o = instr_addr_w_aligned;
assign valid_o = valid_raw & ~branch_mispredict_i;
endmodule