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