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