../src/lowrisc_ibex_ibex_core_0.1/rtl/ibex_prefetch_buffer.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md.
3: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
4: // SPDX-License-Identifier: Apache-2.0
5:
6: /**
7: * Prefetcher Buffer for 32 bit memory interface
8: *
9: * Prefetch Buffer that caches instructions. This cuts overly long critical
10: * paths to the instruction cache.
11: */
12: module ibex_prefetch_buffer (
13: input logic clk_i,
14: input logic rst_ni,
15:
16: input logic req_i,
17:
18: input logic branch_i,
19: input logic branch_spec_i,
20: input logic [31:0] addr_i,
21:
22:
23: input logic ready_i,
24: output logic valid_o,
25: output logic [31:0] rdata_o,
26: output logic [31:0] addr_o,
27: output logic err_o,
28: output logic err_plus2_o,
29:
30:
31: // goes to instruction memory / instruction cache
32: output logic instr_req_o,
33: input logic instr_gnt_i,
34: output logic [31:0] instr_addr_o,
35: input logic [31:0] instr_rdata_i,
36: input logic instr_err_i,
37: input logic instr_pmp_err_i,
38: input logic instr_rvalid_i,
39:
40: // Prefetch Buffer Status
41: output logic busy_o
42: );
43:
44: localparam int unsigned NUM_REQS = 2;
45:
46: logic branch_suppress;
47: logic valid_new_req, valid_req;
48: logic valid_req_d, valid_req_q;
49: logic discard_req_d, discard_req_q;
50: logic gnt_or_pmp_err, rvalid_or_pmp_err;
51: logic [NUM_REQS-1:0] rdata_outstanding_n, rdata_outstanding_s, rdata_outstanding_q;
52: logic [NUM_REQS-1:0] branch_discard_n, branch_discard_s, branch_discard_q;
53: logic [NUM_REQS-1:0] rdata_pmp_err_n, rdata_pmp_err_s, rdata_pmp_err_q;
54: logic [NUM_REQS-1:0] rdata_outstanding_rev;
55:
56: logic [31:0] stored_addr_d, stored_addr_q;
57: logic stored_addr_en;
58: logic [31:0] fetch_addr_d, fetch_addr_q;
59: logic fetch_addr_en;
60: logic [31:0] instr_addr, instr_addr_w_aligned;
61: logic instr_or_pmp_err;
62:
63: logic fifo_valid;
64: logic fifo_ready;
65: logic fifo_clear;
66: logic [NUM_REQS-1:0] fifo_busy;
67:
68: ////////////////////////////
69: // Prefetch buffer status //
70: ////////////////////////////
71:
72: assign busy_o = (|rdata_outstanding_q) | instr_req_o;
73:
74: //////////////////////////////////////////////
75: // Fetch fifo - consumes addresses and data //
76: //////////////////////////////////////////////
77:
78: // Instruction fetch errors are valid on the data phase of a request
79: // PMP errors are generated in the address phase, and registered into a fake data phase
80: assign instr_or_pmp_err = instr_err_i | rdata_pmp_err_q[0];
81:
82: // A branch will invalidate any previously fetched instructions.
83: // Note that the FENCE.I instruction relies on this flushing behaviour on branch. If it is
84: // altered the FENCE.I implementation may require changes.
85: assign fifo_clear = branch_i;
86:
87: // Reversed version of rdata_outstanding_q which can be overlaid with fifo fill state
88: for (genvar i = 0; i < NUM_REQS; i++) begin : gen_rd_rev
89: assign rdata_outstanding_rev[i] = rdata_outstanding_q[NUM_REQS-1-i];
90: end
91:
92: // The fifo is ready to accept a new request if it is not full - including space reserved for
93: // requests already outstanding.
94: // Overlay the fifo fill state with the outstanding requests to see if there is space.
95: assign fifo_ready = ~&(fifo_busy | rdata_outstanding_rev);
96:
97: ibex_fetch_fifo #(
98: .NUM_REQS (NUM_REQS)
99: ) fifo_i (
100: .clk_i ( clk_i ),
101: .rst_ni ( rst_ni ),
102:
103: .clear_i ( fifo_clear ),
104: .busy_o ( fifo_busy ),
105:
106: .in_valid_i ( fifo_valid ),
107: .in_addr_i ( addr_i ),
108: .in_rdata_i ( instr_rdata_i ),
109: .in_err_i ( instr_or_pmp_err ),
110:
111: .out_valid_o ( valid_o ),
112: .out_ready_i ( ready_i ),
113: .out_rdata_o ( rdata_o ),
114: .out_addr_o ( addr_o ),
115: .out_err_o ( err_o ),
116: .out_err_plus2_o ( err_plus2_o )
117: );
118:
119: //////////////
120: // Requests //
121: //////////////
122:
123: // Suppress a new request on a not-taken branch (as the external address will be incorrect)
124: assign branch_suppress = branch_spec_i & ~branch_i;
125:
126: // Make a new request any time there is space in the FIFO, and space in the request queue
127: assign valid_new_req = ~branch_suppress & req_i & (fifo_ready | branch_i) &
128: ~rdata_outstanding_q[NUM_REQS-1];
129:
130: assign valid_req = valid_req_q | valid_new_req;
131:
132: // If a request address triggers a PMP error, the external bus request is suppressed. We might
133: // therefore never receive a grant for such a request. The grant is faked in this case to make
134: // sure the request proceeds and the error is pushed to the FIFO.
135: assign gnt_or_pmp_err = instr_gnt_i | instr_pmp_err_i;
136:
137: // As with the grant, the rvalid must be faked for a PMP error, since the request was suppressed.
138: assign rvalid_or_pmp_err = rdata_outstanding_q[0] & (instr_rvalid_i | rdata_pmp_err_q[0]);
139:
140: // Hold the request stable for requests that didn't get granted
141: assign valid_req_d = valid_req & ~gnt_or_pmp_err;
142:
143: // Record whether an outstanding bus request is cancelled by a branch
144: assign discard_req_d = valid_req_q & (branch_i | discard_req_q);
145:
146: ////////////////
147: // Fetch addr //
148: ////////////////
149:
150: // Two addresses are tracked in the prefetch buffer:
151: // 1. stored_addr_q - This is the address issued on the bus. It stays stable until
152: // the request is granted.
153: // 2. fetch_addr_q - This is our next address to fetch from. It is updated on branches to
154: // capture the new address, and then for each new request issued.
155: // A third address is tracked in the fetch FIFO itself:
156: // 3. instr_addr_q - This is the address at the head of the FIFO, efectively our oldest fetched
157: // address. This address is updated on branches, and does its own increment
158: // each time the FIFO is popped.
159:
160: // 1. stored_addr_q
161:
162: // Only update stored_addr_q for new ungranted requests
163: assign stored_addr_en = valid_new_req & ~valid_req_q & ~gnt_or_pmp_err;
164:
165: // Store whatever address was issued on the bus
166: assign stored_addr_d = instr_addr;
167:
168: // CPU resets with a branch, so no need to reset these addresses
169: always_ff @(posedge clk_i) begin
170: if (stored_addr_en) begin
171: stored_addr_q <= stored_addr_d;
172: end
173: end
174:
175: // 2. fetch_addr_q
176:
177: // Update on a branch or as soon as a request is issued
178: assign fetch_addr_en = branch_i | (valid_new_req & ~valid_req_q);
179:
180: assign fetch_addr_d = (branch_i ? addr_i :
181: {fetch_addr_q[31:2], 2'b00}) +
182: // Current address + 4
183: {{29{1'b0}},(valid_new_req & ~valid_req_q),2'b00};
184:
185: always_ff @(posedge clk_i) begin
186: if (fetch_addr_en) begin
187: fetch_addr_q <= fetch_addr_d;
188: end
189: end
190:
191: // Address mux
192: assign instr_addr = valid_req_q ? stored_addr_q :
193: branch_spec_i ? addr_i :
194: fetch_addr_q;
195:
196: assign instr_addr_w_aligned = {instr_addr[31:2], 2'b00};
197:
198: ///////////////////////////////
199: // Request outstanding queue //
200: ///////////////////////////////
201:
202: for (genvar i = 0; i < NUM_REQS; i++) begin : g_outstanding_reqs
203: // Request 0 (always the oldest outstanding request)
204: if (i == 0) begin : g_req0
205: // A request becomes outstanding once granted, and is cleared once the rvalid is received.
206: // Outstanding requests shift down the queue towards entry 0.
207: assign rdata_outstanding_n[i] = (valid_req & gnt_or_pmp_err) |
208: rdata_outstanding_q[i];
209: // If a branch is received at any point while a request is outstanding, it must be tracked
210: // to ensure we discard the data once received
211: assign branch_discard_n[i] = (valid_req & gnt_or_pmp_err & discard_req_d) |
212: (branch_i & rdata_outstanding_q[i]) | branch_discard_q[i];
213: // Record whether this request received a PMP error
214: assign rdata_pmp_err_n[i] = (valid_req & ~rdata_outstanding_q[i] & instr_pmp_err_i) |
215: rdata_pmp_err_q[i];
216:
217: end else begin : g_reqtop
218: // Entries > 0 consider the FIFO fill state to calculate their next state (by checking
219: // whether the previous entry is valid)
220:
221: assign rdata_outstanding_n[i] = (valid_req & gnt_or_pmp_err &
222: rdata_outstanding_q[i-1]) |
223: rdata_outstanding_q[i];
224: assign branch_discard_n[i] = (valid_req & gnt_or_pmp_err & discard_req_d &
225: rdata_outstanding_q[i-1]) |
226: (branch_i & rdata_outstanding_q[i]) | branch_discard_q[i];
227: assign rdata_pmp_err_n[i] = (valid_req & ~rdata_outstanding_q[i] & instr_pmp_err_i &
228: rdata_outstanding_q[i-1]) |
229: rdata_pmp_err_q[i];
230: end
231: end
232:
233: // Shift the entries down on each instr_rvalid_i
234: assign rdata_outstanding_s = rvalid_or_pmp_err ? {1'b0,rdata_outstanding_n[NUM_REQS-1:1]} :
235: rdata_outstanding_n;
236: assign branch_discard_s = rvalid_or_pmp_err ? {1'b0,branch_discard_n[NUM_REQS-1:1]} :
237: branch_discard_n;
238: assign rdata_pmp_err_s = rvalid_or_pmp_err ? {1'b0,rdata_pmp_err_n[NUM_REQS-1:1]} :
239: rdata_pmp_err_n;
240:
241: // Push a new entry to the FIFO once complete (and not cancelled by a branch)
242: assign fifo_valid = rvalid_or_pmp_err & ~branch_discard_q[0];
243:
244: ///////////////
245: // Registers //
246: ///////////////
247:
248: always_ff @(posedge clk_i or negedge rst_ni) begin
249: if (!rst_ni) begin
250: valid_req_q <= 1'b0;
251: discard_req_q <= 1'b0;
252: rdata_outstanding_q <= 'b0;
253: branch_discard_q <= 'b0;
254: rdata_pmp_err_q <= 'b0;
255: end else begin
256: valid_req_q <= valid_req_d;
257: discard_req_q <= discard_req_d;
258: rdata_outstanding_q <= rdata_outstanding_s;
259: branch_discard_q <= branch_discard_s;
260: rdata_pmp_err_q <= rdata_pmp_err_s;
261: end
262: end
263:
264: /////////////
265: // Outputs //
266: /////////////
267:
268: assign instr_req_o = valid_req;
269: assign instr_addr_o = instr_addr_w_aligned;
270:
271: endmodule
272: