Close

SPI interface verilog HDL code

A project log for Super custom PWM - FPGA

Make a FPGA with lots of PWM ports, all of them 32 bit, and easy to program!

sciencedude1990sciencedude1990 11/02/2020 at 04:010 Comments
// The SPI interface
// Supports loop-back for BER testing
// 8 bit register address
// 32 bit register values
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
);

//////	
// Observe the serial clock
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);
// Detect the falling edge
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

// Detect the falling edge
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;

// Place to store the state variable
// 0 Command arriving
// 1 Read register, address arriving
// 2 Read register, get the register value
// 3 Read register, sending bits 31:0
// 8 Write register, address arriving
// 9 Write register, bits 30:0 arriving
// 10 Write register, bit 31 is ready, write the register
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

// On the rising edge of the serial clock, data is ready for processing
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
				// If we are doing a SPI write, and are up to 15 bits total, prepare the register address, ready to recieve the next 32 bits for the register
				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
		
		// Keep incrementing and sampling bits
		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
		
			// Near the end of register read, the next 8 bits will be a new command
			if ((spi_state == 4'd3) && (bit_count >= 6'd40)) begin
				spi_state <= 4'd0;
				bit_count <= 6'd0;
			end
			
			// Shift the data out			
			data_tx <= {data_tx[30:0], data_tx[31]};
		end
	end
end


// When the spi_state is 2, need the register value for the register read
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;

// The address is stored in the register
assign spi_register_address = spi_register_address_reg;

// The output
assign spi_write_data = spi_write_data_reg;

endmodule

Discussions