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