`timescale 1ns/1ps module spi_controller_simple ( input clk, input rst_n, input [7:0] data_in, input start, output reg [7:0] data_out, output reg busy, output reg spi_clk, output reg mosi, input miso ); // Simplified FSM: IDLE -> SEND -> SAMPLE -> DONE -> IDLE localparam IDLE = 2'd0; localparam SEND = 2'd1; localparam SAMPLE = 2'd2; localparam DONE = 2'd3; reg [1:0] state; reg [2:0] bit_count; reg [7:0] shift_reg; reg spi_clk_en; // SPI clock generation: derived from main FSM state // spi_clk toggles when in SEND state wire spi_clk_next = spi_clk_en ? ~spi_clk : 1'b0; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin spi_clk <= 1'b0; end else begin spi_clk <= spi_clk_next; end end // Main FSM - single always block drives all registers always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; bit_count <= 3'd0; shift_reg <= 8'd0; data_out <= 8'd0; busy <= 1'b0; mosi <= 1'b0; spi_clk_en <= 1'b0; end else begin case (state) IDLE: begin busy <= 1'b0; mosi <= 1'b0; spi_clk_en <= 1'b0; if (start) begin busy <= 1'b1; shift_reg <= data_in; bit_count <= 3'd7; state <= SEND; spi_clk_en <= 1'b1; end end SEND: begin busy <= 1'b1; mosi <= shift_reg[7]; // Check if spi_clk just transitioned (rising edge detection) if (spi_clk == 1'b0) begin state <= SAMPLE; end end SAMPLE: begin busy <= 1'b1; // Shift in miso shift_reg <= {shift_reg[6:0], miso}; if (bit_count == 3'd0) begin data_out <= {shift_reg[6:0], miso}; state <= DONE; spi_clk_en <= 1'b0; end else begin bit_count <= bit_count - 1'b1; state <= SEND; end end DONE: begin busy <= 1'b0; mosi <= 1'b0; state <= IDLE; spi_clk_en <= 1'b0; end default: begin state <= IDLE; spi_clk_en <= 1'b0; end endcase end end endmodule