SPI Master Controller in Verilog


module spi_master (
    input wire clk,                // System clock
    input wire reset,              // Reset signal
    input wire start,              // Start transmission signal
    input wire [7:0] data_in,      // Data to send
    output reg mosi,               // Master Out Slave In
    input wire miso,               // Master In Slave Out
    output reg sck,                // Serial Clock
    output reg cs,                 // Chip Select
    output reg done                // Transmission done flag
);
    parameter CLK_DIV = 4;         // Clock division factor for SPI clock

    reg [3:0] state;               // State machine state
    reg [3:0] bit_cnt;             // Bit counter
    reg [7:0] shift_reg;           // Shift register for data
    reg [1:0] clk_div_cnt;         // Clock division counter

    // SPI states
    parameter IDLE = 4'b0000,
              START = 4'b0001,
              SEND = 4'b0010,
              RECEIVE = 4'b0011,
              DONE = 4'b0100;

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            state <= IDLE;
            mosi <= 1'b0;          // Idle state for MOSI is low
            sck <= 1'b0;           // Idle state for SCK is low
            cs <= 1'b1;            // Chip select inactive
            done <= 1'b0;
            bit_cnt <= 4'b0;
            clk_div_cnt <= 2'b0;
        end else begin
            clk_div_cnt <= clk_div_cnt + 1;

            if (clk_div_cnt == CLK_DIV - 1) begin
                clk_div_cnt <= 2'b0; // Reset clock division counter

                case (state)
                    IDLE: begin
                        done <= 1'b0;
                        cs <= 1'b1; // Deselect slave
                        if (start) begin
                            state <= START;
                            shift_reg <= data_in; // Load data to shift register
                            bit_cnt <= 4'b0;       // Reset bit counter
                            cs <= 1'b0;            // Select slave
                        end
                    end

                    START: begin
                        state <= SEND; // Move to send state
                    end

                    SEND: begin
                        if (bit_cnt < 8) begin
                            sck <= 1'b0; // Set clock low
                            mosi <= shift_reg[7]; // Send MSB first
                            shift_reg <= {shift_reg[6:0], 1'b0}; // Shift left
                            bit_cnt <= bit_cnt + 1;

                            sck <= 1'b1; // Set clock high to latch data
                        end else begin
                            state <= RECEIVE; // Move to receive state
                        end
                    end

                    RECEIVE: begin
                        if (bit_cnt < 8) begin
                            sck <= 1'b0; // Set clock low
                            // Read MISO line
                            // Assuming the receiver logic is in another part of your design
                            // shift_reg[0] <= miso; // Store the incoming bit (optional)
                            bit_cnt <= bit_cnt + 1;

                            sck <= 1'b1; // Set clock high to latch data
                        end else begin
                            state <= DONE; // Move to done state
                        end
                    end

                    DONE: begin
                        sck <= 1'b0; // Set clock low
                        cs <= 1'b1; // Deselect slave
                        done <= 1'b1; // Indicate transmission done
                        state <= IDLE; // Go back to idle
                    end

                    default: state <= IDLE; // Default to idle
                endcase
            end
        end
    end
endmodule