blob: cd321917e62d079779b482c0381e46250f235b29 [file] [log] [blame]
Timothy Chen5cdde152020-08-14 20:46:21 -07001// 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 Controller for life cycle / key management handling
6//
7
8module flash_ctrl_lcmgr import flash_ctrl_pkg::*; (
9 input clk_i,
10 input rst_ni,
11
Timothy Chen04834fd2020-09-11 14:58:32 -070012 // initialization command
13 input init_i,
Timothy Chena49dc7b2020-09-15 17:02:03 -070014 output logic init_done_o,
Timothy Chen04834fd2020-09-11 14:58:32 -070015
16 // only access seeds when provisioned
17 input provision_en_i,
18
Timothy Chen5cdde152020-08-14 20:46:21 -070019 // interface to ctrl arb control ports
20 output flash_ctrl_reg_pkg::flash_ctrl_reg2hw_control_reg_t ctrl_o,
21 output logic req_o,
Timothy Chen2df95612020-08-31 15:29:12 -070022 output logic [top_pkg::TL_AW-1:0] addr_o,
Timothy Chen5cdde152020-08-14 20:46:21 -070023 input done_i,
24 input err_i,
25
26 // interface to ctrl_arb data ports
27 output logic rready_o,
28 input rvalid_i,
29
30 // direct form rd_fifo
31 input [BusWidth-1:0] rdata_i,
32
33 // external rma request
Timothy Chen04834fd2020-09-11 14:58:32 -070034 // This should be simplified to just multi-bit request and multi-bit response
Timothy Chen5cdde152020-08-14 20:46:21 -070035 input rma_i,
36 input [BusWidth-1:0] rma_token_i, // just a random string
37 output logic [BusWidth-1:0] rma_token_o,
38 output logic rma_rsp_o,
39
40 // random value
41 input [BusWidth-1:0] rand_i,
42
43 // seeds to the outside world,
44 output logic [NumSeeds-1:0][SeedWidth-1:0] seeds_o,
45
Timothy Chen8204f1d2020-09-02 19:05:39 -070046 // indicate to memory protection what phase the hw interface is in
47 output flash_lcmgr_phase_e phase_o,
48
49 // error status to registers
50 output logic seed_err_o,
51
Timothy Chen5cdde152020-08-14 20:46:21 -070052 // init ongoing
53 output logic init_busy_o
54);
55
56 // total number of pages to be wiped during RMA entry
57 localparam int TotalDataPages = NumBanks * PagesPerBank;
58 localparam int CntWidth = $clog2(TotalDataPages + 1);
59
60 // seed related local params
61 localparam int SeedReads = SeedWidth / BusWidth;
62 localparam int SeedRdsWidth = $clog2(SeedReads);
63 localparam int SeedCntWidth = $clog2(NumSeeds+1);
64 localparam int NumSeedWidth = $clog2(NumSeeds);
65
66 // the various seed outputs
67 logic [NumSeeds-1:0][SeedReads-1:0][BusWidth-1:0] seeds_q;
68
69 // rma related functions
70 logic [1:0][BusWidth-1:0] rsp_token;
71
72 // progress through and read out the various pieces of content
73 // This FSM should become sparse, especially for StRmaRsp
74 typedef enum logic [3:0] {
Timothy Chen04834fd2020-09-11 14:58:32 -070075 StIdle,
Timothy Chen5cdde152020-08-14 20:46:21 -070076 StReadSeeds,
77 StWait,
78 StWipeOwner,
79 StWipeDataPart,
Timothy Chen8204f1d2020-09-02 19:05:39 -070080 StRmaRsp,
81 StInvalid
Timothy Chen5cdde152020-08-14 20:46:21 -070082 } state_e;
83
84 state_e state_q, state_d;
Timothy Chena49dc7b2020-09-15 17:02:03 -070085 logic init_done_d;
Timothy Chen5cdde152020-08-14 20:46:21 -070086 logic validate_q, validate_d;
87 logic [SeedCntWidth-1:0] seed_cnt_q;
88 logic [CntWidth-1:0] addr_cnt_q;
89 logic seed_cnt_en, seed_cnt_clr;
90 logic addr_cnt_en, addr_cnt_clr;
Timothy Chen8204f1d2020-09-02 19:05:39 -070091 flash_lcmgr_phase_e phase;
Timothy Chen5cdde152020-08-14 20:46:21 -070092 logic seed_phase;
93 logic rma_phase;
94
Timothy Chen8204f1d2020-09-02 19:05:39 -070095 assign seed_phase = phase == PhaseSeed;
96 assign rma_phase = phase == PhaseRma;
97
Timothy Chen5cdde152020-08-14 20:46:21 -070098 always_ff @(posedge clk_i or negedge rst_ni) begin
99 if (!rst_ni) begin
Timothy Chen04834fd2020-09-11 14:58:32 -0700100 state_q <= StIdle;
Timothy Chen5cdde152020-08-14 20:46:21 -0700101 validate_q <= 1'b0;
Timothy Chena49dc7b2020-09-15 17:02:03 -0700102 init_done_o <= 1'b0;
Timothy Chen5cdde152020-08-14 20:46:21 -0700103 end else begin
104 state_q <= state_d;
105 validate_q <= validate_d;
Timothy Chena49dc7b2020-09-15 17:02:03 -0700106 init_done_o <= init_done_d;
Timothy Chen5cdde152020-08-14 20:46:21 -0700107 end
108 end
109
110 // seed cnt tracks which seed round we are handling at the moment
111 always_ff @(posedge clk_i or negedge rst_ni) begin
112 if (!rst_ni) begin
113 seed_cnt_q <= '0;
114 end else if (seed_cnt_clr) begin
115 seed_cnt_q <= '0;
116 end else if (seed_cnt_en) begin
117 seed_cnt_q <= seed_cnt_q + 1'b1;
118 end
119 end
120
121 // addr cnt tracks how far we are in an address looop
122 always_ff @(posedge clk_i or negedge rst_ni) begin
123 if (!rst_ni) begin
124 addr_cnt_q <= '0;
125 end else if (addr_cnt_clr) begin
126 addr_cnt_q <= '0;
127 end else if (addr_cnt_en) begin
128 addr_cnt_q <= addr_cnt_q + 1'b1;
129 end
130 end
131
132 // capture the seed values
133 logic [SeedRdsWidth-1:0] rd_idx;
134 logic [NumSeedWidth-1:0] seed_idx;
135 assign rd_idx = addr_cnt_q[SeedRdsWidth-1:0];
136 assign seed_idx = seed_cnt_q[NumSeedWidth-1:0];
137 always_ff @(posedge clk_i) begin
138 // validate current value
139 if (seed_phase && validate_q && rvalid_i) begin
140 seeds_q[seed_idx][rd_idx] <= seeds_q[seed_idx][rd_idx] &
141 rdata_i;
142 end else if (seed_phase && rvalid_i) begin
143 seeds_q[seed_idx][rd_idx] <= rdata_i;
144 end
145 end
146
147 // capture the random token for return
148 // store token in 2-shares and continuously xor in data
149 always_ff @(posedge clk_i or negedge rst_ni) begin
150 if (!rst_ni) begin
151 rsp_token <= '0;
152 end else if (rma_i) begin
153 rsp_token[0] <= rma_token_i ^ rand_i ^ BusWidth'(StRmaRsp);
154 rsp_token[1] <= rand_i;
155 // token can be changed during validation portion of the rma phase
156 end else if (rma_phase && validate_q && rvalid_i) begin
157 rsp_token <= rsp_token ^ {rdata_i, rdata_i};
158 end
159 end
160
Timothy Chen5cdde152020-08-14 20:46:21 -0700161 logic [BusAddrW-1:0] seed_page_addr;
162 assign seed_page_addr = BusAddrW'({SeedInfoPageSel[seed_idx], BusWordW'(0)});
163
164 logic [BusAddrW-1:0] owner_page_addr;
Timothy Chen812f0bd2020-09-11 12:09:31 -0700165 assign owner_page_addr = BusAddrW'({SeedInfoPageSel[OwnerSeedIdx], BusWordW'(0)});
Timothy Chen5cdde152020-08-14 20:46:21 -0700166
167 logic start;
168 flash_op_e op;
Timothy Chen103b4cb2020-09-12 15:31:37 -0700169 flash_prog_e prog_type;
Timothy Chen5cdde152020-08-14 20:46:21 -0700170 flash_erase_e erase_type;
171 flash_part_e part_sel;
172 logic [11:0] num_words;
173 logic [BusAddrW-1:0] addr;
174 logic [BusWidth-1:0] rsp_mask;
175
Timothy Chen103b4cb2020-09-12 15:31:37 -0700176 assign prog_type = FlashProgNormal;
Timothy Chen5cdde152020-08-14 20:46:21 -0700177 assign erase_type = FlashErasePage;
178 // seed phase is always read
179 // rma phase is erase unless we are validating
180 assign op = seed_phase || validate_q ? FlashOpRead : FlashOpErase;
181
Timothy Chena49dc7b2020-09-15 17:02:03 -0700182 // synchronize inputs
183 logic init_q;
184
185 prim_flop_2sync #(
186 .Width(1),
187 .ResetValue(0)
188 ) u_sync_flash_init (
189 .clk_i,
190 .rst_ni,
191 .d_i(init_i),
192 .q_o(init_q)
193 );
194
Timothy Chen5cdde152020-08-14 20:46:21 -0700195 always_comb begin
196
197 // phases of the hardware interface
Timothy Chen8204f1d2020-09-02 19:05:39 -0700198 phase = PhaseNone;
Timothy Chen5cdde152020-08-14 20:46:21 -0700199
200 // timer controls
201 seed_cnt_en = 1'b0;
202 seed_cnt_clr = 1'b0;
203 addr_cnt_en = 1'b0;
204 addr_cnt_clr = 1'b0;
205
206 // flash ctrrl arb controls
207 start = 1'b0;
208 addr = '0;
209 part_sel = FlashPartInfo;
210 num_words = SeedReads - 1'b1;
211
212 // rma responses
213 rma_rsp_o = 1'b0;
214 rsp_mask = {BusWidth{1'b1}};
215
Timothy Chen8204f1d2020-09-02 19:05:39 -0700216 // seed status
217 seed_err_o = 1'b0;
218
Timothy Chen5cdde152020-08-14 20:46:21 -0700219 state_d = state_q;
220 validate_d = validate_q;
221
Timothy Chena49dc7b2020-09-15 17:02:03 -0700222 // init status
223 // flash_ctrl handles its own arbitration between hardware and software.
224 // So once the init kicks off it is safe to ack. The done signal is still
225 // to give a chance to hold off further power up progression in the future
226 // if required.
227 init_done_d = 1'b1;
228
Timothy Chen5cdde152020-08-14 20:46:21 -0700229 unique case (state_q)
230
Timothy Chen04834fd2020-09-11 14:58:32 -0700231 StIdle: begin
Timothy Chena49dc7b2020-09-15 17:02:03 -0700232 init_done_d = 1'b0;
Timothy Chen04834fd2020-09-11 14:58:32 -0700233 phase = PhaseSeed;
Timothy Chena49dc7b2020-09-15 17:02:03 -0700234 // provision_en is only a "good" value after otp/lc initialization
235 if (init_q) begin
Timothy Chen04834fd2020-09-11 14:58:32 -0700236 // if provisioning is not enabled, do not read seeds and skip directly
237 // to wait state.
238 state_d = provision_en_i ? StReadSeeds : StWait;
239 end
240 end
241
Timothy Chen5cdde152020-08-14 20:46:21 -0700242 // read seeds
243 StReadSeeds: begin
244 // seeds can be updated in this state
Timothy Chen8204f1d2020-09-02 19:05:39 -0700245 phase = PhaseSeed;
Timothy Chen5cdde152020-08-14 20:46:21 -0700246
247 // kick off flash transaction
248 start = 1'b1;
249 addr = BusAddrW'(seed_page_addr);
250
251 // we have checked all seeds, proceed
252 if (seed_cnt_q == NumSeeds) begin
253 start = 1'b0;
254 state_d = StWait;
255
256 // still reading curent seed, increment whenever data returns
257 end else if (!done_i) begin
258 addr_cnt_en = rvalid_i;
259
260 // current seed reading is complete
261 // error is intentionally not used here, as we do not want read seed
262 // failures to stop the software from using flash
Timothy Chenc0b28742020-09-14 12:18:08 -0700263 // When there are upstream failures, the data returned is simply all 1's.
Timothy Chen8204f1d2020-09-02 19:05:39 -0700264 // So instead of doing anything explicit, a status is indicated for software.
Timothy Chen5cdde152020-08-14 20:46:21 -0700265 end else if (done_i) begin
266 addr_cnt_clr = 1'b1;
Timothy Chen8204f1d2020-09-02 19:05:39 -0700267 seed_err_o = 1'b1;
Timothy Chen5cdde152020-08-14 20:46:21 -0700268
269 // we move to the next seed only if current seed is read and validated
270 // if not, flip to validate phase and read seed again
271 if (validate_q) begin
272 seed_cnt_en = 1'b1;
273 validate_d = 1'b0;
274 end else begin
275 validate_d = 1'b1;
276 end
277 end
278 end
279
280 // Waiting for an rma entry command
281 StWait: begin
282 if (rma_i) begin
283 state_d = StWipeOwner;
284 end
285 end
286
287 // wipe away owner seed partition
288 StWipeOwner: begin
Timothy Chen8204f1d2020-09-02 19:05:39 -0700289 phase = PhaseRma;
Timothy Chen5cdde152020-08-14 20:46:21 -0700290 start = 1'b1;
291 addr = BusAddrW'(owner_page_addr);
292 num_words = BusWordsPerPage - 1'b1;
293
294 if (done_i && !err_i) begin
295 // if validate flag is set, erase and validation completed, proceed
296 // if validate flag not set, begin validation
297 if (validate_q) begin
298 validate_d = 1'b0;
299 state_d = StWipeDataPart;
300 end else begin
301 validate_d = 1'b1;
302 end
Timothy Chen8204f1d2020-09-02 19:05:39 -0700303 end else if (done_i && err_i) begin
304 state_d = StInvalid;
Timothy Chen5cdde152020-08-14 20:46:21 -0700305 end
306 end
307
308 // wipe away data partitions
Timothy Chen8204f1d2020-09-02 19:05:39 -0700309 // TBD: Add an alert if not address counts are seen
Timothy Chen5cdde152020-08-14 20:46:21 -0700310 StWipeDataPart: begin
Timothy Chen8204f1d2020-09-02 19:05:39 -0700311 phase = PhaseRma;
Timothy Chen5cdde152020-08-14 20:46:21 -0700312 part_sel = FlashPartData;
313 start = 1'b1;
314 addr = BusAddrW'({addr_cnt_q, BusWordW'(0)});
315 num_words = BusWordsPerPage - 1'b1;
316
317 // reached the final page
318 if (addr_cnt_q == TotalDataPages) begin
319 start = 1'b0;
320 addr_cnt_clr = 1'b1;
321
322 // completed wiping and validation, proceed
323 if (validate_q) begin
324 state_d = StRmaRsp;
325 validate_d = 1'b0;
326 // completed wiping, begin validation
327 end else begin
328 validate_d = 1'b1;
329 end
330
331 // still working through erasing / validating pages
332 end else if (done_i && !err_i) begin
333 addr_cnt_en = 1'b1;
334 end
335 end
336
337 // response to rma request
338 StRmaRsp: begin
Timothy Chen8204f1d2020-09-02 19:05:39 -0700339 phase = PhaseRma;
Timothy Chen5cdde152020-08-14 20:46:21 -0700340 rma_rsp_o = 1'b1;
341 rsp_mask = BusWidth'(StRmaRsp);
Timothy Chen8204f1d2020-09-02 19:05:39 -0700342 state_d = StInvalid;
343 end
344
345 StInvalid: begin
346 phase = PhaseInvalid;
347 state_d = StInvalid;
Timothy Chen5cdde152020-08-14 20:46:21 -0700348 end
349
350 default:;
Timothy Chen8204f1d2020-09-02 19:05:39 -0700351
Timothy Chen5cdde152020-08-14 20:46:21 -0700352 endcase // unique case (state_q)
353
354 end // always_comb
355
356 assign rma_token_o = rsp_token[0] ^ rsp_token[1] ^ rsp_mask;
357 assign ctrl_o.start.q = start;
358 assign ctrl_o.op.q = op;
Timothy Chen103b4cb2020-09-12 15:31:37 -0700359 assign ctrl_o.prog_sel.q = prog_type;
Timothy Chen5cdde152020-08-14 20:46:21 -0700360 assign ctrl_o.erase_sel.q = erase_type;
361 assign ctrl_o.partition_sel.q = part_sel;
362 assign ctrl_o.num = num_words;
Timothy Chen2df95612020-08-31 15:29:12 -0700363 // address is consistent with software width format (full bus)
364 assign addr_o = top_pkg::TL_AW'({addr, {BusByteWidth{1'b0}}});
Timothy Chen5cdde152020-08-14 20:46:21 -0700365 assign init_busy_o = seed_phase;
366 assign req_o = seed_phase | rma_phase;
367 assign rready_o = 1'b1;
368 assign seeds_o = seeds_q;
Timothy Chen8204f1d2020-09-02 19:05:39 -0700369 assign phase_o = phase;
Timothy Chen5cdde152020-08-14 20:46:21 -0700370
371endmodule // flash_ctrl_lcmgr