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