strix/fpga/rtl/spi_slave.v

69 lines
2.4 KiB
Verilog

// SPI mode 0 slave, oversampled in the system clock domain (SCK must stay below about a
// tenth of clk). MISO changes a few clk cycles after each SCK falling edge, which leaves
// enough margin for the master's next rising edge at 8 MHz SCK with a 120 MHz clk.
module spi_slave (
input wire clk,
input wire reset,
input wire sck,
input wire mosi,
input wire ss_n,
output wire miso,
output wire selected, // ss_n asserted (synchronized)
output reg rx_valid, // pulse: rx_byte holds a complete byte
output reg [7:0] rx_byte,
output reg rx_is_first, // rx_byte is the first byte of the transaction
input wire [7:0] tx_byte, // sampled when the next byte starts shifting out
output reg tx_loaded // pulse: tx_byte was taken
);
reg [2:0] sck_history, ss_n_history;
reg [1:0] mosi_history;
always @(posedge clk) begin
sck_history <= {sck_history[1:0], sck};
ss_n_history <= {ss_n_history[1:0], ss_n};
mosi_history <= {mosi_history[0], mosi};
end
wire sck_rising = (sck_history[2:1] == 2'b01);
wire sck_falling = (sck_history[2:1] == 2'b10);
wire mosi_bit = mosi_history[1];
assign selected = ~ss_n_history[1];
reg [2:0] bit_count;
reg [6:0] rx_shift;
reg [7:0] tx_shift;
reg expecting_first;
assign miso = tx_shift[7];
always @(posedge clk) begin
rx_valid <= 1'b0;
tx_loaded <= 1'b0;
if (reset || !selected) begin
bit_count <= 3'd0;
expecting_first <= 1'b1;
tx_shift <= 8'h00; // MISO is don't-care during the command byte
end else begin
if (sck_rising) begin
rx_shift <= {rx_shift[5:0], mosi_bit};
bit_count <= bit_count + 3'd1;
if (bit_count == 3'd7) begin
rx_valid <= 1'b1;
rx_byte <= {rx_shift, mosi_bit};
rx_is_first <= expecting_first;
expecting_first <= 1'b0;
end
end
if (sck_falling) begin
if (bit_count == 3'd0) begin
tx_shift <= tx_byte;
tx_loaded <= 1'b1;
end else begin
tx_shift <= {tx_shift[6:0], 1'b0};
end
end
end
end
endmodule