mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-22 04:57:25 -04:00
[rtl] Add branch prediction signals to icache
These changes correspond to similar changes in the prefetch buffer to support branch prediction. A registered version of fill_ext_done was required to prevent a combinational loop from branch_i in to valid_o out. Multiplexing priorities for fifo_addr have been swapped to match fetch_addr_d in the same module and all similar multiplexing in the icache (prioritize incoming branch_i over branch_mispredict_i). Note however that it is not expected that these conditions will actually occur together, and an assertion has been added to check that. Signed-off-by: Tom Roberts <tomroberts@lowrisc.org>
This commit is contained in:
parent
64ee9a930d
commit
8db89a9dfc
6 changed files with 172 additions and 108 deletions
|
@ -25,32 +25,34 @@ module tb #(parameter bit ICacheECC = 1'b0);
|
|||
ibex_icache #(
|
||||
.ICacheECC (ICacheECC)
|
||||
) dut (
|
||||
.clk_i (clk),
|
||||
.rst_ni (rst_n),
|
||||
.clk_i (clk),
|
||||
.rst_ni (rst_n),
|
||||
|
||||
// Connect icache <-> core interface
|
||||
.req_i (core_if.req),
|
||||
.branch_i (core_if.branch),
|
||||
.branch_spec_i (core_if.branch_spec),
|
||||
.addr_i (core_if.branch_addr),
|
||||
.ready_i (core_if.ready),
|
||||
.valid_o (core_if.valid),
|
||||
.rdata_o (core_if.rdata),
|
||||
.addr_o (core_if.addr),
|
||||
.err_o (core_if.err),
|
||||
.err_plus2_o (core_if.err_plus2),
|
||||
.icache_enable_i (core_if.enable),
|
||||
.icache_inval_i (core_if.invalidate),
|
||||
.busy_o (core_if.busy),
|
||||
.req_i (core_if.req),
|
||||
.branch_i (core_if.branch),
|
||||
.branch_spec_i (core_if.branch_spec),
|
||||
.predicted_branch_i (1'b0),
|
||||
.branch_mispredict_i (1'b0),
|
||||
.addr_i (core_if.branch_addr),
|
||||
.ready_i (core_if.ready),
|
||||
.valid_o (core_if.valid),
|
||||
.rdata_o (core_if.rdata),
|
||||
.addr_o (core_if.addr),
|
||||
.err_o (core_if.err),
|
||||
.err_plus2_o (core_if.err_plus2),
|
||||
.icache_enable_i (core_if.enable),
|
||||
.icache_inval_i (core_if.invalidate),
|
||||
.busy_o (core_if.busy),
|
||||
|
||||
// Connect icache <-> instruction bus interface
|
||||
.instr_req_o (mem_if.req),
|
||||
.instr_gnt_i (mem_if.gnt),
|
||||
.instr_addr_o (mem_if.addr),
|
||||
.instr_rdata_i (mem_if.rdata),
|
||||
.instr_err_i (mem_if.err),
|
||||
.instr_pmp_err_i (mem_if.pmp_err),
|
||||
.instr_rvalid_i (mem_if.rvalid)
|
||||
.instr_req_o (mem_if.req),
|
||||
.instr_gnt_i (mem_if.gnt),
|
||||
.instr_addr_o (mem_if.addr),
|
||||
.instr_rdata_i (mem_if.rdata),
|
||||
.instr_err_i (mem_if.err),
|
||||
.instr_pmp_err_i (mem_if.pmp_err),
|
||||
.instr_rvalid_i (mem_if.rvalid)
|
||||
);
|
||||
|
||||
// If the ICacheECC parameter is set in the DUT, generate another interface for each tag ram and
|
||||
|
|
|
@ -17,21 +17,22 @@
|
|||
|
||||
module formal_tb #(
|
||||
// DUT parameters
|
||||
parameter int unsigned BusWidth = 32,
|
||||
parameter int unsigned CacheSizeBytes = 4*1024,
|
||||
parameter bit ICacheECC = 1'b0,
|
||||
parameter int unsigned LineSize = 64,
|
||||
parameter int unsigned NumWays = 2,
|
||||
parameter bit BranchCache = 1'b0,
|
||||
parameter bit BranchPredictor = 1'b0,
|
||||
parameter int unsigned BusWidth = 32,
|
||||
parameter int unsigned CacheSizeBytes = 4*1024,
|
||||
parameter bit ICacheECC = 1'b0,
|
||||
parameter int unsigned LineSize = 64,
|
||||
parameter int unsigned NumWays = 2,
|
||||
parameter bit BranchCache = 1'b0,
|
||||
|
||||
// Internal parameters / localparams
|
||||
parameter int unsigned ADDR_W = 32,
|
||||
parameter int unsigned NUM_FB = 4,
|
||||
parameter int unsigned LINE_W = 3,
|
||||
parameter int unsigned BUS_BYTES = BusWidth/8,
|
||||
parameter int unsigned BUS_W = $clog2(BUS_BYTES),
|
||||
parameter int unsigned LINE_BEATS = 2,
|
||||
parameter int unsigned LINE_BEATS_W = 1
|
||||
parameter int unsigned ADDR_W = 32,
|
||||
parameter int unsigned NUM_FB = 4,
|
||||
parameter int unsigned LINE_W = 3,
|
||||
parameter int unsigned BUS_BYTES = BusWidth/8,
|
||||
parameter int unsigned BUS_W = $clog2(BUS_BYTES),
|
||||
parameter int unsigned LINE_BEATS = 2,
|
||||
parameter int unsigned LINE_BEATS_W = 1
|
||||
) (
|
||||
// Top-level ports
|
||||
input logic clk_i,
|
||||
|
@ -39,6 +40,8 @@ module formal_tb #(
|
|||
input logic req_i,
|
||||
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,
|
||||
input logic ready_i,
|
||||
input logic valid_o,
|
||||
|
@ -65,7 +68,8 @@ module formal_tb #(
|
|||
input logic [NUM_FB-1:0] fill_hit_q,
|
||||
input logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_ext_cnt_q,
|
||||
input logic [NUM_FB-1:0] fill_ext_hold_q,
|
||||
input logic [NUM_FB-1:0] fill_ext_done,
|
||||
input logic [NUM_FB-1:0] fill_ext_done_d,
|
||||
input logic [NUM_FB-1:0] fill_ext_done_q,
|
||||
input logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_rvd_cnt_q,
|
||||
input logic [NUM_FB-1:0] fill_rvd_done,
|
||||
input logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_out_cnt_q,
|
||||
|
@ -122,6 +126,17 @@ module formal_tb #(
|
|||
end
|
||||
end
|
||||
|
||||
// Reset assumptions
|
||||
//
|
||||
// We assume that req_i isn't asserted to the block when in reset (which avoids it making requests
|
||||
// on the external bus).
|
||||
`ASSUME_ZERO_IN_RESET(req_i)
|
||||
|
||||
// Parameter assumptions
|
||||
//
|
||||
// If BranchPredictor = 1'b0, the branch_mispredict_i signal will never go high
|
||||
`ASSUME(no_mispred_without_branch_pred, `IMPLIES(branch_mispredict_i, BranchPredictor))
|
||||
|
||||
// Protocol assumptions
|
||||
//
|
||||
// These are assumptions based on the top-level ports. They somewhat mirror the assertions in the
|
||||
|
@ -346,7 +361,7 @@ module formal_tb #(
|
|||
(fill_ext_cnt_q[fb] != '0) &&
|
||||
fill_older_q[fb][fb2] &&
|
||||
fill_busy_q[fb2]),
|
||||
fill_ext_done[fb2]))
|
||||
fill_ext_done_q[fb2]))
|
||||
|
||||
// Similarly, if J is older than I then we should see fill_rvd_done[J] before
|
||||
// fill_rvd_cnt_q[I] is nonzero.
|
||||
|
@ -656,7 +671,7 @@ module formal_tb #(
|
|||
// for beat b and the memory request was squashed by a PMP error.
|
||||
//
|
||||
// The former case is easy (bit b should be set in f_fill_rvd_mask). In the latter case,
|
||||
// fill_ext_done will be true, fill_ext_cnt_q will be less than LINE_BEATS, and fill_ext_off (the
|
||||
// fill_ext_done_d will be true, fill_ext_cnt_q will be less than LINE_BEATS, and fill_ext_off (the
|
||||
// next beat to fetch) will equal b. We define explicit masks for the bits allowed in each case.
|
||||
logic [NUM_FB-1:0][LINE_BEATS-1:0] f_rvd_err_mask, f_pmp_err_mask, f_err_mask;
|
||||
always_comb begin
|
||||
|
@ -665,7 +680,7 @@ module formal_tb #(
|
|||
for (int i = 0; i < NUM_FB; i++) begin
|
||||
f_rvd_err_mask[i] = f_fill_rvd_mask[i];
|
||||
for (int b = 0; b < LINE_BEATS; b++) begin
|
||||
f_pmp_err_mask[i][b] = (fill_ext_done[i] &&
|
||||
f_pmp_err_mask[i][b] = (fill_ext_done_d[i] &&
|
||||
!fill_ext_cnt_q[i][LINE_BEATS_W] &&
|
||||
(fill_ext_off[i] == b[LINE_BEATS_W-1:0]));
|
||||
end
|
||||
|
|
|
@ -8,18 +8,19 @@
|
|||
// Using a wildcard (.*) for ports allows the testbench to inspect internal signals of the cache.
|
||||
|
||||
formal_tb #(
|
||||
.BusWidth (BusWidth),
|
||||
.CacheSizeBytes (CacheSizeBytes),
|
||||
.ICacheECC (ICacheECC),
|
||||
.LineSize (LineSize),
|
||||
.NumWays (NumWays),
|
||||
.BranchCache (BranchCache),
|
||||
.BranchPredictor (BranchPredictor),
|
||||
.BusWidth (BusWidth),
|
||||
.CacheSizeBytes (CacheSizeBytes),
|
||||
.ICacheECC (ICacheECC),
|
||||
.LineSize (LineSize),
|
||||
.NumWays (NumWays),
|
||||
.BranchCache (BranchCache),
|
||||
|
||||
.ADDR_W (ADDR_W),
|
||||
.NUM_FB (NUM_FB),
|
||||
.LINE_W (LINE_W),
|
||||
.BUS_BYTES (BUS_BYTES),
|
||||
.BUS_W (BUS_W),
|
||||
.LINE_BEATS (LINE_BEATS),
|
||||
.LINE_BEATS_W (LINE_BEATS_W)
|
||||
.ADDR_W (ADDR_W),
|
||||
.NUM_FB (NUM_FB),
|
||||
.LINE_W (LINE_W),
|
||||
.BUS_BYTES (BUS_BYTES),
|
||||
.BUS_W (BUS_W),
|
||||
.LINE_BEATS (LINE_BEATS),
|
||||
.LINE_BEATS_W (LINE_BEATS_W)
|
||||
) tb_i (.*);
|
||||
|
|
|
@ -11,14 +11,15 @@
|
|||
`include "prim_assert.sv"
|
||||
|
||||
module ibex_icache #(
|
||||
parameter bit BranchPredictor = 1'b0,
|
||||
// Cache arrangement parameters
|
||||
parameter int unsigned BusWidth = 32,
|
||||
parameter int unsigned CacheSizeBytes = 4*1024,
|
||||
parameter bit ICacheECC = 1'b0,
|
||||
parameter int unsigned LineSize = 64,
|
||||
parameter int unsigned NumWays = 2,
|
||||
parameter int unsigned BusWidth = 32,
|
||||
parameter int unsigned CacheSizeBytes = 4*1024,
|
||||
parameter bit ICacheECC = 1'b0,
|
||||
parameter int unsigned LineSize = 64,
|
||||
parameter int unsigned NumWays = 2,
|
||||
// Only cache branch targets
|
||||
parameter bit BranchCache = 1'b0
|
||||
parameter bit BranchCache = 1'b0
|
||||
) (
|
||||
// Clock and reset
|
||||
input logic clk_i,
|
||||
|
@ -30,6 +31,8 @@ module ibex_icache #(
|
|||
// Set the cache's address counter
|
||||
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,
|
||||
|
||||
// IF stage interface: Pass fetched instructions to the core
|
||||
|
@ -77,8 +80,10 @@ module ibex_icache #(
|
|||
|
||||
// Prefetch signals
|
||||
logic [ADDR_W-1:0] lookup_addr_aligned;
|
||||
logic [ADDR_W-1:0] branch_mispredict_addr;
|
||||
logic [ADDR_W-1:0] prefetch_addr_d, prefetch_addr_q;
|
||||
logic prefetch_addr_en;
|
||||
logic branch_or_mispredict;
|
||||
// Cache pipelipe IC0 signals
|
||||
logic branch_suppress;
|
||||
logic lookup_throttle;
|
||||
|
@ -134,7 +139,7 @@ module ibex_icache #(
|
|||
logic [NUM_FB-1:0] fill_hit_ic1, fill_hit_d, fill_hit_q;
|
||||
logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_ext_cnt_d, fill_ext_cnt_q;
|
||||
logic [NUM_FB-1:0] fill_ext_hold_d, fill_ext_hold_q;
|
||||
logic [NUM_FB-1:0] fill_ext_done;
|
||||
logic [NUM_FB-1:0] fill_ext_done_d, fill_ext_done_q;
|
||||
logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_rvd_cnt_d, fill_rvd_cnt_q;
|
||||
logic [NUM_FB-1:0] fill_rvd_done;
|
||||
logic [NUM_FB-1:0] fill_ram_done_d, fill_ram_done_q;
|
||||
|
@ -175,6 +180,7 @@ module ibex_icache #(
|
|||
logic output_valid;
|
||||
logic addr_incr_two;
|
||||
logic output_addr_en;
|
||||
logic [ADDR_W-1:1] output_addr_incr;
|
||||
logic [ADDR_W-1:1] output_addr_d, output_addr_q;
|
||||
logic [15:0] output_data_lo, output_data_hi;
|
||||
logic data_valid, output_ready;
|
||||
|
@ -194,6 +200,33 @@ module ibex_icache #(
|
|||
// Instruction prefetch //
|
||||
//////////////////////////
|
||||
|
||||
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 <= {output_addr_incr, 1'b0};
|
||||
end
|
||||
end
|
||||
|
||||
assign branch_mispredict_addr = branch_mispredict_addr_q;
|
||||
|
||||
end else begin : g_no_branch_predictor
|
||||
logic unused_predicted_branch;
|
||||
|
||||
assign unused_predicted_branch = predicted_branch_i;
|
||||
|
||||
assign branch_mispredict_addr = '0;
|
||||
end
|
||||
|
||||
assign branch_or_mispredict = branch_i | branch_mispredict_i;
|
||||
|
||||
assign lookup_addr_aligned = {lookup_addr_ic0[ADDR_W-1:LINE_W],{LINE_W{1'b0}}};
|
||||
|
||||
// The prefetch address increments by one cache line for each granted request.
|
||||
|
@ -204,9 +237,10 @@ module ibex_icache #(
|
|||
// line must also be recorded for later use by the fill buffers.
|
||||
assign prefetch_addr_d =
|
||||
lookup_grant_ic0 ? (lookup_addr_aligned + {{ADDR_W-LINE_W-1{1'b0}},1'b1,{LINE_W{1'b0}}}) :
|
||||
addr_i;
|
||||
branch_i ? addr_i :
|
||||
branch_mispredict_addr;
|
||||
|
||||
assign prefetch_addr_en = branch_i | lookup_grant_ic0;
|
||||
assign prefetch_addr_en = branch_or_mispredict | lookup_grant_ic0;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (prefetch_addr_en) begin
|
||||
|
@ -221,9 +255,11 @@ module ibex_icache #(
|
|||
// Cache lookup
|
||||
assign lookup_throttle = (fb_fill_level > FB_THRESHOLD[$clog2(NUM_FB)-1:0]);
|
||||
|
||||
assign lookup_req_ic0 = req_i & ~&fill_busy_q & (branch_i | ~lookup_throttle) & ~ecc_write_req;
|
||||
assign lookup_addr_ic0 = branch_spec_i ? addr_i :
|
||||
prefetch_addr_q;
|
||||
assign lookup_req_ic0 = req_i & ~&fill_busy_q & (branch_or_mispredict | ~lookup_throttle) &
|
||||
~ecc_write_req;
|
||||
assign lookup_addr_ic0 = branch_spec_i ? addr_i :
|
||||
branch_mispredict_i ? branch_mispredict_addr :
|
||||
prefetch_addr_q;
|
||||
assign lookup_index_ic0 = lookup_addr_ic0[INDEX_HI:LINE_W];
|
||||
|
||||
// Cache write
|
||||
|
@ -516,7 +552,7 @@ module ibex_icache #(
|
|||
assign fill_new_alloc = lookup_grant_ic0;
|
||||
// Track whether a speculative external request was made from IC0, and whether it was granted
|
||||
// Speculative requests are only made for branches, or if the cache is disabled
|
||||
assign fill_spec_req = (~icache_enable_i | branch_i) & ~|fill_ext_req;
|
||||
assign fill_spec_req = (~icache_enable_i | branch_or_mispredict) & ~|fill_ext_req;
|
||||
assign fill_spec_done = fill_spec_req & gnt_not_pmp_err;
|
||||
assign fill_spec_hold = fill_spec_req & ~gnt_or_pmp_err;
|
||||
|
||||
|
@ -545,7 +581,7 @@ module ibex_icache #(
|
|||
assign fill_done[fb] = (fill_ram_done_q[fb] | fill_hit_q[fb] | ~fill_cache_q[fb] |
|
||||
(|fill_err_q[fb])) &
|
||||
// all data output unless stale due to intervening branch
|
||||
(fill_out_done[fb] | fill_stale_q[fb] | branch_i) &
|
||||
(fill_out_done[fb] | fill_stale_q[fb] | branch_or_mispredict) &
|
||||
// all external requests completed
|
||||
fill_rvd_done[fb];
|
||||
|
||||
|
@ -554,7 +590,7 @@ module ibex_icache #(
|
|||
/////////////////////////////////
|
||||
|
||||
// Track staleness (requests become stale when a branch intervenes)
|
||||
assign fill_stale_d[fb] = fill_busy_q[fb] & (branch_i | fill_stale_q[fb]);
|
||||
assign fill_stale_d[fb] = fill_busy_q[fb] & (branch_or_mispredict | fill_stale_q[fb]);
|
||||
// Track whether or not this request should allocate to the cache
|
||||
// Any invalidation or disabling of the cache while the buffer is busy will stop allocation
|
||||
assign fill_cache_d[fb] = (fill_alloc[fb] & fill_cache_new) |
|
||||
|
@ -569,7 +605,7 @@ module ibex_icache #(
|
|||
///////////////////////////////////////////
|
||||
|
||||
// Make an external request
|
||||
assign fill_ext_req[fb] = fill_busy_q[fb] & ~fill_ext_done[fb];
|
||||
assign fill_ext_req[fb] = fill_busy_q[fb] & ~fill_ext_done_d[fb];
|
||||
|
||||
// Count the number of completed external requests (each line requires LINE_BEATS requests)
|
||||
// Don't count fake PMP error grants here since they will never receive an rvalid response
|
||||
|
@ -581,17 +617,17 @@ module ibex_icache #(
|
|||
assign fill_ext_hold_d[fb] = (fill_alloc[fb] & fill_spec_hold) |
|
||||
(fill_ext_arb[fb] & ~gnt_or_pmp_err);
|
||||
// External requests are completed when the counter is filled or when the request is cancelled
|
||||
assign fill_ext_done[fb] = (fill_ext_cnt_q[fb][LINE_BEATS_W] |
|
||||
assign fill_ext_done_d[fb] = (fill_ext_cnt_q[fb][LINE_BEATS_W] |
|
||||
// external requests are considered complete if the request hit
|
||||
fill_hit_ic1[fb] | fill_hit_q[fb] |
|
||||
// external requests will stop once any PMP error is received
|
||||
fill_err_q[fb][fill_ext_off[fb]] |
|
||||
// cancel if the line won't be cached and, it is stale
|
||||
(~fill_cache_q[fb] & (branch_i | fill_stale_q[fb] |
|
||||
(~fill_cache_q[fb] & (branch_or_mispredict | fill_stale_q[fb] |
|
||||
// or we're already at the end of the line
|
||||
fill_ext_beat[fb][LINE_BEATS_W]))) &
|
||||
// can't cancel while we are waiting for a grant on the bus
|
||||
~fill_ext_hold_q[fb];
|
||||
~fill_ext_hold_q[fb] & fill_busy_q[fb];
|
||||
// Track whether this fill buffer expects to receive beats of data
|
||||
assign fill_rvd_exp[fb] = fill_busy_q[fb] & ~fill_rvd_done[fb];
|
||||
// Count the number of rvalid beats received
|
||||
|
@ -599,7 +635,8 @@ module ibex_icache #(
|
|||
(fill_rvd_cnt_q[fb] +
|
||||
{{LINE_BEATS_W{1'b0}},fill_rvd_arb[fb]});
|
||||
// External data is complete when all issued external requests have received their data
|
||||
assign fill_rvd_done[fb] = fill_ext_done[fb] & (fill_rvd_cnt_q[fb] == fill_ext_cnt_q[fb]);
|
||||
assign fill_rvd_done[fb] = (fill_ext_done_q[fb] & ~fill_ext_hold_q[fb]) &
|
||||
(fill_rvd_cnt_q[fb] == fill_ext_cnt_q[fb]);
|
||||
|
||||
//////////////////////////////////////
|
||||
// Fill buffer data output tracking //
|
||||
|
@ -705,6 +742,7 @@ module ibex_icache #(
|
|||
fill_hit_q[fb] <= 1'b0;
|
||||
fill_ext_cnt_q[fb] <= '0;
|
||||
fill_ext_hold_q[fb] <= 1'b0;
|
||||
fill_ext_done_q[fb] <= 1'b0;
|
||||
fill_rvd_cnt_q[fb] <= '0;
|
||||
fill_ram_done_q[fb] <= 1'b0;
|
||||
fill_out_cnt_q[fb] <= '0;
|
||||
|
@ -716,6 +754,7 @@ module ibex_icache #(
|
|||
fill_hit_q[fb] <= fill_hit_d[fb];
|
||||
fill_ext_cnt_q[fb] <= fill_ext_cnt_d[fb];
|
||||
fill_ext_hold_q[fb] <= fill_ext_hold_d[fb];
|
||||
fill_ext_done_q[fb] <= fill_ext_done_d[fb];
|
||||
fill_rvd_cnt_q[fb] <= fill_rvd_cnt_d[fb];
|
||||
fill_ram_done_q[fb] <= fill_ram_done_d[fb];
|
||||
fill_out_cnt_q[fb] <= fill_out_cnt_d[fb];
|
||||
|
@ -828,7 +867,7 @@ module ibex_icache #(
|
|||
// External requests //
|
||||
///////////////////////
|
||||
|
||||
assign instr_req = ((~icache_enable_i | branch_i) & lookup_grant_ic0) |
|
||||
assign instr_req = ((~icache_enable_i | branch_or_mispredict) & lookup_grant_ic0) |
|
||||
(|fill_ext_req);
|
||||
|
||||
assign instr_addr = |fill_ext_req ? fill_ext_req_addr :
|
||||
|
@ -893,7 +932,7 @@ module ibex_icache #(
|
|||
|
||||
assign skid_valid_d =
|
||||
// Branches invalidate the skid buffer
|
||||
branch_i ? 1'b0 :
|
||||
branch_or_mispredict ? 1'b0 :
|
||||
// Once valid, the skid buffer stays valid until a compressed instruction realigns the stream
|
||||
(skid_valid_q ? ~(ready_i & ((skid_data_q[1:0] != 2'b11) | skid_err_q)) :
|
||||
// The skid buffer becomes valid when:
|
||||
|
@ -921,15 +960,20 @@ module ibex_icache #(
|
|||
output_err | (output_data[17:16] != 2'b11)));
|
||||
|
||||
// Update the address on branches and every time an instruction is driven
|
||||
assign output_addr_en = branch_i | (ready_i & valid_o);
|
||||
assign output_addr_en = branch_or_mispredict | (ready_i & valid_o);
|
||||
|
||||
// Increment the address by two every time a compressed instruction is popped
|
||||
assign addr_incr_two = output_compressed & ~err_o;
|
||||
|
||||
assign output_addr_d = branch_i ? addr_i[31:1] :
|
||||
(output_addr_q[31:1] +
|
||||
// Increment address by 4 or 2
|
||||
{29'd0, ~addr_incr_two, addr_incr_two});
|
||||
// Next IF stage PC
|
||||
assign output_addr_incr = (output_addr_q[31:1] +
|
||||
// Increment address by 4 or 2
|
||||
{29'd0, ~addr_incr_two, addr_incr_two});
|
||||
|
||||
// Redirect the address on branches or mispredicts
|
||||
assign output_addr_d = branch_i ? addr_i[31:1] :
|
||||
branch_mispredict_i ? branch_mispredict_addr[31:1] :
|
||||
output_addr_incr;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (output_addr_en) begin
|
||||
|
@ -963,7 +1007,7 @@ module ibex_icache #(
|
|||
end
|
||||
end
|
||||
|
||||
assign valid_o = output_valid;
|
||||
assign valid_o = output_valid & ~branch_mispredict_i;
|
||||
assign rdata_o = {output_data_hi, (skid_valid_q ? skid_data_q : output_data_lo)};
|
||||
assign addr_o = {output_addr_q, 1'b0};
|
||||
assign err_o = (skid_valid_q & skid_err_q) | (~skid_complete_instr & output_err);
|
||||
|
|
|
@ -184,40 +184,39 @@ module ibex_if_stage #(
|
|||
if (ICache) begin : gen_icache
|
||||
// Full I-Cache option
|
||||
ibex_icache #(
|
||||
.ICacheECC (ICacheECC)
|
||||
.BranchPredictor (BranchPredictor),
|
||||
.ICacheECC (ICacheECC)
|
||||
) icache_i (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
|
||||
.req_i ( req_i ),
|
||||
.req_i ( req_i ),
|
||||
|
||||
.branch_i ( branch_req ),
|
||||
.branch_spec_i ( branch_spec ),
|
||||
.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 ),
|
||||
|
||||
.icache_enable_i ( icache_enable_i ),
|
||||
.icache_inval_i ( icache_inval_i ),
|
||||
.busy_o ( prefetch_busy )
|
||||
.icache_enable_i ( icache_enable_i ),
|
||||
.icache_inval_i ( icache_inval_i ),
|
||||
.busy_o ( prefetch_busy )
|
||||
);
|
||||
// Branch predictor tie-offs (which are unused when the instruction cache is enabled)
|
||||
logic unused_nt_branch_mispredict, unused_predicted_branch;
|
||||
assign unused_nt_branch_mispredict = nt_branch_mispredict_i;
|
||||
assign unused_predicted_branch = predicted_branch;
|
||||
end else begin : gen_prefetch_buffer
|
||||
// prefetch buffer, caches a fixed number of instructions
|
||||
ibex_prefetch_buffer #(
|
||||
|
@ -590,6 +589,9 @@ module ibex_if_stage #(
|
|||
// 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
|
||||
|
|
|
@ -286,7 +286,7 @@ 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;
|
||||
assign fifo_addr = branch_i ? addr_i : branch_mispredict_addr;
|
||||
|
||||
///////////////
|
||||
// Registers //
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue