module spi_interface(
input wire clk,
input wire bar_ss,
input wire mosi,
input wire sck,
output wire miso,
output wire spi_ready_write_data,
output wire spi_ready_read_data,
output wire [7:0] spi_register_address,
output wire [31:0] spi_write_data,
input wire [31:0] spi_read_data
);
reg [2:0] sck_r;
initial begin
sck_r <= 3'b000;
end
always @ (posedge clk) begin
sck_r <= {sck_r[1:0], sck};
end
// Detect the rising edge
wire sck_risingedge = (sck_r[2:1]==2'b01);
wire sck_fallingedge = (sck_r[2:1]==2'b10);
//////
// Observe the mosi wire
reg [1:0] mosi_r;
always @ (posedge clk) begin
mosi_r <= {mosi_r[0], mosi};
end
// The mosi data
wire mosi_data = mosi_r[1];
//////
// Observe bar_ss
reg[2:0] bar_ss_r;
initial begin
bar_ss_r <= 3'b111;
end
always @ (posedge clk) begin
bar_ss_r <= {bar_ss_r[1:0], bar_ss};
end
wire bar_ss_fallingedge = (bar_ss_r[2:1]==2'b10);
// Observation of bar_ss
wire bar_ss_observe = bar_ss_r[1];
//////
// Place to store input data
// reg data_ready;
reg [5:0] bit_count;
reg [31:0] data_rx;
reg [31:0] data_tx;
initial begin
bit_count <= 6'd0;
data_tx <= 32'd0;
end
// Output is tristate, until bar_ss_observe becomes 0
assign miso = (!bar_ss_observe) ? data_tx[31] : 1'bz;
reg [3:0] spi_state;
initial begin
spi_state <= 4'd0;
end
reg [7:0] spi_register_address_reg;
reg [31:0] spi_write_data_reg;
initial begin
spi_write_data_reg <= 32'd0;
end
always @(posedge clk) begin
if (bar_ss_fallingedge == 1'b1) begin
// On the falling edge of bar_ss, clear bit count and set spi state
bit_count <= 6'd0;
spi_state <= 4'd0;
end else if ((bar_ss_observe == 1'b0) && (sck_risingedge == 1'b1)) begin
// If we have 7 bits, figure out the next state from the input bit
if (bit_count == 6'd7) begin
if ({data_rx[6:0], mosi_data} == 8'd0) begin
// the master is asking to read a register
spi_state <= 4'd1;
end else if ({data_rx[6:0], mosi_data} == 8'd1) begin
// the master is asking to write a register
spi_state <= 4'd8;
end
end
if (bit_count == 6'd15) begin
// If we are doing a SPI read, and are up to 15 bits total, prepare the register address, ready to send the register back
if (spi_state == 4'd1) begin
spi_register_address_reg <= {data_rx[6:0], mosi_data};
spi_state <= 4'd2;
end else if (spi_state == 4'd8) begin
spi_register_address_reg <= {data_rx[6:0], mosi_data};
spi_state <= 4'd9;
end
end
// Have 31 bits for the register write, so get the mosi data to make 32 bits, set the state to 10
if ((bit_count == 6'd47) && (spi_state == 4'd9)) begin
spi_write_data_reg <= {data_rx[30:0], mosi_data};
spi_state <= 4'd10;
end
bit_count <= bit_count + 6'd1;
data_rx <= {data_rx[30:0], mosi_data};
end else if ((bar_ss_observe == 1'b0) && (sck_fallingedge == 1'b1)) begin
// Transfer the register value to the local register, and change the state
if (spi_state == 4'd2) begin
data_tx <= spi_read_data;
spi_state <= 4'd3;
end else if (spi_state == 4'd10) begin
spi_state <= 4'd0;
bit_count <= 6'd0;
end else begin
if ((spi_state == 4'd3) && (bit_count >= 6'd40)) begin
spi_state <= 4'd0;
bit_count <= 6'd0;
end
data_tx <= {data_tx[30:0], data_tx[31]};
end
end
end
assign spi_ready_read_data = (spi_state == 4'd2) ? 1'b1 : 1'b0;
// When the spi_state is 10, need to write the register value
assign spi_ready_write_data = (spi_state == 4'd10) ? 1'b1 : 1'b0;
assign spi_register_address = spi_register_address_reg;
assign spi_write_data = spi_write_data_reg;
endmodule
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.