blob: e96e1db507f8240ea02fb55a50bbfb664e5670d7 [file] [log] [blame]
// 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