../src/lowrisc_ip_flash_ctrl_0.1/rtl/flash_phy_rd.sv Cov: 95.5%

   1: // Copyright lowRISC contributors.
   2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
   3: // SPDX-License-Identifier: Apache-2.0
   4: //
   5: // Flash Phy Read Module
   6: //
   7: // This module implements the flash phy read pipeline.
   8: // The read pipeline consists of read buffers, the actual flash read stage, the
   9: // descrambling stage, and finally the response.
  10: //
  11: // Note this module backpressures the front end, but cannot handle any back end
  12: // back pressuring at the response stage.  It is thus assumed it will tell the
  13: // upstream to stop issuing instructions, however once issued, the upstream will
  14: // always accept the response.
  15: 
  16: module flash_phy_rd import flash_phy_pkg::*; (
  17:   input clk_i,
  18:   input rst_ni,
  19: 
  20:   // interface with arbitration unit
  21:   input req_i,
  22:   input prog_i,
  23:   input pg_erase_i,
  24:   input bk_erase_i,
  25:   input [BankAddrW-1:0] addr_i,
  26:   output logic rdy_o,
  27:   output logic data_valid_o,
  28:   output logic [BusWidth-1:0] data_o,
  29:   output logic idle_o, // the entire read pipeline is idle
  30: 
  31:   // interface to actual flash primitive
  32:   output logic req_o,
  33:   input ack_i,
  34:   input [DataWidth-1:0] data_i
  35:   );
  36: 
  37:   /////////////////////////////////
  38:   // Read buffers
  39:   /////////////////////////////////
  40: 
  41:   // A buffer allocate is invoked when a new transaction arrives.
  42:   // Alloc only happens if the new transaction does not match an existing entry.
  43:   logic [NumBuf-1:0] alloc;
  44: 
  45:   // A buffer update is invoked after the completion of the de-scramble stage.
  46:   // This updates the buffer that was allocated when a new transaction was initiated.
  47:   logic [NumBuf-1:0] update;
  48: 
  49:   rd_buf_t read_buf [NumBuf];
  50:   logic [NumBuf-1:0] buf_invalid;
  51:   logic [NumBuf-1:0] buf_valid;
  52:   logic [NumBuf-1:0] buf_wip;
  53: 
  54:   // The new transaction matches an already allocated buffer.
  55:   // The buffer may be valid or work in progress.
  56:   logic [NumBuf-1:0] buf_match;
  57:   logic no_match;
  58: 
  59:   // There is a stateful operation aimed at valid buffer, that buffer must be flushed
  60:   logic [NumBuf-1:0] data_hazard;
  61: 
  62:   // The next buffer allocated is determined in the following way:
  63:   // If there is an invalid buffer, use that lowest one
  64:   // If there are no invalid buffers, pick a valid buffer
  65:   // Work in progress buffer is NEVER replaced.
  66:   // There should only be one work in progress buffer at a time
  67:   logic [NumBuf-1:0] buf_invalid_alloc;
  68:   logic [NumBuf-1:0] buf_valid_alloc;
  69:   logic [NumBuf-1:0] buf_alloc;
  70: 
  71:   for (genvar i = 0; i < NumBuf; i++) begin: gen_buf_states
  72:     assign buf_valid[i]   = read_buf[i].attr == Valid;
  73:     assign buf_wip[i]     = read_buf[i].attr == Wip;
  74:     assign buf_invalid[i] = read_buf[i].attr == Invalid;
  75:   end
  76: 
  77:   assign buf_invalid_alloc[0] = buf_invalid[0];
  78:   for (genvar i = 1; i < NumBuf; i++) begin: gen_inv_alloc_bufs
  79:     assign buf_invalid_alloc[i] = buf_invalid[i] & ~|buf_invalid_alloc[i-1:0];
  80:   end
  81: 
  82:   // a prim arbiter is used to somewhat fairly select among the valid buffers
  83:   logic [1:0] dummy_data [NumBuf];
  84:   for (genvar i = 0; i < NumBuf; i++) begin: gen_dummy
  85:     assign dummy_data[i] = '0;
  86:   end
  87: 
  88:   // using prim arbiter tree since it supports per cycle arbitration instead of
  89:   // winner lock
  90:   prim_arbiter_tree #(
  91:     .N(NumBuf),
  92:     .Lock(0),
  93:     .DW(2)
  94:   ) i_valid_random (
  95:     .clk_i,
  96:     .rst_ni,
  97:     .req_i(buf_valid),
  98:     .data_i(dummy_data),
  99:     .gnt_o(buf_valid_alloc),
 100:     .idx_o(),
 101:     .valid_o(),
 102:     .data_o(),
 103:     .ready_i(req_i & rdy_o)
 104:   );
 105: 
 106:   // which buffer to allocate upon a new transaction
 107:   assign buf_alloc = |buf_invalid_alloc ? buf_invalid_alloc : buf_valid_alloc;
 108: 
 109:   // do not attempt to generate match unless the transaction is relevant
 110:   for (genvar i = 0; i < NumBuf; i++) begin: gen_buf_match
 111:     assign buf_match[i] = req_i & (buf_valid[i] | buf_wip[i]) &
 112:                           read_buf[i].addr == addr_i[BankAddrW-1:LsbAddrBit];
 113: 
 114:     // A data hazard should never happen to a wip buffer because it implies
 115:     // that a read is in progress, so a hazard operation cannot start.
 116:     // If bank erase, all buffers must be flushed.
 117:     // If page erase, only if the buffer lands in the same page.
 118:     // If program, only if it's the same flash word.
 119:     assign data_hazard[i] = buf_valid[i] &
 120:                             (bk_erase_i |
 121:                             (prog_i & read_buf[i].addr == addr_i[BankAddrW-1:LsbAddrBit]) |
 122:                             (pg_erase_i & read_buf[i].addr[FlashWordsW +: PageW] ==
 123:                             addr_i[WordW +: PageW]));
 124: 
 125:   end
 126: 
 127:   assign no_match = ~|buf_match;
 128: 
 129:   // if new request does not match anything, allocate
 130:   assign alloc = no_match ? {NumBuf{req_i}} &  buf_alloc : '0;
 131: 
 132:   // read buffers
 133:   // allocate sets state to Wip
 134:   // update sets state to valid
 135:   // wipe sets state to invalid - this comes from prog
 136:   for (genvar i = 0; i < NumBuf; i++) begin: gen_bufs
 137:     flash_phy_rd_buffers i_rd_buf (
 138:       .clk_i,
 139:       .rst_ni,
 140:       .alloc_i(rdy_o & alloc[i]),
 141:       .update_i(update[i]),
 142:       .wipe_i(data_hazard[i]),
 143:       .addr_i(addr_i[BankAddrW-1:LsbAddrBit]),
 144:       .data_i(data_i),
 145:       .out_o(read_buf[i])
 146:     );
 147:   end
 148: 
 149:   /////////////////////////////////
 150:   // Flash read stage
 151:   /////////////////////////////////
 152: 
 153:   // Flash read stage determines if the transactions are accepted.
 154:   //
 155:   // The response fifo is written to when a transaction initiates a flash read OR when a match
 156:   // is hit. The information written is just the allocated buffer that would have satisifed the
 157:   // transaction, as well as bits that indiate which part of the buffer is the right return data
 158:   //
 159:   // This allows a hit transaction to match in-order, and unblock later transactions to begin
 160:   // reading from the flash primitive
 161: 
 162:   rsp_fifo_entry_t rsp_fifo_wdata, rsp_fifo_rdata;
 163:   logic rsp_fifo_rdy;
 164:   logic rsp_fifo_vld;
 165: 
 166:   // whether there is an ongoing read to flash
 167:   // stage is idle when a transaction is ongoing, and the cycle when a response comes from
 168:   // the flash primitive
 169:   logic rd_stage_idle;
 170:   logic rd_busy;
 171:   logic rd_done;
 172:   logic [NumBuf-1:0] alloc_q;
 173: 
 174:   assign rd_done = rd_busy & ack_i;
 175: 
 176:   // if buffer allocated, that is the return source
 177:   // if buffer matched, that is the return source
 178:   assign rsp_fifo_wdata.buf_sel = |alloc ? buf_alloc : buf_match;
 179: 
 180:   // If width is the same, word_sel is unused
 181:   if (WidthMultiple == 1) begin : gen_single_word_sel
 182:     assign rsp_fifo_wdata.word_sel = '0;
 183:   end else begin : gen_word_sel
 184:     assign rsp_fifo_wdata.word_sel = addr_i[0 +: LsbAddrBit];
 185:   end
 186: 
 187:   // response order FIFO
 188:   prim_fifo_sync #(
 189:       .Width  (RspOrderFifoWidth),
 190:       .Pass   (0),
 191:       .Depth  (RspOrderDepth)
 192:   ) i_rsp_order_fifo (
 193:     .clk_i,
 194:     .rst_ni,
 195:     .clr_i  (1'b0),
 196:     .wvalid (req_i && rdy_o),
 197:     .wready (rsp_fifo_rdy),
 198:     .wdata  (rsp_fifo_wdata),
 199:     .depth  (),
 200:     .rvalid (rsp_fifo_vld),
 201:     .rready (data_valid_o), // pop when a match has been found
 202:     .rdata  (rsp_fifo_rdata)
 203:   );
 204: 
 205:   always_ff @(posedge clk_i or negedge rst_ni) begin
 206:     if (!rst_ni) begin
 207:       rd_busy <= 1'b0;
 208:       alloc_q <= '0;
 209:     end else if (req_o) begin
 210:       // read only becomes busy if a buffer is allocated and read
 211:       rd_busy <= 1'b1;
 212:       alloc_q <= alloc;
 213:     end else if (rd_done) begin
 214:       rd_busy <= 1'b0;
 215:     end
 216:   end
 217: 
 218:   // this stage is idle whenever there is not an ongoing read, or if there is
 219:   // but the ack has returned
 220:   assign rd_stage_idle = !rd_busy | ack_i;
 221: 
 222:   // if no buffers matched, accept only if read state is idle and there is space
 223:   // if buffer is matched, accept as long as there is space in the rsp fifo
 224:   assign rdy_o = no_match ? rd_stage_idle & rsp_fifo_rdy : rsp_fifo_rdy;
 225: 
 226:   // issue a transaction to flash
 227:   assign req_o = req_i & rdy_o & no_match;
 228: 
 229:   /////////////////////////////////
 230:   // De-scrambling stage
 231:   /////////////////////////////////
 232: 
 233:   // nothing here yet
 234: 
 235: 
 236:   /////////////////////////////////
 237:   // Response
 238:   /////////////////////////////////
 239: 
 240:   logic flash_rsp_match;
 241:   logic [NumBuf-1:0] buf_rsp_match;
 242:   logic [DataWidth-1:0] buf_rsp_data;
 243: 
 244:   // update buffers
 245:   assign update = rd_done ? alloc_q : '0;
 246: 
 247:   // match in flash response when allocated buffer is the same as top of response fifo
 248:   assign flash_rsp_match = rsp_fifo_vld & rd_done & (rsp_fifo_rdata.buf_sel == alloc_q);
 249: 
 250:   // match in buf response when there is a valie buffer that is the same as top of response fifo
 251:   for (genvar i = 0; i < NumBuf; i++) begin: gen_buf_rsp_match
 252:     assign buf_rsp_match[i] = rsp_fifo_vld & (rsp_fifo_rdata.buf_sel[i] & buf_valid[i]);
 253:   end
 254: 
 255:   // select among the buffers
 256:   always_comb begin
 257:     buf_rsp_data = data_i;
 258:     for (int i = 0; i < NumBuf; i++) begin
 259:       if (buf_rsp_match[i]) begin
 260:         buf_rsp_data = read_buf[i].data;
 261:       end
 262:     end
 263:   end
 264: 
 265:   if (WidthMultiple == 1) begin : gen_width_one_rd
 266:     // When multiple is 1, just pass the read through directly
 267:     logic unused_word_sel;
 268:     assign data_o = |buf_rsp_match ? buf_rsp_data : data_i;
 269:     assign unused_word_sel = rsp_fifo_rdata.word_sel;
 270: 
 271:   end else begin : gen_rd
 272:     // Re-arrange data into packed array to pick the correct one
 273:     logic [WidthMultiple-1:0][BusWidth-1:0] bus_words_packed;
 274:     assign bus_words_packed = |buf_rsp_match ? buf_rsp_data : data_i;
 275:     assign data_o = bus_words_packed[rsp_fifo_rdata.word_sel];
 276: 
 277:   end
 278: 
 279:   assign data_valid_o = flash_rsp_match | |buf_rsp_match;
 280: 
 281: 
 282:   // the entire read pipeline is idle when there are no responses to return
 283:   assign idle_o = ~rsp_fifo_vld;
 284: 
 285:   /////////////////////////////////
 286:   // Assertions
 287:   /////////////////////////////////
 288: 
 289:   // The buffers are flip flop based, do not allow too many of them
 290:   `ASSERT_INIT(MaxBufs_A, NumBuf <= 8)
 291: 
 292:   // match should happen only to 1 buffer
 293:   `ASSERT(OneHotMatch_A, $onehot0(buf_match))
 294: 
 295:   // allocate should happen only to 1 buffer at time
 296:   `ASSERT(OneHotAlloc_A, $onehot0(alloc))
 297: 
 298:   // update should happen only to 1 buffer at time
 299:   `ASSERT(OneHotUpdate_A, $onehot0(update))
 300: 
 301:   // alloc and update should be mutually exclusive for a buffer
 302:   `ASSERT(ExclusiveOps_A, (alloc & update) == 0 )
 303: 
 304:   // valid and wip are mutually exclusive
 305:   `ASSERT(ExclusiveState_A, (buf_valid & buf_wip) == 0)
 306: 
 307:   // data_hazard and wip should be mutually exclusive
 308:   `ASSERT(ExclusiveProgHazard_A, (data_hazard & buf_wip) == 0)
 309: 
 310:   // unless the pipeline is idle, we should not have non-read trasnactions
 311:   `ASSERT(IdleCheck_A, !idle_o |-> {prog_i,pg_erase_i,bk_erase_i} == '0)
 312: 
 313: 
 314: endmodule // flash_phy_core
 315: