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: