// © 2017 - 2022 Raptor Engineering, LLC // // Released under the terms of the GPL v3 // See the LICENSE file for full details // SPI mode 3 transfer // Single data rate, quad transfer // This module assumes it is on the same clock domain as the external control logic module spi_master_phy_quad( input wire platform_clock, input wire reset, output wire ready, input wire [31:0] tx_data, output reg [31:0] rx_data, input wire [7:0] dummy_cycle_count, input wire hold_ss_active, input wire qspi_mode_active, input wire qspi_transfer_mode, // 0 == byte transfer, 1 == word transfer input wire qspi_transfer_direction, // 0 == read (input), 1 == write (output) input wire cycle_start, output reg transaction_complete, output reg spi_clock, output reg spi_d0_out, input wire spi_d0_in, output reg spi_d1_out, input wire spi_d1_in, output reg spi_d2_out, input wire spi_d2_in, output reg spi_d3_out, input wire spi_d3_in, output reg spi_ss_n, output reg spi_data_direction, // 0 == tristate (input), 1 == driven (output) output reg spi_quad_mode_pin_enable ); reg phy_ready = 0; reg [3:0] transfer_state = 0; reg [31:0] data_shift_out = 0; reg [7:0] state_iteration = 0; reg ss_state_at_idle = 1'b1; reg [7:0] dummy_cycle_count_reg = 0; reg [7:0] dummy_cycle_ctr = 0; reg qspi_transfer_mode_reg = 0; reg [7:0] sspi_transfer_cycle_stop_value = 0; reg [3:0] qspi_transfer_cycle_stop_value = 0; assign ready = phy_ready; always @(posedge platform_clock) begin if (reset) begin phy_ready <= 0; transfer_state <= 0; state_iteration <= 0; transaction_complete <= 1; dummy_cycle_count_reg <= 0; spi_clock <= 1'b1; spi_d0_out <= 1'b0; spi_d1_out <= 1'b1; spi_d2_out <= 1'b1; spi_d3_out <= 1'b1; spi_ss_n <= 1'b1; ss_state_at_idle <= 1'b1; spi_data_direction <= 1'b0; spi_quad_mode_pin_enable <= 1'b0; end else begin case (transfer_state) 0: begin // Idle state spi_clock <= 1'b1; spi_d0_out <= 1'b0; spi_ss_n <= ss_state_at_idle; transaction_complete <= 0; state_iteration <= 0; if (cycle_start) begin // Set up transfer rx_data <= 0; dummy_cycle_count_reg <= dummy_cycle_count; data_shift_out <= tx_data; spi_quad_mode_pin_enable <= qspi_mode_active; spi_data_direction <= qspi_transfer_direction; qspi_transfer_mode_reg <= qspi_transfer_mode; if (qspi_transfer_mode == 0) begin // Byte transfer (2 nibbles per word) qspi_transfer_cycle_stop_value <= 1; sspi_transfer_cycle_stop_value <= 7; end else begin // Word transfer (4 bytes / 8 nibbles per word) qspi_transfer_cycle_stop_value <= 7; sspi_transfer_cycle_stop_value <= 31; end // Drive frame start spi_clock <= 1'b1; spi_ss_n <= 1'b0; transfer_state <= 1; phy_ready <= 0; end else begin if (!hold_ss_active) begin ss_state_at_idle <= 1'b1; end spi_quad_mode_pin_enable <= 0; transfer_state <= 0; phy_ready <= 1; end end 1: begin // Shift out TX byte / toggle clock spi_clock <= 1'b0; spi_ss_n <= 1'b0; if (spi_quad_mode_pin_enable) begin if (qspi_transfer_mode_reg) begin spi_d3_out <= data_shift_out[31]; spi_d2_out <= data_shift_out[30]; spi_d1_out <= data_shift_out[29]; spi_d0_out <= data_shift_out[28]; end else begin spi_d3_out <= data_shift_out[7]; spi_d2_out <= data_shift_out[6]; spi_d1_out <= data_shift_out[5]; spi_d0_out <= data_shift_out[4]; end data_shift_out <= data_shift_out << 4; end else begin if (qspi_transfer_mode_reg) begin spi_d0_out <= data_shift_out[31]; end else begin spi_d0_out <= data_shift_out[7]; end data_shift_out <= data_shift_out << 1; end transfer_state <= 2; end 2: begin // Shift in RX byte / toggle clock spi_clock <= 1'b1; spi_ss_n <= 1'b0; state_iteration <= state_iteration + 1; if (spi_quad_mode_pin_enable) begin if (qspi_transfer_mode_reg) begin rx_data <= {rx_data[27:0], spi_d3_in, spi_d2_in, spi_d1_in, spi_d0_in}; end else begin rx_data <= {rx_data[3:0], spi_d3_in, spi_d2_in, spi_d1_in, spi_d0_in}; end if (state_iteration >= qspi_transfer_cycle_stop_value) begin if (hold_ss_active) begin ss_state_at_idle <= 1'b0; end else begin ss_state_at_idle <= 1'b1; end if (dummy_cycle_count_reg == 0) begin transaction_complete <= 1; transfer_state <= 3; end else begin dummy_cycle_ctr <= 0; transfer_state <= 4; end end else begin transfer_state <= 1; end end else begin if (qspi_transfer_mode_reg) begin rx_data <= {rx_data[30:0], spi_d1_in}; end else begin rx_data <= {rx_data[6:0], spi_d1_in}; end if (state_iteration >= sspi_transfer_cycle_stop_value) begin if (hold_ss_active) begin ss_state_at_idle <= 1'b0; end else begin ss_state_at_idle <= 1'b1; end transaction_complete <= 1; if (dummy_cycle_count_reg == 0) begin transfer_state <= 3; end else begin dummy_cycle_ctr <= 0; transfer_state <= 4; end end else begin transfer_state <= 1; end end end 3: begin // Wait for host to deassert transaction request if (!cycle_start) begin transaction_complete <= 0; transfer_state <= 0; end spi_clock <= 1'b1; spi_d0_out <= 1'b0; spi_d1_out <= 1'b1; spi_d2_out <= 1'b1; spi_d3_out <= 1'b1; spi_ss_n <= ss_state_at_idle; spi_data_direction <= 0; spi_quad_mode_pin_enable <= 0; end 4: begin // Increment counter / toggle clock spi_clock <= 1'b0; dummy_cycle_ctr <= dummy_cycle_ctr + 1; transfer_state <= 5; end 5: begin if (dummy_cycle_ctr < dummy_cycle_count_reg) begin transfer_state <= 4; end else begin transaction_complete <= 1; transfer_state <= 3; end // Toggle clock spi_clock <= 1'b1; end default: begin transfer_state <= 0; end endcase end end endmodule