SPI协议

协议简介

https://www.cnblogs.com/liujinggang/p/9609739.html
https://www.cnblogs.com/deng-tao/p/6004280.html
https://blog.csdn.net/weiqifa0/article/details/82765892

Verilog代码

SPI总线协议是一种全双工的串行通信协议,数据传输时高位在前,低位在后。SPI协议规定一个SPI设备不能在数据通信过程中仅仅充当一个发送者(Transmitter)或者接受者(Receiver)。在片选信号CS为0的情况下,每个clock周期内,SPI设备都会发送并接收1 bit数据,相当于有1 bit数据被交换了。数据传输高位在前,低位在后(MSB first)。SPI主从结构内部数据传输示意图如下图所示

用三段式状态机实现

代码

`timescale 1ns / 1ps
module spi_master2(
	input clk,
	input rstn,
	input en,   
	
	input [7:0] tx_data,  // 要发送给slave的数据
	output reg [7:0] rx_data, // 从slave接收的数据
	output trans_done,    // 发送完成信号
	output rec_done,     // 接收完成信号
	
	// spi interface
	input miso,
	output reg sclk,
	output mosi,
	output cs
    );
	
reg [1:0] spi_state;
reg [1:0] spi_state_next;

reg [2:0] spi_count;

localparam [1:0] IDLE = 2'd0,TRANS = 2'd1,FINISH = 2'd2;

always @(posedge clk or negedge rstn) begin
	if(!rstn) spi_state <= IDLE;
	else spi_state <= spi_state_next;
end

always @(*) begin
	case(spi_state)
		IDLE:
			begin
				if(en) spi_state_next <= TRANS;
				else spi_state_next <= IDLE;
			end
		TRANS:
			begin
				if(spi_count == 3'd0 && sclk) spi_state_next <= FINISH; // 跟sclk相与是为了spi_count==0持续1个sclk周期
				else spi_state_next <= TRANS;
			end
		FINISH:
			begin
				spi_state_next <= IDLE;
			end
		default: spi_state_next <= IDLE;
	endcase
end

// sclk 为系统时钟的二分频时钟
always @(posedge clk or negedge rstn) begin
	if(!rstn) sclk <= 1'b0;
	else begin
		if(spi_state==TRANS) sclk <= ~sclk;
		else sclk <= 1'b0;
	end
end

// spi_count
always @(posedge clk or negedge rstn) begin
	if(!rstn) spi_count <= 3'd7;
	else begin
		if(spi_state==FINISH) spi_count <= 3'd7;
		else if(spi_state==TRANS && sclk) spi_count <= spi_count - 1'b1;
		else spi_count <= spi_count;
	end
end

// 接收数据
always @(posedge clk or negedge rstn) begin
	if(!rstn) rx_data <= 8'd0;
	else begin
		if(spi_state==TRANS && (~sclk)) rx_data[spi_count] <= miso;
		else rx_data <= rx_data;
	end
end

// 发送数据
assign mosi = (spi_state == TRANS)? tx_data[spi_count] : 1'bz;  // 设为z态方便调试


assign trans_done = (spi_state == FINISH)? 1'b1 : 1'b0;
assign rec_done  = trans_done;
assign cs = (spi_state == TRANS)? 1'b1 : 1'b0;

endmodule

测试激励

`timescale 1ns / 1ps

module spi_master2_tb;

	// Inputs
	reg clk;
	reg rstn;
	reg en;
	reg [7:0] tx_data;
	wire miso;

	// Outputs
	wire [7:0] rx_data;
	wire trans_done;
	wire rec_done;
	wire sclk;
	wire mosi;
	wire cs;

	// Instantiate the Unit Under Test (UUT)
	spi_master2 uut (
		.clk(clk), 
		.rstn(rstn), 
		.en(en), 
		.tx_data(tx_data), 
		.rx_data(rx_data), 
		.trans_done(trans_done), 
		.rec_done(rec_done), 
		.miso(miso), 
		.sclk(sclk), 
		.mosi(mosi), 
		.cs(cs)
	);

	initial begin
		// Initialize Inputs
		clk = 0;
		rstn = 0;
		en = 0;
		tx_data = 0;
		//miso = 0;

		// Wait 100 ns for global reset to finish
		#100;
		@(negedge clk);
		rstn = 1;
		@(negedge clk);
		en = 1;
		tx_data = 8'b1010_1010;
		
		@(negedge trans_done);
		//miso = 1;
		tx_data = 8'b0101_0101;
		@(negedge trans_done);
		en = 0;
		
        
		// Add stimulus here

	end
	
	assign miso = mosi;
	
	always #20 clk = ~clk;
      
endmodule

测试波形

用计数实现

代码

`timescale 1ns / 1ps
module spi_master(
	input clk,
	input rstn,
	input en,  
	
	input [7:0] tx_data,  // 要发送给slave的数据
	output reg [7:0] rx_data, // 从slave接收的数据
	output reg trans_done,    // 发送完成信号
	output reg rec_done,     // 接收完成信号
	
	// spi interface
	input miso,
	output reg sclk,
	output reg mosi,
	output reg cs
    );
	
reg [3:0] spi_state;
	
always @(posedge clk or negedge rstn) begin
	if(!rstn) begin
		spi_state <= 4'd0;
		cs <= 1'b1;
		mosi <= 1'b0;
		trans_done <= 1'b0;
		sclk <= 1'b0;
		rx_data <= 8'd0;
		rec_done <= 1'b0;
	end
	else begin
	if(en) begin
		spi_state <= spi_state + 1'b1;
		cs <= 1'b0;
		case(spi_state)
			4'd0: 
				begin
					sclk <= 1'b0;
					mosi <= tx_data[7];
					trans_done <= 1'b0;
					rec_done <= 1'b0;
				end
			4'd1:
				begin
					sclk <= 1'b1;
					mosi <= mosi;
					trans_done <= 1'b0;
					rx_data[7] <= miso;
					rec_done <= 1'b0;
				end
			4'd2:
				begin
					sclk <= 1'b0;
					mosi <= tx_data[6];
					trans_done <= 1'b0;
					rec_done <= 1'b0;
				end
			4'd3: 
				begin
					sclk <= 1'b1;
					mosi <= mosi;
					trans_done <= 1'b0;
					rx_data[6] <= miso;
					rec_done <= 1'b0;
				end
			4'd4:
				begin
					sclk <= 1'b0;
					mosi <= tx_data[5];
					trans_done <= 1'b0;
					rec_done <= 1'b0;
				end
			4'd5:
				begin
					sclk <= 1'b1;
					mosi <= mosi;
					trans_done <= 1'b0;
					rx_data[5] <= miso;
					rec_done <= 1'b0;
				end				
			4'd6:
				begin
					sclk <= 1'b0;
					mosi <= tx_data[4];
					trans_done <= 1'b0;
					rec_done <= 1'b0;
				end
			4'd7:
				begin
					sclk <= 1'b1;
					mosi <= mosi;
					trans_done <= 1'b0;
					rx_data[4] <= miso;
					rec_done <= 1'b0;
				end				
			4'd8: 
				begin
					sclk <= 1'b0;
					mosi <= tx_data[3];
					trans_done <= 1'b0;
					rec_done <= 1'b0;
				end
			4'd9:
				begin
					sclk <= 1'b1;
					mosi <= mosi;
					trans_done <= 1'b0;
					rx_data[3] <= miso;
					rec_done <= 1'b0;
				end
			4'd10:
				begin
					sclk <= 1'b0;
					mosi <= tx_data[2];
					trans_done <= 1'b0;
					rec_done <= 1'b0;
				end
			4'd11: 
				begin
					sclk <= 1'b1;
					mosi <= mosi;
					trans_done <= 1'b0;
					rx_data[2] <= miso;
					rec_done <= 1'b0;
				end
			4'd12:
				begin
					sclk <= 1'b0;
					mosi <= tx_data[1];
					trans_done <= 1'b0;
					rec_done <= 1'b0;
				end
			4'd13:
				begin
					sclk <= 1'b1;
					mosi <= mosi;
					trans_done <= 1'b0;
					rx_data[1] <= miso;
					rec_done <= 1'b0;
				end				
			4'd14:
				begin
					sclk <= 1'b0;
					mosi <= tx_data[0];
					trans_done <= 1'b1;
					rec_done <= 1'b0;
				end
			4'd15:
				begin
					sclk <= 1'b1;
					mosi <= mosi;
					trans_done <= 1'b0;
					rx_data[0] <= miso;
					rec_done <= 1'b1;
				end				
		endcase
	end
	else begin
		spi_state <= 4'd0;
		cs <= 1'b1;
		mosi <= 1'b0;
		trans_done <= 1'b0;
		sclk <= 1'b0;
		rx_data <= 8'd0;
		rec_done <= 1'b0;
	end
end
	
end

endmodule

测试激励

`timescale 1ns / 1ps

module spi_master_tb;

	// Inputs
	reg clk;
	reg rstn;
	reg en;
	reg [7:0] tx_data;
	wire miso;

	// Outputs
	wire [7:0] rx_data;
	wire trans_done;
	wire rec_done;
	wire sclk;
	wire mosi;
	wire cs;

	// Instantiate the Unit Under Test (UUT)
	spi_master uut (
		.clk(clk), 
		.rstn(rstn), 
		.en(en), 
		.tx_data(tx_data), 
		.rx_data(rx_data), 
		.trans_done(trans_done), 
		.rec_done(rec_done),
		.miso(miso), 
		.sclk(sclk), 
		.mosi(mosi), 
		.cs(cs)
	);

	initial begin
		// Initialize Inputs
		clk = 0;
		rstn = 0;
		en = 0;
		tx_data = 0;
		//miso = 0;

		// Wait 100 ns for global reset to finish
		#100;
		@(negedge clk);
		rstn = 1;
		@(negedge clk);
		en = 1;
		tx_data = 8'b1010_1010;
		
		@(negedge trans_done);
		//miso = 1;
		tx_data = 8'b0101_0101;
		@(negedge trans_done);
		en = 0;
		
        
		// Add stimulus here

	end
	
	assign miso = mosi;
	
	always #20 clk = ~clk;
      
endmodule

波形

原文地址:https://www.cnblogs.com/wt-seu/p/12720275.html