../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: