`timescale 1ns/1ps module spi_controller ( input clk, input rst_n, input [7:0] data_in, input start, input cpol, input cpha, output reg [7:0] data_out, output reg busy, output reg spi_clk, output reg mosi, input miso ); localparam IDLE = 3'd0; localparam LOAD = 3'd1; localparam LEAD = 3'd2; localparam SAMPLE = 3'd3; localparam SHIFT = 3'd4; localparam DONE = 3'd5; localparam ERROR = 3'd6; reg [2:0] state; reg [2:0] bit_count; reg [7:0] tx_shift; reg [7:0] rx_shift; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; bit_count <= 3'd0; tx_shift <= 8'd0; rx_shift <= 8'd0; data_out <= 8'd0; busy <= 1'b0; spi_clk <= 1'b0; mosi <= 1'b0; end else begin case (state) IDLE: begin busy <= 1'b0; spi_clk <= cpol; mosi <= 1'b0; if (start) begin busy <= 1'b1; bit_count <= 3'd7; tx_shift <= data_in; rx_shift <= 8'd0; state <= LOAD; end end LOAD: begin busy <= 1'b1; spi_clk <= cpol; mosi <= tx_shift[7]; if (start) begin state <= ERROR; end else begin state <= LEAD; end end LEAD: begin busy <= 1'b1; spi_clk <= ~cpol; if (start) begin state <= ERROR; end else if (!cpha) begin rx_shift <= {rx_shift[6:0], miso}; if (bit_count == 3'd0) begin data_out <= {rx_shift[6:0], miso}; state <= DONE; end else begin state <= SHIFT; end end else begin state <= SAMPLE; end end SAMPLE: begin busy <= 1'b1; spi_clk <= ~cpol; if (start) begin state <= ERROR; end else begin rx_shift <= {rx_shift[6:0], miso}; if (bit_count == 3'd0) begin data_out <= {rx_shift[6:0], miso}; state <= DONE; end else begin state <= SHIFT; end end end SHIFT: begin busy <= 1'b1; spi_clk <= cpol; tx_shift <= {tx_shift[6:0], 1'b0}; bit_count <= bit_count - 1'b1; mosi <= tx_shift[6]; if (start) begin state <= ERROR; end else begin state <= LEAD; end end DONE: begin busy <= 1'b0; spi_clk <= cpol; mosi <= 1'b0; if (start) begin state <= ERROR; end else begin state <= IDLE; end end default: begin busy <= 1'b0; spi_clk <= cpol; mosi <= 1'b0; if (!start) begin state <= IDLE; end end endcase end end endmodule