Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 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 Controller for life cycle / key management handling |
| 6 | // |
| 7 | |
| 8 | module flash_ctrl_lcmgr import flash_ctrl_pkg::*; ( |
| 9 | input clk_i, |
| 10 | input rst_ni, |
| 11 | |
Timothy Chen | 04834fd | 2020-09-11 14:58:32 -0700 | [diff] [blame] | 12 | // initialization command |
| 13 | input init_i, |
Timothy Chen | a49dc7b | 2020-09-15 17:02:03 -0700 | [diff] [blame^] | 14 | output logic init_done_o, |
Timothy Chen | 04834fd | 2020-09-11 14:58:32 -0700 | [diff] [blame] | 15 | |
| 16 | // only access seeds when provisioned |
| 17 | input provision_en_i, |
| 18 | |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 19 | // 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 Chen | 2df9561 | 2020-08-31 15:29:12 -0700 | [diff] [blame] | 22 | output logic [top_pkg::TL_AW-1:0] addr_o, |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 23 | 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 Chen | 04834fd | 2020-09-11 14:58:32 -0700 | [diff] [blame] | 34 | // This should be simplified to just multi-bit request and multi-bit response |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 35 | 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 Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 46 | // 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 Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 52 | // 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 Chen | 04834fd | 2020-09-11 14:58:32 -0700 | [diff] [blame] | 75 | StIdle, |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 76 | StReadSeeds, |
| 77 | StWait, |
| 78 | StWipeOwner, |
| 79 | StWipeDataPart, |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 80 | StRmaRsp, |
| 81 | StInvalid |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 82 | } state_e; |
| 83 | |
| 84 | state_e state_q, state_d; |
Timothy Chen | a49dc7b | 2020-09-15 17:02:03 -0700 | [diff] [blame^] | 85 | logic init_done_d; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 86 | 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 Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 91 | flash_lcmgr_phase_e phase; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 92 | logic seed_phase; |
| 93 | logic rma_phase; |
| 94 | |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 95 | assign seed_phase = phase == PhaseSeed; |
| 96 | assign rma_phase = phase == PhaseRma; |
| 97 | |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 98 | always_ff @(posedge clk_i or negedge rst_ni) begin |
| 99 | if (!rst_ni) begin |
Timothy Chen | 04834fd | 2020-09-11 14:58:32 -0700 | [diff] [blame] | 100 | state_q <= StIdle; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 101 | validate_q <= 1'b0; |
Timothy Chen | a49dc7b | 2020-09-15 17:02:03 -0700 | [diff] [blame^] | 102 | init_done_o <= 1'b0; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 103 | end else begin |
| 104 | state_q <= state_d; |
| 105 | validate_q <= validate_d; |
Timothy Chen | a49dc7b | 2020-09-15 17:02:03 -0700 | [diff] [blame^] | 106 | init_done_o <= init_done_d; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 107 | 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 Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 161 | 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 Chen | 812f0bd | 2020-09-11 12:09:31 -0700 | [diff] [blame] | 165 | assign owner_page_addr = BusAddrW'({SeedInfoPageSel[OwnerSeedIdx], BusWordW'(0)}); |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 166 | |
| 167 | logic start; |
| 168 | flash_op_e op; |
Timothy Chen | 103b4cb | 2020-09-12 15:31:37 -0700 | [diff] [blame] | 169 | flash_prog_e prog_type; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 170 | 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 Chen | 103b4cb | 2020-09-12 15:31:37 -0700 | [diff] [blame] | 176 | assign prog_type = FlashProgNormal; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 177 | 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 Chen | a49dc7b | 2020-09-15 17:02:03 -0700 | [diff] [blame^] | 182 | // 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 Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 195 | always_comb begin |
| 196 | |
| 197 | // phases of the hardware interface |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 198 | phase = PhaseNone; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 199 | |
| 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 Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 216 | // seed status |
| 217 | seed_err_o = 1'b0; |
| 218 | |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 219 | state_d = state_q; |
| 220 | validate_d = validate_q; |
| 221 | |
Timothy Chen | a49dc7b | 2020-09-15 17:02:03 -0700 | [diff] [blame^] | 222 | // 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 Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 229 | unique case (state_q) |
| 230 | |
Timothy Chen | 04834fd | 2020-09-11 14:58:32 -0700 | [diff] [blame] | 231 | StIdle: begin |
Timothy Chen | a49dc7b | 2020-09-15 17:02:03 -0700 | [diff] [blame^] | 232 | init_done_d = 1'b0; |
Timothy Chen | 04834fd | 2020-09-11 14:58:32 -0700 | [diff] [blame] | 233 | phase = PhaseSeed; |
Timothy Chen | a49dc7b | 2020-09-15 17:02:03 -0700 | [diff] [blame^] | 234 | // provision_en is only a "good" value after otp/lc initialization |
| 235 | if (init_q) begin |
Timothy Chen | 04834fd | 2020-09-11 14:58:32 -0700 | [diff] [blame] | 236 | // 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 Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 242 | // read seeds |
| 243 | StReadSeeds: begin |
| 244 | // seeds can be updated in this state |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 245 | phase = PhaseSeed; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 246 | |
| 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 Chen | c0b2874 | 2020-09-14 12:18:08 -0700 | [diff] [blame] | 263 | // When there are upstream failures, the data returned is simply all 1's. |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 264 | // So instead of doing anything explicit, a status is indicated for software. |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 265 | end else if (done_i) begin |
| 266 | addr_cnt_clr = 1'b1; |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 267 | seed_err_o = 1'b1; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 268 | |
| 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 Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 289 | phase = PhaseRma; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 290 | 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 Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 303 | end else if (done_i && err_i) begin |
| 304 | state_d = StInvalid; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 305 | end |
| 306 | end |
| 307 | |
| 308 | // wipe away data partitions |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 309 | // TBD: Add an alert if not address counts are seen |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 310 | StWipeDataPart: begin |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 311 | phase = PhaseRma; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 312 | 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 Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 339 | phase = PhaseRma; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 340 | rma_rsp_o = 1'b1; |
| 341 | rsp_mask = BusWidth'(StRmaRsp); |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 342 | state_d = StInvalid; |
| 343 | end |
| 344 | |
| 345 | StInvalid: begin |
| 346 | phase = PhaseInvalid; |
| 347 | state_d = StInvalid; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 348 | end |
| 349 | |
| 350 | default:; |
Timothy Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 351 | |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 352 | 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 Chen | 103b4cb | 2020-09-12 15:31:37 -0700 | [diff] [blame] | 359 | assign ctrl_o.prog_sel.q = prog_type; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 360 | 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 Chen | 2df9561 | 2020-08-31 15:29:12 -0700 | [diff] [blame] | 363 | // address is consistent with software width format (full bus) |
| 364 | assign addr_o = top_pkg::TL_AW'({addr, {BusByteWidth{1'b0}}}); |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 365 | 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 Chen | 8204f1d | 2020-09-02 19:05:39 -0700 | [diff] [blame] | 369 | assign phase_o = phase; |
Timothy Chen | 5cdde15 | 2020-08-14 20:46:21 -0700 | [diff] [blame] | 370 | |
| 371 | endmodule // flash_ctrl_lcmgr |