remove branch predictor (#49)

* remove parameter BranchPredictor

Signed-off-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>

* Remove references to the removed parameter(s) from examples

Signed-off-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>

* remove references to the removed parameters from compliance verification

Signed-off-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>

* remove references to the removed parameters from core lists

Signed-off-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>

* remove references to the removed parameters from the example configurations

Signed-off-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>

* Remove references to the removed parameter from documentation

Signed-off-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>

* Remove related and dead code

Signed-off-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>

---------

Signed-off-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>
Co-authored-by: Szymon Bieganski <szymon.bieganski@oss.nxp.com>
This commit is contained in:
christian-herber-nxp 2023-07-20 16:40:10 +02:00 committed by GitHub
parent 7836daeb14
commit 066ff47261
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 24 additions and 364 deletions

View file

@ -38,7 +38,6 @@ ${DESIGN_RTL_DIR}/cve2_tracer_pkg.sv
${DESIGN_RTL_DIR}/../vendor/lowrisc_ip/ip/prim/rtl/prim_secded_pkg.sv
${DESIGN_RTL_DIR}/../vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_pkg.sv
${DESIGN_RTL_DIR}/cve2_alu.sv
${DESIGN_RTL_DIR}/cve2_branch_predict.sv
${DESIGN_RTL_DIR}/cve2_compressed_decoder.sv
${DESIGN_RTL_DIR}/cve2_controller.sv
${DESIGN_RTL_DIR}/cve2_cs_registers.sv

View file

@ -13,7 +13,6 @@ small:
RV32B : "cve2_pkg::RV32BNone"
RegFile : "cve2_pkg::RegFileFF"
WritebackStage : 0
BranchPredictor : 0
PMPEnable : 0
PMPGranularity : 0
PMPNumRegions : 4
@ -25,7 +24,6 @@ opentitan:
RV32B : "cve2_pkg::RV32BOTEarlGrey"
RegFile : "cve2_pkg::RegFileFF"
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 1
PMPGranularity : 0
PMPNumRegions : 16
@ -43,7 +41,6 @@ experimental-maxperf:
RV32B : "cve2_pkg::RV32BNone"
RegFile : "cve2_pkg::RegFileFF"
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 0
PMPGranularity : 0
PMPNumRegions : 4
@ -55,7 +52,6 @@ experimental-maxperf-pmp:
RV32B : "cve2_pkg::RV32BNone"
RegFile : "cve2_pkg::RegFileFF"
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 1
PMPGranularity : 0
PMPNumRegions : 16
@ -67,7 +63,6 @@ experimental-maxperf-pmp-bmbalanced:
RV32B : "cve2_pkg::RV32BBalanced"
RegFile : "cve2_pkg::RegFileFF"
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 1
PMPGranularity : 0
PMPNumRegions : 16
@ -79,7 +74,6 @@ experimental-maxperf-pmp-bmfull:
RV32B : "cve2_pkg::RV32BFull"
RegFile : "cve2_pkg::RegFileFF"
WritebackStage : 1
BranchPredictor : 0
PMPEnable : 1
PMPGranularity : 0
PMPNumRegions : 16
@ -94,7 +88,6 @@ experimental-branch-predictor:
RV32B : "cve2_pkg::RV32BNone"
RegFile : "cve2_pkg::RegFileFF"
WritebackStage : 1
BranchPredictor : 1
PMPEnable : 0
PMPGranularity : 0
PMPNumRegions : 4

View file

@ -68,7 +68,6 @@ parameters:
paramtype: vlogdefine
description: "Bitmanip implementation parameter enum. See the cve2_pkg::rv32b_e enum in cve2_pkg.sv for permitted values."
targets:
default: &default_target
filesets:

View file

@ -65,12 +65,6 @@ parameters:
paramtype: vlogparam
description: "Enables third pipeline stage (EXPERIMENTAL) [0/1]"
BranchPredictor:
datatype: int
paramtype: vlogparam
default: 0
description: "Enables static branch prediction (EXPERIMENTAL)"
SecureCVE2:
datatype: int
default: 0
@ -121,7 +115,6 @@ targets:
- ICache
- ICacheECC
- WritebackStage
- BranchPredictor
- SecureCVE2
- ICacheScramble
- PMPEnable

View file

@ -18,7 +18,6 @@ Instantiation Template
.MHPMCounterWidth ( 40 ),
.RV32E ( 0 ),
.RV32M ( cve2_pkg::RV32MFast ),
.BranchPrediction ( 0 ),
.RndCnstLfsrSeed ( cve2_pkg::RndCnstLfsrSeedDefault ),
.RndCnstLfsrPerm ( cve2_pkg::RndCnstLfsrPermDefault ),
.DmHaltAddr ( 32'h1A110800 ),
@ -94,8 +93,6 @@ Parameters
| | | | "cve2_pkg::RV32MFast": 3-4 cycle multiplier, iterative divider |
| | | | "cve2_pkg::RV32MSingleCycle": 1-2 cycle multiplier, iterative divider |
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
| ``BranchPrediction`` | bit | 0 | *EXPERIMENTAL* Enable Static branch prediction |
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
| ``DmHaltAddr`` | int | 0x1A110800 | Address to jump to when entering Debug Mode |
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
| ``DmExceptionAddr`` | int | 0x1A110808 | Address to jump to when an exception occurs while in Debug Mode |

View file

@ -6,9 +6,6 @@ Coverage Plan
.. note::
Work to implement the functional coverage described in this plan is on-going and the plan itself is not yet complete.
.. todo::
Branch prediction hasn't yet been considered, this will add more coverage points and alter some others
Introduction
------------
Ibex functional coverage is split into two major categories:

View file

@ -21,16 +21,6 @@ A localparam ``DEPTH`` gives a configurable depth which is set to 3 by default.
The top-level of the instruction fetch controls the prefetch buffer (in particular flushing it on branches/jumps/exception and beginning prefetching from the appropriate new PC) and supplies new instructions to the ID/EX stage along with their PC.
Compressed instructions are expanded by the IF stage so the decoder can always deal with uncompressed instructions (the ID stage still receives the compressed instruction for placing into ``mtval`` on an illegal instruction exception).
Branch Prediction
-----------------
Ibex can be configured to use static branch prediction by setting the ``BranchPrediction`` parameter to 1.
This improves performance by predicting that any branch with a negative offset is taken and that any branch with a positive offset is not.
When successful, the prediction removes a stall cycle from a taken branch.
However, there is a mis-predict penalty if a branch is wrongly predicted to be taken.
This penalty is at least one cycle, or at least two cycles if the instruction following the branch is uncompressed and not aligned.
This feature is *EXPERIMENTAL* and its effects are not yet fully documented.
Instruction-Side Memory Interface
---------------------------------

View file

@ -59,12 +59,6 @@ parameters:
paramtype: vlogparam
description: "Enable ECC protection in instruction cache"
BranchPredictor:
datatype: int
paramtype: vlogparam
default: 0
description: "Enables static branch prediction (EXPERIMENTAL)"
PMPEnable:
datatype: int
default: 0
@ -108,7 +102,6 @@ targets:
- RegFile
- ICache
- ICacheECC
- BranchPredictor
- PMPEnable
- PMPGranularity
- PMPNumRegions

View file

@ -24,7 +24,6 @@ module cve2_riscv_compliance (
parameter cve2_pkg::regfile_e RegFile = cve2_pkg::RegFileFF;
parameter bit ICache = 1'b0;
parameter bit ICacheECC = 1'b0;
parameter bit BranchPredictor = 1'b0;
parameter bit SecureIbex = 1'b0;
parameter bit ICacheScramble = 1'b0;
@ -121,7 +120,6 @@ module cve2_riscv_compliance (
.RegFile (RegFile ),
.ICache (ICache ),
.ICacheECC (ICacheECC ),
.BranchPredictor (BranchPredictor ),
.SecureIbex (SecureIbex ),
.ICacheScramble (ICacheScramble ),
.DmHaltAddr (32'h00000000 ),

View file

@ -66,12 +66,6 @@ parameters:
paramtype: vlogparam
description: "Enables ICache scrambling feature (EXPERIMENTAL) [0/1]"
BranchPredictor:
datatype: int
paramtype: vlogparam
default: 0
description: "Enables static branch prediction (EXPERIMENTAL)"
PMPEnable:
datatype: int
default: 0
@ -104,7 +98,6 @@ targets:
- ICacheScramble
- ICacheECC
- SecureIbex
- BranchPredictor
- PMPEnable
- PMPGranularity
- PMPNumRegions

View file

@ -46,7 +46,6 @@ module cve2_simple_system (
parameter cve2_pkg::regfile_e RegFile = `RegFile;
parameter bit ICache = 1'b0;
parameter bit ICacheECC = 1'b0;
parameter bit BranchPredictor = 1'b0;
parameter SRAMInitFile = "";
logic clk_sys = 1'b0, rst_sys_n;
@ -173,7 +172,6 @@ module cve2_simple_system (
.RegFile ( RegFile ),
.ICache ( ICache ),
.ICacheECC ( ICacheECC ),
.BranchPredictor ( BranchPredictor ),
.DmHaltAddr ( 32'h00100000 ),
.DmExceptionAddr ( 32'h00100000 )
) u_top (

View file

@ -11,7 +11,6 @@
`include "dv_fcov_macros.svh"
module cve2_controller #(
parameter bit BranchPredictor = 0
) (
input logic clk_i,
input logic rst_ni,
@ -33,7 +32,6 @@ module cve2_controller #(
input logic [31:0] instr_i, // uncompressed instr 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
@ -49,8 +47,6 @@ module cve2_controller #(
output logic pc_set_o, // jump to address set by pc_mux
output cve2_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 cve2_pkg::exc_pc_sel_e exc_pc_mux_o, // IF stage selector for exception PC
output cve2_pkg::exc_cause_e exc_cause_o, // for IF stage, CSRs
@ -62,7 +58,6 @@ module cve2_controller #(
// jump/branch signals
input logic branch_set_i, // branch set signal (branch definitely
// taken)
input logic branch_not_set_i, // branch is definitely not taken
input logic jump_set_i, // jump taken set signal
// interrupt signals
@ -338,7 +333,6 @@ module cve2_controller #(
// helping timing.
pc_mux_o = PC_BOOT;
pc_set_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
@ -458,21 +452,12 @@ module cve2_controller #(
end
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;
pc_set_o = 1'b1;
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
// If entering debug mode or handling an IRQ the core needs to wait until any instruction in
// ID has finished executing. Stall IF during that time.
if ((enter_debug_mode || handle_irq) && (stall || instr_valid_i)) begin
@ -776,8 +761,6 @@ module cve2_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

@ -21,7 +21,6 @@ module cve2_core import cve2_pkg::*; #(
parameter bit RV32E = 1'b0,
parameter rv32m_e RV32M = RV32MFast,
parameter rv32b_e RV32B = RV32BNone,
parameter bit BranchPredictor = 1'b0,
parameter bit DbgTriggerEn = 1'b0,
parameter int unsigned DbgHwBreakNum = 1,
parameter int unsigned DmHaltAddr = 32'h1A110800,
@ -119,7 +118,6 @@ module cve2_core import cve2_pkg::*; #(
logic [15:0] instr_rdata_c_id; // Compressed instruction sampled inside IF stage
logic instr_is_compressed_id;
logic instr_perf_count_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
@ -132,8 +130,6 @@ module cve2_core import cve2_pkg::*; #(
logic instr_first_cycle_id;
logic instr_valid_clear;
logic pc_set;
logic nt_branch_mispredict;
logic [31:0] nt_branch_addr;
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
@ -290,8 +286,7 @@ module cve2_core import cve2_pkg::*; #(
cve2_if_stage #(
.DmHaltAddr (DmHaltAddr),
.DmExceptionAddr (DmExceptionAddr),
.BranchPredictor (BranchPredictor)
.DmExceptionAddr (DmExceptionAddr)
) if_stage_i (
.clk_i (clk_i),
.rst_ni(rst_ni),
@ -314,7 +309,6 @@ module cve2_core import cve2_pkg::*; #(
.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),
@ -327,13 +321,11 @@ module cve2_core import cve2_pkg::*; #(
.instr_valid_clear_i (instr_valid_clear),
.pc_set_i (pc_set),
.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),
// branch targets
.branch_target_ex_i(branch_target_ex),
.nt_branch_addr_i (nt_branch_addr),
// CSRs
.csr_mepc_i (csr_mepc), // exception return address
@ -361,8 +353,7 @@ module cve2_core import cve2_pkg::*; #(
cve2_id_stage #(
.RV32E (RV32E),
.RV32M (RV32M),
.RV32B (RV32B),
.BranchPredictor(BranchPredictor)
.RV32B (RV32B)
) id_stage_i (
.clk_i (clk_i),
.rst_ni(rst_ni),
@ -378,7 +369,6 @@ module cve2_core import cve2_pkg::*; #(
.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),
@ -390,8 +380,6 @@ module cve2_core import cve2_pkg::*; #(
.instr_req_o (instr_req_int),
.pc_set_o (pc_set),
.pc_mux_o (pc_mux_id),
.nt_branch_mispredict_o(nt_branch_mispredict),
.nt_branch_addr_o (nt_branch_addr),
.exc_pc_mux_o (exc_pc_mux_id),
.exc_cause_o (exc_cause),

View file

@ -20,8 +20,7 @@
module cve2_id_stage #(
parameter bit RV32E = 0,
parameter cve2_pkg::rv32m_e RV32M = cve2_pkg::RV32MFast,
parameter cve2_pkg::rv32b_e RV32B = cve2_pkg::RV32BNone,
parameter bit BranchPredictor = 0
parameter cve2_pkg::rv32b_e RV32B = cve2_pkg::RV32BNone
) (
input logic clk_i,
input logic rst_ni,
@ -36,7 +35,6 @@ module cve2_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
@ -48,8 +46,6 @@ module cve2_id_stage #(
// IF and ID stage signals
output logic pc_set_o,
output cve2_pkg::pc_sel_e pc_mux_o,
output logic nt_branch_mispredict_o,
output logic [31:0] nt_branch_addr_o,
output cve2_pkg::exc_pc_sel_e exc_pc_mux_o,
output cve2_pkg::exc_cause_e exc_cause_o,
@ -170,7 +166,6 @@ module cve2_id_stage #(
logic branch_in_dec;
logic branch_set, branch_set_raw, branch_set_raw_d;
logic branch_jump_set_done_q, branch_jump_set_done_d;
logic branch_not_set;
logic jump_in_dec;
logic jump_set_dec;
logic jump_set, jump_set_raw;
@ -450,7 +445,6 @@ module cve2_id_stage #(
assign illegal_insn_o = instr_valid_i & (illegal_insn_dec | illegal_csr_insn_i);
cve2_controller #(
.BranchPredictor(BranchPredictor)
) controller_i (
.clk_i (clk_i),
.rst_ni(rst_ni),
@ -472,7 +466,6 @@ module cve2_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),
@ -486,7 +479,6 @@ module cve2_id_stage #(
.instr_req_o (instr_req_o),
.pc_set_o (pc_set_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),
@ -496,7 +488,6 @@ module cve2_id_stage #(
.store_err_i (lsu_store_err_i),
// jump/branch control
.branch_set_i (branch_set),
.branch_not_set_i (branch_not_set),
.jump_set_i (jump_set),
// interrupt signals
@ -608,22 +599,6 @@ module cve2_id_stage #(
assign jump_set = jump_set_raw & ~branch_jump_set_done_q;
assign branch_set = branch_set_raw & ~branch_jump_set_done_q;
// Holding branch_set/jump_set high for more than one cycle should not cause a functional issue.
// However it could generate needless prefetch buffer flushes and instruction fetches. The ID/EX
// designs ensures that this never happens for non-predicted branches.
`ASSERT(NeverDoubleBranch, branch_set & ~instr_bp_taken_i |=> ~branch_set)
`ASSERT(NeverDoubleJump, jump_set & ~instr_bp_taken_i |=> ~jump_set)
//////////////////////////////
// Branch not-taken address //
//////////////////////////////
if (BranchPredictor) begin : g_calc_nt_addr
assign nt_branch_addr_o = pc_id_i + (instr_is_compressed_i ? 32'd2 : 32'd4);
end else begin : g_n_calc_nt_addr
assign nt_branch_addr_o = 32'd0;
end
///////////////
// ID-EX FSM //
///////////////
@ -652,7 +627,6 @@ module cve2_id_stage #(
stall_branch = 1'b0;
stall_alu = 1'b0;
branch_set_raw_d = 1'b0;
branch_not_set = 1'b0;
jump_set_raw = 1'b0;
perf_branch_o = 1'b0;
@ -686,10 +660,6 @@ module cve2_id_stage #(
stall_branch = branch_decision_i;
branch_set_raw_d = branch_decision_i;
if (BranchPredictor) begin
branch_not_set = 1'b1;
end
perf_branch_o = 1'b1;
end
jump_in_dec: begin

View file

@ -14,8 +14,7 @@
module cve2_if_stage import cve2_pkg::*; #(
parameter int unsigned DmHaltAddr = 32'h1A110800,
parameter int unsigned DmExceptionAddr = 32'h1A110808,
parameter bit BranchPredictor = 1'b0
parameter int unsigned DmExceptionAddr = 32'h1A110808
) (
input logic clk_i,
input logic rst_ni,
@ -42,8 +41,6 @@ module cve2_if_stage import cve2_pkg::*; #(
// 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
@ -57,9 +54,6 @@ module cve2_if_stage import cve2_pkg::*; #(
input logic instr_valid_clear_i, // clear instr valid bit in IF-ID
input logic pc_set_i, // set the PC to a new value
input 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 logic [31:0] nt_branch_addr_i, // Not-taken branch address in ID/EX
input exc_pc_sel_e exc_pc_mux_i, // selects ISR address
input exc_cause_e exc_cause, // selects ISR address for
// vectorized interrupt lines
@ -101,10 +95,6 @@ module cve2_if_stage import cve2_pkg::*; #(
logic illegal_c_insn;
logic instr_is_compressed;
logic if_instr_valid;
logic [31:0] if_instr_rdata;
logic [31:0] if_instr_addr;
logic if_instr_bus_err;
logic if_instr_pmp_err;
logic if_instr_err;
logic if_instr_err_plus2;
@ -116,9 +106,6 @@ module cve2_if_stage import cve2_pkg::*; #(
logic if_id_pipe_reg_we; // IF-ID pipeline reg write enable
logic predict_branch_taken;
logic [31:0] predict_branch_pc;
cve2_pkg::pc_sel_e pc_mux_internal;
logic [7:0] unused_boot_addr;
@ -142,10 +129,8 @@ module cve2_if_stage import cve2_pkg::*; #(
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;
pc_mux_i;
// fetch address selection mux
always_comb begin : fetch_addr_mux
@ -155,9 +140,6 @@ module cve2_if_stage import cve2_pkg::*; #(
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. We still handle no branch
// predictor case here to ensure redundant mux logic isn't synthesised.
PC_BP: fetch_addr_n = BranchPredictor ? predict_branch_pc : { boot_addr_i[31:8], 8'h00 };
default: fetch_addr_n = { boot_addr_i[31:8], 8'h00 };
endcase
end
@ -174,8 +156,6 @@ module cve2_if_stage import cve2_pkg::*; #(
.req_i ( req_i ),
.branch_i ( branch_req ),
.branch_mispredict_i ( nt_branch_mispredict_i ),
.mispredict_addr_i ( nt_branch_addr_i ),
.addr_i ( {fetch_addr_n[31:1], 1'b0} ),
.ready_i ( fetch_ready ),
@ -197,22 +177,22 @@ module cve2_if_stage import cve2_pkg::*; #(
assign unused_fetch_addr_n0 = fetch_addr_n[0];
assign branch_req = pc_set_i | predict_branch_taken;
assign branch_req = pc_set_i;
assign pc_if_o = if_instr_addr;
assign pc_if_o = fetch_addr;
assign if_busy_o = prefetch_busy;
// PMP errors
// An error can come from the instruction address, or the next instruction address for unaligned,
// uncompressed instructions.
assign if_instr_pmp_err = pmp_err_if_i |
(if_instr_addr[2] & ~instr_is_compressed & pmp_err_if_plus2_i);
(fetch_addr[2] & ~instr_is_compressed & pmp_err_if_plus2_i);
// Combine bus errors and pmp errors
assign if_instr_err = if_instr_bus_err | if_instr_pmp_err;
assign if_instr_err = fetch_err | if_instr_pmp_err;
// Capture the second half of the address for errors on the second part of an instruction
assign if_instr_err_plus2 = ((if_instr_addr[2] & ~instr_is_compressed & pmp_err_if_plus2_i) |
assign if_instr_err_plus2 = ((fetch_addr[2] & ~instr_is_compressed & pmp_err_if_plus2_i) |
fetch_err_plus2) & ~pmp_err_if_i;
// compressed instruction decoding, or more precisely compressed instruction
@ -224,7 +204,7 @@ module cve2_if_stage import cve2_pkg::*; #(
.clk_i (clk_i),
.rst_ni (rst_ni),
.valid_i (fetch_valid & ~fetch_err),
.instr_i (if_instr_rdata),
.instr_i (fetch_rdata),
.instr_o (instr_decompressed),
.is_compressed_o(instr_is_compressed),
.illegal_instr_o(illegal_c_insn)
@ -233,9 +213,9 @@ module cve2_if_stage import cve2_pkg::*; #(
// 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 = (if_instr_valid & id_in_ready_i & ~pc_set_i) |
assign instr_valid_id_d = (fetch_valid & id_in_ready_i & ~pc_set_i) |
(instr_valid_id_q & ~instr_valid_clear_i);
assign instr_new_id_d = if_instr_valid & id_in_ready_i;
assign instr_new_id_d = fetch_valid & id_in_ready_i;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
@ -270,110 +250,14 @@ module cve2_if_stage import cve2_pkg::*; #(
instr_rdata_alu_id_o <= instr_decompressed;
instr_fetch_err_o <= if_instr_err;
instr_fetch_err_plus2_o <= if_instr_err_plus2;
instr_rdata_c_id_o <= if_instr_rdata[15:0];
instr_rdata_c_id_o <= fetch_rdata; //if_instr_rdata[15:0];
instr_is_compressed_id_o <= instr_is_compressed;
illegal_c_insn_id_o <= illegal_c_insn;
pc_id_o <= pc_if_o;
end
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
begin : g_bp_taken
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
instr_bp_taken_q <= '0;
end else if (if_id_pipe_reg_we) begin
instr_bp_taken_q <= instr_bp_taken_d;
end
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 taken branch and ID/EX is not ready the
// instruction in IF 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 = predict_branch_taken & ~pc_set_i & ~id_in_ready_i & ~instr_skid_valid_q;
assign instr_skid_valid_d = (instr_skid_valid_q & ~id_in_ready_i) |
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 or negedge rst_ni) begin
if (!rst_ni) begin
instr_skid_bp_taken_q <= '0;
instr_skid_data_q <= '0;
instr_skid_addr_q <= '0;
end else 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
cve2_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;
assign if_instr_valid = fetch_valid | (instr_skid_valid_q & ~nt_branch_mispredict_i);
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_bus_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 & ~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 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_bus_err = fetch_err;
assign fetch_ready = id_in_ready_i;
end
assign fetch_ready = id_in_ready_i;
////////////////
// Assertions //
@ -382,109 +266,6 @@ module cve2_if_stage import cve2_pkg::*; #(
// Selectors must be known/valid.
`ASSERT_KNOWN(IbexExcPcMuxKnown, exc_pc_mux_i)
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 ? 32'd2 : 32'd4);
logic predicted_branch;
// pc_set_i takes precendence over branch prediction
assign predicted_branch = predict_branch_taken & ~pc_set_i;
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)
// Note that we should never see a mispredict and an incoming branch on the same cycle.
// The mispredict also cancels any predicted branch so overall branch_req must be low.
`ASSERT(NoMispredBranch, nt_branch_mispredict_i |-> ~branch_req)
`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

@ -17,8 +17,6 @@ module cve2_prefetch_buffer #(
input logic req_i,
input logic branch_i,
input logic branch_mispredict_i,
input logic [31:0] mispredict_addr_i,
input logic [31:0] addr_i,
@ -64,16 +62,12 @@ module cve2_prefetch_buffer #(
logic valid_raw;
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 //
//////////////////////////////////////////////
@ -81,7 +75,7 @@ module cve2_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_or_mispredict;
assign fifo_clear = branch_i;
// 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
@ -120,7 +114,7 @@ module cve2_prefetch_buffer #(
//////////////
// Make a new request any time there is space in the FIFO, and space in the request queue
assign valid_new_req = req_i & (fifo_ready | branch_or_mispredict) &
assign valid_new_req = req_i & (fifo_ready | branch_i) &
~rdata_outstanding_q[NUM_REQS-1];
assign valid_req = valid_req_q | valid_new_req;
@ -129,7 +123,7 @@ module cve2_prefetch_buffer #(
assign valid_req_d = valid_req & ~instr_gnt_i;
// Record whether an outstanding bus request is cancelled by a branch
assign discard_req_d = valid_req_q & (branch_or_mispredict | discard_req_q);
assign discard_req_d = valid_req_q & (branch_i | discard_req_q);
////////////////
// Fetch addr //
@ -164,10 +158,9 @@ module cve2_prefetch_buffer #(
// 2. fetch_addr_q
// Update on a branch or as soon as a request is issued
assign fetch_addr_en = branch_or_mispredict | (valid_new_req & ~valid_req_q);
assign fetch_addr_en = branch_i | (valid_new_req & ~valid_req_q);
assign fetch_addr_d = (branch_i ? addr_i :
branch_mispredict_i ? {mispredict_addr_i[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};
@ -183,7 +176,6 @@ module cve2_prefetch_buffer #(
// Address mux
assign instr_addr = valid_req_q ? stored_addr_q :
branch_i ? addr_i :
branch_mispredict_i ? mispredict_addr_i :
fetch_addr_q;
assign instr_addr_w_aligned = {instr_addr[31:2], 2'b00};
@ -202,7 +194,7 @@ module cve2_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 & instr_gnt_i & discard_req_d) |
(branch_or_mispredict & rdata_outstanding_q[i]) |
(branch_i & rdata_outstanding_q[i]) |
branch_discard_q[i];
end else begin : g_reqtop
@ -214,7 +206,7 @@ module cve2_prefetch_buffer #(
rdata_outstanding_q[i];
assign branch_discard_n[i] = (valid_req & instr_gnt_i & discard_req_d &
rdata_outstanding_q[i-1]) |
(branch_or_mispredict & rdata_outstanding_q[i]) |
(branch_i & rdata_outstanding_q[i]) |
branch_discard_q[i];
end
end
@ -228,7 +220,7 @@ module cve2_prefetch_buffer #(
// Push a new entry to the FIFO once complete (and not cancelled by a branch)
assign fifo_valid = instr_rvalid_i & ~branch_discard_q[0];
assign fifo_addr = branch_i ? addr_i : mispredict_addr_i;
assign fifo_addr = addr_i;
///////////////
// Registers //
@ -255,6 +247,6 @@ module cve2_prefetch_buffer #(
assign instr_req_o = valid_req;
assign instr_addr_o = instr_addr_w_aligned;
assign valid_o = valid_raw & ~branch_mispredict_i;
assign valid_o = valid_raw;
endmodule

View file

@ -17,7 +17,6 @@ module cve2_top import cve2_pkg::*; #(
parameter int unsigned MHPMCounterWidth = 40,
parameter bit RV32E = 1'b0,
parameter rv32m_e RV32M = RV32MFast,
parameter bit BranchPredictor = 1'b0,
parameter int unsigned DmHaltAddr = 32'h1A110800,
parameter int unsigned DmExceptionAddr = 32'h1A110808
) (
@ -159,7 +158,6 @@ module cve2_top import cve2_pkg::*; #(
.RV32E (RV32E),
.RV32M (RV32M),
.RV32B (RV32B),
.BranchPredictor (BranchPredictor),
.DbgTriggerEn (DbgTriggerEn),
.DbgHwBreakNum (DbgHwBreakNum),
.DmHaltAddr (DmHaltAddr),

View file

@ -12,7 +12,6 @@ module cve2_top_tracing import cve2_pkg::*; #(
parameter bit RV32E = 1'b0,
parameter rv32m_e RV32M = RV32MFast,
parameter rv32b_e RV32B = RV32BNone,
parameter bit BranchPredictor = 1'b0,
parameter bit DbgTriggerEn = 1'b0,
parameter int unsigned DbgHwBreakNum = 1,
parameter int unsigned DmHaltAddr = 32'h1A110800,
@ -122,7 +121,6 @@ module cve2_top_tracing import cve2_pkg::*; #(
.RV32E ( RV32E ),
.RV32M ( RV32M ),
.RV32B ( RV32B ),
.BranchPredictor ( BranchPredictor ),
.DbgTriggerEn ( DbgTriggerEn ),
.DbgHwBreakNum ( DbgHwBreakNum ),
.DmHaltAddr ( DmHaltAddr ),