| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // Faux Flash Prog Control | 
 | // | 
 |  | 
 | module flash_ctrl_prog import flash_ctrl_pkg::*; ( | 
 |   input clk_i, | 
 |   input rst_ni, | 
 |  | 
 |   // Control Interface | 
 |   input                    op_start_i, | 
 |   input  [11:0]            op_num_words_i, | 
 |   output logic             op_done_o, | 
 |   output flash_ctrl_err_t  op_err_o, | 
 |   input [BusAddrW-1:0]     op_addr_i, | 
 |   input                    op_addr_oob_i, | 
 |   input flash_prog_e       op_type_i, | 
 |   input [ProgTypes-1:0]    type_avail_i, | 
 |   output logic [BusAddrW-1:0] op_err_addr_o, | 
 |   output logic             cnt_err_o, | 
 |  | 
 |   // FIFO Interface | 
 |   input                    data_rdy_i, | 
 |   input  [BusFullWidth-1:0] data_i, | 
 |   output logic             data_rd_o, | 
 |  | 
 |   // Flash Macro Interface | 
 |   output logic             flash_req_o, | 
 |   output logic [BusAddrW-1:0] flash_addr_o, | 
 |   output logic             flash_ovfl_o, | 
 |   output logic [BusFullWidth-1:0] flash_data_o, | 
 |   output logic             flash_last_o, // last beat of prog data | 
 |   output flash_prog_e      flash_type_o, | 
 |   input                    flash_done_i, | 
 |   input                    flash_macro_err_i, | 
 |   input                    flash_mp_err_i, | 
 |   input                    flash_prog_intg_err_i | 
 | ); | 
 |  | 
 |   typedef enum logic { | 
 |     StNorm  = 'h0, | 
 |     StErr   = 'h1 | 
 |   } state_e; | 
 |  | 
 |   state_e st_q, st_d; | 
 |   logic [11:0] cnt; | 
 |   logic cnt_hit; | 
 |   logic [BusAddrW:0] int_addr; | 
 |   logic txn_done; | 
 |  | 
 |   flash_ctrl_err_t op_err_q, op_err_d; | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       st_q <= StNorm; | 
 |     end else begin | 
 |       st_q <= st_d; | 
 |     end | 
 |   end | 
 |  | 
 |   //always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |   //  if (!rst_ni) begin | 
 |   //    cnt <= '0; | 
 |   //  end else if (op_start_i && op_done_o) begin | 
 |   //    cnt <= '0; | 
 |   //  end else if (data_rd_o) begin | 
 |   //    cnt <= cnt + 1'b1; | 
 |   //  end | 
 |   //end | 
 |  | 
 |   prim_count #( | 
 |     .Width(12), | 
 |     .OutSelDnCnt(0), | 
 |     .CntStyle(prim_count_pkg::DupCnt) | 
 |   ) u_cnt ( | 
 |     .clk_i, | 
 |     .rst_ni, | 
 |     .clr_i(op_start_i && op_done_o), | 
 |     .set_i('0), | 
 |     .set_cnt_i('0), | 
 |     .en_i(data_rd_o), | 
 |     .step_i(12'h1), | 
 |     .cnt_o(cnt), | 
 |     .err_o(cnt_err_o) | 
 |   ); | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       op_err_addr_o <= '0; | 
 |     end else if (~|op_err_q && |op_err_d) begin | 
 |       op_err_addr_o <= flash_addr_o; | 
 |     end | 
 |   end | 
 |  | 
 |   assign txn_done = flash_req_o && flash_done_i; | 
 |   assign cnt_hit = (cnt >= op_num_words_i); | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       op_err_q <= '0; | 
 |     end else if (op_start_i && op_done_o) begin | 
 |       op_err_q <= '0; | 
 |     end else begin | 
 |       op_err_q <= op_err_d; | 
 |     end | 
 |   end | 
 |  | 
 |   // if the requested prog type is available | 
 |   logic prog_type_avail; | 
 |   assign prog_type_avail = type_avail_i[op_type_i]; | 
 |  | 
 |   // program resolution check | 
 |   // if the incoming beat is larger than the maximum program resolution, error | 
 |   // immediately and do not allow it to start. | 
 |   localparam int WindowWidth = BusAddrW - BusPgmResWidth; | 
 |   logic [WindowWidth-1:0] start_window, end_window; | 
 |   logic [BusAddrW-1:0] end_addr; | 
 |   logic pgm_res_err; | 
 |   logic win_err; | 
 |   assign end_addr = op_addr_i + BusAddrW'(op_num_words_i); | 
 |   assign start_window = op_addr_i[BusAddrW-1:BusPgmResWidth]; | 
 |   assign end_window = end_addr[BusAddrW-1:BusPgmResWidth]; | 
 |   assign pgm_res_err = start_window != end_window; | 
 |   assign win_err = pgm_res_err | op_addr_oob_i; | 
 |  | 
 |   // when error'd, continue to drain all program fifo contents like normal operation | 
 |   // if this is not done, software may fill up the fifo without anyone | 
 |   // draining the contents, leading to a lockup | 
 |   always_comb begin | 
 |     st_d = st_q; | 
 |     flash_req_o = 1'b0; | 
 |     data_rd_o = 1'b0; | 
 |     op_done_o = 1'b0; | 
 |     op_err_d = op_err_q; | 
 |  | 
 |     unique case (st_q) | 
 |  | 
 |       // Note the address counter is incremented on tx_done | 
 |       // and cleared when the entire operation is complete. | 
 |       StNorm: begin | 
 |  | 
 |         if (cnt_err_o) begin | 
 |           // if count error'd don't bother doing anything else, just try to finish | 
 |           st_d = StErr; | 
 |  | 
 |         end else if (op_start_i && prog_type_avail && !win_err) begin | 
 |           // if the select operation type is not available, error | 
 |           flash_req_o = data_rdy_i; | 
 |  | 
 |           if (txn_done) begin | 
 |             op_err_d.mp_err = flash_mp_err_i; | 
 |             op_err_d.macro_err = flash_macro_err_i; | 
 |             op_err_d.prog_err = flash_prog_intg_err_i; | 
 |             data_rd_o = 1'b1; | 
 |  | 
 |             if (cnt_hit) begin | 
 |               op_done_o = 1'b1; | 
 |             end else begin | 
 |               st_d = |op_err_d ? StErr : StNorm; | 
 |             end | 
 |           end | 
 |  | 
 |         end else if (op_start_i && (!prog_type_avail || win_err)) begin | 
 |           op_err_d.oob_err = op_addr_oob_i; | 
 |           op_err_d.prog_type_err = !prog_type_avail; | 
 |           op_err_d.prog_win_err = pgm_res_err; | 
 |           st_d = StErr; | 
 |         end | 
 |       end | 
 |       StErr: begin | 
 |         data_rd_o = data_rdy_i; | 
 |  | 
 |         if (data_rdy_i && cnt_hit) begin | 
 |           st_d = StNorm; | 
 |           op_done_o = 1'b1; | 
 |         end | 
 |       end | 
 |       default:; | 
 |     endcase // unique case (st) | 
 |   end | 
 |  | 
 |   assign flash_data_o = data_i; | 
 |   assign int_addr = op_addr_i + BusAddrW'(cnt); | 
 |   assign flash_addr_o = int_addr[0 +: BusAddrW]; | 
 |   assign flash_ovfl_o = int_addr[BusAddrW]; | 
 |   assign flash_last_o = flash_req_o & cnt_hit; | 
 |   assign flash_type_o = op_type_i; | 
 |   assign op_err_o = op_err_q | op_err_d; | 
 |  | 
 |   // unused signals | 
 |   logic [BusPgmResWidth-1:0] unused_end_addr; | 
 |   assign unused_end_addr = end_addr[BusPgmResWidth-1:0]; | 
 |  | 
 | endmodule // flash_ctrl_prog |