mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-20 03:57:25 -04:00
[rtl] Introduce static branch prediction
This commit is contained in:
parent
b1531f2e38
commit
6123ac7719
20 changed files with 593 additions and 104 deletions
|
@ -95,6 +95,7 @@ jobs:
|
|||
- small
|
||||
- experimental-maxperf-pmp
|
||||
- experimental-maxperf-pmp-bmfull
|
||||
- experimental-branch-predictor
|
||||
|
||||
# Run lint on simple system
|
||||
- bash: |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
100
rtl/ibex_branch_predict.sv
Normal 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
|
|
@ -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,
|
||||
|
|
|
@ -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 ),
|
||||
|
|
|
@ -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 ),
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue