// © 2017 - 2020 Raptor Engineering, LLC // // Released under the terms of the AGPL v3 // See the LICENSE file for full details // // SPI mode 3 transfer // Single data rate, standard unidirectional transfer module spi_master_interface( input wire platform_clock, input wire reset, input wire [7:0] tx_data, output reg [7:0] rx_data, input wire hold_ss_active, input wire cycle_start, output reg transaction_complete, output reg spi_clock, output reg spi_mosi, input wire spi_miso, output reg spi_ss_n, output reg spi_hold_n, output reg spi_wp_n ); reg [3:0] transfer_state = 0; reg [7:0] data_shift_out = 0; reg [7:0] state_iteration = 0; reg ss_state_at_idle = 1'b1; reg cycle_start_reg = 0; reg hold_ss_active_reg = 0; reg [7:0] tx_data_reg = 0; always @(posedge platform_clock) begin if (reset) begin transfer_state <= 0; state_iteration <= 0; transaction_complete <= 1; spi_clock <= 1'b1; spi_mosi <= 1'b0; spi_ss_n <= 1'b1; spi_hold_n <= 1'b1; spi_wp_n <= 1'b1; ss_state_at_idle <= 1'b1; end else begin case (transfer_state) 0: begin // Idle state spi_clock <= 1'b1; spi_mosi <= 1'b0; spi_ss_n <= ss_state_at_idle; transaction_complete <= 0; state_iteration <= 0; if (cycle_start_reg) begin // Set up transfer rx_data <= 0; data_shift_out <= tx_data_reg; // Drive frame start spi_clock <= 1'b1; spi_ss_n <= 1'b0; transfer_state <= 1; end else begin if (!hold_ss_active_reg) begin ss_state_at_idle <= 1'b1; end transfer_state <= 0; end end 1: begin // Shift out TX byte / toggle clock spi_clock <= 1'b0; spi_ss_n <= 1'b0; spi_mosi <= data_shift_out[7]; data_shift_out <= data_shift_out << 1; transfer_state <= 2; end 2: begin // Shift in RX byte / toggle clock spi_clock <= 1'b1; spi_ss_n <= 1'b0; rx_data <= {rx_data[6:0], spi_miso}; state_iteration <= state_iteration + 1; if (state_iteration >= 7) begin if (hold_ss_active_reg) begin ss_state_at_idle <= 1'b0; end else begin ss_state_at_idle <= 1'b1; end transaction_complete <= 1; transfer_state <= 3; end else begin transfer_state <= 1; end end 3: begin // Wait for host to deassert transaction request if (!cycle_start_reg) begin transaction_complete <= 0; transfer_state <= 0; end spi_clock <= 1'b1; spi_mosi <= 1'b0; spi_ss_n <= ss_state_at_idle; end default: begin transfer_state <= 0; end endcase end // Avoid glitches from signals crossing clock domains cycle_start_reg <= cycle_start; hold_ss_active_reg = hold_ss_active; tx_data_reg <= tx_data; end endmodule // 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_interface_quad( input wire platform_clock, input wire reset, 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 [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 [3:0] qspi_transfer_cycle_stop_value = 1; always @(posedge platform_clock) begin if (reset) begin 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; end else begin // Word transfer (4 bytes / 8 nibbles per word) qspi_transfer_cycle_stop_value <= 7; end // Drive frame start spi_clock <= 1'b1; spi_ss_n <= 1'b0; transfer_state <= 1; end else begin if (!hold_ss_active) begin ss_state_at_idle <= 1'b1; end spi_quad_mode_pin_enable <= 0; transfer_state <= 0; 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 spi_d0_out <= data_shift_out[7]; 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 rx_data <= {rx_data[6:0], spi_d1_in}; if (state_iteration >= 7) 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