| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // SPI Flash Read JEDEC ID handler | 
 |  | 
 | `include "prim_assert.sv" | 
 |  | 
 | module spid_jedec | 
 |   import spi_device_pkg::*; | 
 | ( | 
 |   input clk_i, | 
 |   input rst_ni, | 
 |  | 
 |   input clk_out_i, // Output clock (inverted SCK) | 
 |  | 
 |   input inclk_csb_asserted_pulse_i, | 
 |  | 
 |   input jedec_cfg_t sys_jedec_i, // from CSR | 
 |  | 
 |   output io_mode_e io_mode_o, | 
 |  | 
 |   input sel_datapath_e          sel_dp_i, | 
 |   input cmd_info_t              cmd_info_i, | 
 |   input logic [CmdInfoIdxW-1:0] cmd_info_idx_i, | 
 |  | 
 |   output logic      outclk_p2s_valid_o, | 
 |   output spi_byte_t outclk_p2s_byte_o, | 
 |   input  logic      outclk_p2s_sent_i | 
 | ); | 
 |  | 
 |   typedef enum logic [1:0] { | 
 |     StIdle, | 
 |  | 
 |     // If num_cc is non-zero, State machine moves to CC state and sends CC up | 
 |     // to num_cc times. | 
 |     StCC, | 
 |  | 
 |     // In JedecId state, the logic sends manufacturer ID (jedec.jedec_id) | 
 |     StJedecId, | 
 |  | 
 |     // After sending a byte at StJedecId, the logic returns 2 DevId bytes to | 
 |     // the host system. | 
 |     StDevId | 
 |   } st_e; | 
 |   st_e st_q, st_d; | 
 |  | 
 |   //////////// | 
 |   // Signal // | 
 |   //////////// | 
 |  | 
 |   assign io_mode_o = SingleIO; | 
 |  | 
 |   jedec_cfg_t jedec; | 
 |  | 
 |   logic      p2s_valid; | 
 |   spi_byte_t p2s_byte; | 
 |  | 
 |   logic next_byte; | 
 |   logic [1:0] byte_sel_q, byte_sel_d; | 
 |  | 
 |   // Unused | 
 |   logic unused_cmd_info; | 
 |   assign unused_cmd_info = ^{cmd_info_i , cmd_info_idx_i}; | 
 |  | 
 |   ////////////// | 
 |   // Datapath // | 
 |   ////////////// | 
 |  | 
 |   // Jedec latch | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni)                         jedec <= '{default: '0}; | 
 |     else if (inclk_csb_asserted_pulse_i) jedec <= sys_jedec_i; | 
 |   end | 
 |  | 
 |   // If num_cc is non-zero, the logic shall return CC first | 
 |   logic cc_needed; | 
 |   assign cc_needed = |jedec.num_cc; | 
 |  | 
 |   logic [7:0] cc_count; | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin | 
 |     if (!rst_ni) cc_count <= '0; | 
 |     else if (st_q == StCC && outclk_p2s_sent_i) begin | 
 |       cc_count <= cc_count + 1'b 1; | 
 |     end | 
 |   end | 
 |  | 
 |   // Output to Parallel-to-Serial | 
 |   always_ff @(posedge clk_out_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       outclk_p2s_valid_o <= 1'b 0; | 
 |       outclk_p2s_byte_o  <= 8'h 0; | 
 |     end else begin | 
 |       outclk_p2s_valid_o <= p2s_valid; | 
 |       outclk_p2s_byte_o  <= p2s_byte; | 
 |     end | 
 |   end | 
 |  | 
 |   always_comb begin : p2s_byte_logic | 
 |     p2s_byte = 8'h 0; | 
 |  | 
 |     if (st_q == StIdle) begin | 
 |       // Manufacturer ID always | 
 |       p2s_byte = (cc_needed) ? jedec.cc : jedec.jedec_id; | 
 |     end else if (st_q == StCC) begin | 
 |       p2s_byte = jedec.cc; | 
 |     end else if (st_q == StJedecId) begin | 
 |       p2s_byte = jedec.jedec_id; | 
 |     end else begin | 
 |       // based on byte_sel_q | 
 |       // End of the transfer but host keep toggles SCK. Sending out 0 always | 
 |       p2s_byte = (byte_sel_q >= 2'b 10) ? 8'h 0 : | 
 |                  (byte_sel_q == 2'b 01) ? jedec.device_id[15:8] : | 
 |                                           jedec.device_id[7:0] ; | 
 |     end | 
 |   end : p2s_byte_logic | 
 |  | 
 |   // Byte selection | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin : byte_sel_latch | 
 |     if (!rst_ni)        byte_sel_q <= 8'h 0; // select manufacturer id | 
 |     else if (next_byte) byte_sel_q <= byte_sel_d; | 
 |   end : byte_sel_latch | 
 |  | 
 |   assign byte_sel_d = (byte_sel_q == 2'b 10) ? 2'b 10 : byte_sel_q + 1'b 1; | 
 |  | 
 |   /////////// | 
 |   // State // | 
 |   /////////// | 
 |  | 
 |   always_ff @(posedge clk_i or negedge rst_ni) begin : state_latch | 
 |     if (!rst_ni) st_q <= StIdle; | 
 |     else         st_q <= st_d; | 
 |   end : state_latch | 
 |  | 
 |   `ASSERT_KNOWN(JedecStKnown_A, st_q) | 
 |   always_comb begin : next_state_logic | 
 |     st_d = st_q; | 
 |  | 
 |     p2s_valid = 1'b 0; | 
 |     next_byte = 1'b 0; | 
 |  | 
 |     unique case (st_q) | 
 |       StIdle: begin | 
 |         if (sel_dp_i == DpReadJEDEC) begin | 
 |           st_d = (cc_needed) ? StCC : StJedecId ; | 
 |  | 
 |           // Send out the dat | 
 |           p2s_valid = 1'b 1; | 
 |         end | 
 |       end | 
 |  | 
 |       StCC: begin | 
 |  | 
 |         // cc_count is increased by 1 when outclk_p2s_sent_i is asserted | 
 |         // outclk_p2s_sent_i asserts at the 7th beat, not 8th beat.  When | 
 |         // cc_count reaches to num_cc, it is the end of a byte transaction in | 
 |         // SCK input clock domain. So, that time state machine moves to | 
 |         // JedecId | 
 |         if (cc_count == jedec.num_cc) begin | 
 |           st_d = StJedecId; | 
 |         end | 
 |  | 
 |         p2s_valid = 1'b 1; | 
 |       end | 
 |  | 
 |       StJedecId: begin | 
 |         if (outclk_p2s_sent_i) begin | 
 |           st_d = StDevId; | 
 |         end | 
 |  | 
 |         p2s_valid = 1'b 1; | 
 |       end | 
 |  | 
 |       StDevId: begin | 
 |         // TERMINAL_STATE | 
 |  | 
 |         // Sends data | 
 |         p2s_valid = 1'b 1; | 
 |  | 
 |         if (outclk_p2s_sent_i) begin | 
 |           next_byte = 1'b 1; | 
 |         end | 
 |       end | 
 |  | 
 |       default: begin | 
 |         st_d = StIdle; | 
 |       end | 
 |     endcase | 
 |   end : next_state_logic | 
 |  | 
 | endmodule : spid_jedec |