Verilog Code for SPI Slave: Syntax and Example
An SPI slave in Verilog listens to the SPI clock (
SCLK) and chip select (CS) signals to receive and send data on the MOSI and MISO lines. The code typically uses a shift register triggered on clock edges when CS is active to capture incoming bits and send outgoing bits.Syntax
The SPI slave module usually has inputs for SCLK (clock), CS (chip select), and MOSI (master out slave in), and an output for MISO (master in slave out). Inside, it uses a shift register to receive bits on the rising or falling edge of SCLK when CS is low (active). The module counts bits to know when a full byte is received.
- SCLK: SPI clock from master
- CS: Chip select, active low
- MOSI: Data from master to slave
- MISO: Data from slave to master
- Shift register: Stores incoming/outgoing bits
verilog
module spi_slave(
input wire clk, // System clock
input wire rst_n, // Active low reset
input wire sclk, // SPI clock from master
input wire cs_n, // Chip select, active low
input wire mosi, // Master Out Slave In
output reg miso, // Master In Slave Out
output reg [7:0] data_out, // Received byte
output reg data_ready // Flag when byte received
);
reg [2:0] bit_cnt; // Counts bits received
reg [7:0] shift_reg; // Shift register for input data
reg sclk_prev; // Previous SCLK state for edge detection
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bit_cnt <= 0;
shift_reg <= 0;
data_out <= 0;
data_ready <= 0;
miso <= 0;
sclk_prev <= 0;
end else begin
sclk_prev <= sclk;
data_ready <= 0; // Clear flag by default
if (!cs_n) begin // Active when CS is low
// Detect rising edge of SCLK
if (sclk_prev == 0 && sclk == 1) begin
shift_reg <= {shift_reg[6:0], mosi}; // Shift in MOSI bit
bit_cnt <= bit_cnt + 1;
if (bit_cnt == 7) begin
data_out <= {shift_reg[6:0], mosi};
data_ready <= 1; // Byte received
bit_cnt <= 0;
end
end
// Prepare MISO bit (example: send MSB first)
miso <= shift_reg[7];
end else begin
bit_cnt <= 0;
end
end
end
endmoduleExample
This example shows a simple SPI slave that receives 8 bits from the master on the MOSI line and sets a flag data_ready when a full byte is received. It also outputs the received byte on data_out. The MISO line sends back the most significant bit of the shift register.
verilog
module spi_slave_example(
input wire clk,
input wire rst_n,
input wire sclk,
input wire cs_n,
input wire mosi,
output wire miso,
output wire [7:0] data_out,
output wire data_ready
);
reg [7:0] shift_reg = 8'b0;
reg [2:0] bit_cnt = 3'b0;
reg sclk_prev = 1'b0;
reg data_ready_reg = 1'b0;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
shift_reg <= 8'b0;
bit_cnt <= 3'b0;
data_ready_reg <= 1'b0;
sclk_prev <= 1'b0;
end else begin
sclk_prev <= sclk;
data_ready_reg <= 1'b0;
if (!cs_n) begin
if (sclk_prev == 0 && sclk == 1) begin
shift_reg <= {shift_reg[6:0], mosi};
bit_cnt <= bit_cnt + 1;
if (bit_cnt == 7) begin
data_ready_reg <= 1'b1;
bit_cnt <= 3'b0;
end
end
end else begin
bit_cnt <= 3'b0;
end
end
end
assign miso = shift_reg[7];
assign data_out = shift_reg;
assign data_ready = data_ready_reg;
endmoduleCommon Pitfalls
Common mistakes when writing SPI slave code include:
- Not properly detecting clock edges, causing missed bits.
- Failing to reset bit counters when
CSgoes high, leading to incorrect data framing. - Not handling the
MISOline timing correctly, which can cause data corruption. - Ignoring asynchronous signals and not synchronizing them to the system clock, causing metastability.
Always use edge detection on SCLK and reset counters on CS deassertion.
verilog
/* Wrong approach: sampling MOSI without edge detection */ always @(posedge clk) begin if (!cs_n) begin shift_reg <= {shift_reg[6:0], mosi}; // No clock edge check end end /* Correct approach: sample MOSI on SCLK rising edge */ always @(posedge clk) begin sclk_prev <= sclk; if (!cs_n && sclk_prev == 0 && sclk == 1) begin shift_reg <= {shift_reg[6:0], mosi}; end end
Quick Reference
- SCLK edge detection: Sample data on rising or falling edge as per SPI mode.
- CS active low: Only shift data when
CSis low. - Bit counter: Count bits to know when a full byte is received.
- Shift register: Use to store incoming and outgoing bits.
- MISO timing: Prepare output bit before clock edge.
Key Takeaways
Detect SPI clock edges to correctly sample incoming bits.
Reset bit counters when chip select (
CS) is inactive.Use a shift register to collect bits and form bytes.
Drive
MISO line with the correct bit timing.Synchronize asynchronous inputs to avoid timing issues.