SPI

   一般来讲,SPI通信,都是MCU作为主机,而FPGA作为从机。SPI数据从MCU输入,与FPGA不在同一时钟域。  

一级D触发器可以实现上升沿、下降沿捕获电路。但是一级的D触发器,前一时刻的信号已经同步到同一时钟,而当前时刻直接从外部输入,与FPGA整体逻辑电路不在同一时钟域。可以使用两级D触发器。

为保证SPI总线数据的同步,其他信号/数据也需要经过2级D触发器输出同步。

       

//------------------------------------------------------------------------------------------------//分割线


`timescale 1ns/1ns
module spi_interface (
 
//clk&&rst_n
	input				rst_n			,
//spi_interface		
	input 				SCLK			,
	input				CS			,
	input				SDI			,
	output				SDO			,
//reg		
	output	[ 7:0]		        WDATA		        ,
	output	[ 6:0]		        WADDR			,
	output				WDATA_VALID		,
	
	input	[ 7:0]		        RDATA			,
	output	[ 6:0]		        RADDR			,
	output				RDATA_VALID		,
	
	output				ADC_CLEAN		
 
	);
	
//	===============================================================================	\   			
//	***************	Interal signals and Interface	*******************************  	
//	===============================================================================	/		
	parameter	idle 		=	7'b000_0001 ;
	parameter	write		= 	7'b000_0010 ;
	parameter	wdata_ready     =  	7'b000_0100 ;
	parameter	wrdata_wait     = 	7'b000_1000 ;
	parameter	read		=	7'b001_0000 ;
	parameter	rdata_ready     = 	7'b010_0000 ;
	parameter	read_data	= 	7'b100_0000 ;	
//state	
	reg 	        [ 7:0]		state_c				;
	reg		[ 7:0]		state_n				;
	
	reg		[ 1:0]		spi_cs				;
	wire				spi_cs_rise			;
	reg		[ 7:0]		cnt				;
	reg		[15:0]		data_in				;
//write 	
	reg				wr_flag			        ;
	reg				write_data_valid	        ;
	reg		[ 6:0]		write_data_addr		        ;
	reg		[ 7:0]		write_data			;
//read	
	reg				read_data_valid		        ;
	reg 	[ 6:0]		        read_data_addr		        ;
	reg 	[ 7:0]		        spi_rdata			;
	reg				adc_reg_clean		        ;
	
//	============================================================================ \   
//	************************	Main Code	**********************************    	
//	============================================================================ /
 
 
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			spi_cs <= 2'b0 ;
		end else begin
			spi_cs[0] <= CS ; 
			spi_cs[1] <= spi_cs[0] ;
		end
	end 
	
	assign	spi_cs_rise = ~spi_cs[1] & spi_cs[0] ;
	
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			cnt <= 8'b0 ;
		end else begin
			if(!CS) begin
				cnt <= cnt + 1'b1 ;
			end else begin
				cnt <= 8'b0 ;
			end 
		end 
	end 
	
	always @ (posedge SCLK or negedge rst_n) begin 
		if(!rst_n) begin
			data_in <= 16'b0 ;
		end else begin
			if(CS == 1'b0) begin
				if(cnt <= 8'd15) begin
					data_in <= {data_in[14:0],SOI} ;
				end else begin
					data_in <= data_in ;
				end 
			end else begin
				data_in <= 16'd0 ;
			end 
		end
	end 
	
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			wr_flag <= 1'b0 ;
		end else begin
			if(cnt == 8'b1) begin
				wr_flag <= data_in[0] ;
			end else begin
				wr_flag <= 1'b0 ;
			end 
		end 
	end 
//因为write_data和address都是一个clk有效,所以需要有效信号write_data_valid	
//而且在state_c==wdata_ready时cnt==18,因为data_in完全寄存了SDI
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			write_data_valid <= 1'b0 ;
		end else begin
			if(state_c == wdata_ready) begin
				write_data_valid <= 1'b1 ;
			end else begin
				write_data_valid <= 1'b0 ;
			end 
		end 
	end 
	
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			write_data_addr <= 7'b0 ;
		end else begin
			if(state_c == wdata_ready) begin
				write_data_addr <= data_in[14:8] ;
			end else begin	
				write_data_addr <= 7'b0 ;
			end 
		end 
	end 
	
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			write_data <= 8'b0 ;
		end else begin
			if(state_c == wdata_ready) begin
				write_data <= data_in[7:0] ; 
			end else begin
				write_data <= 8'b0 ;
			end
		end
	end
//read转rdata_ready的条件是cnt == 8'd7 
//state_c == rdata_ready时cnt == 8'd8,此时刚好SDI在data_in的低八位进行存储。	
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			read_data_valid <= 1'b0 ;			
		end	else begin
			if(state_c == rdata_ready && cnt == 8'd8) begin
				read_data_valid <= 1'b1 ;
			end else begin
				read_data_valid <= 1'b0 ;
			end
		end 
	end 
	
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			read_data_addr <= 7'b0 ;
		end else begin
			if(state_c == rdata_ready && cnt == 8'd8) begin
				read_data_addr <= data_in[6:0] ;
			end else begin
				read_data_addr <= 7'b0 ;
			end 
		end 
	end 
	
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			spi_rdata <= 8'b0 ;
		end else begin
			if(state_c == rdata_ready && cnt == 8'd10 ) begin
				spi_rdata <= RDATA ;
			end else if(state_c == read_data) begin
				spi_rdata <= spi_rdata << 1 ;
			end else begin
				spi_rdata <=8'b0 ;
			end 
		end 
	end 
		
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			adc_reg_clean <= 1'b0 ;
		end else begin
			if(state_c == write && cnt == 8'd16 && data_in[14:8] == 7'd3) begin
				adc_reg_clean <= 1'b1 ;
			end else begin
				adc_reg_clean <= 1'b0 ;
			end 
		end 
	end 
				
//	============================================================================ \   
//	************************	state	**********************************    	
//	============================================================================ /		
	wire 	idle2writ			        ;
	wire	write2wdata_ready			;
	wire	wrdata_wait2idle                        ;
	wire	read2rdata_ready                        ;
	wire 	rdata_ready2read_data   	        ;
	wire 	read_data2wrdata_wait		        ;
	
	assign	idle2writ		 	= 	(cnt == 8'd2) && (wr_flag == 1'b0) && (state_c == idle) ;
	assign 	write2wdata_ready 		=	(cnt == 8'd16) && (state_c == write) ;
	assign 	wrdata_wait2idle		= 	spi_cs_rise && (state_c == wrdata_wait) ;
	assign	read2rdata_ready		=	(cnt == 8'd7) && (state_c == read) ;
	assign	rdata_ready2read_data	        =	(cnt == 8'd10) && (state_c == rdata_ready) ;
	assign	read_data2wrdata_wait         	=	(cnt == 8'd19) && (state_c == read_data) ;	
	
	always @ (posedge SCLK or negedge rst_n) begin
		if(!rst_n) begin
			state_c <= idle ;
		end else begin
			state_c <= state_n ;
		end
	end
	
	always @ (*) begin	
		case(state_c)
			idle : begin
				if(idle2writ) begin
					state_n = write ;
				end else begin
					state_n = read ;
				end 
			end
			
			write : begin
				if(write2wdata_ready) begin
					state_n = wdata_ready ;
				end else begin
					state_n = state_c ;
				end
			end
			
			wdata_ready : begin
				state_n = wrdata_wait ;
			end 
			
			wrdata_wait : begin
				if(wrdata_wait2idle) begin
					state_n = idle ;
				end else begin
					state_n = state_c ;
				end 
			end
			read : begin
				if(read2rdata_ready) begin
					state_n = rdata_ready ;
				end else begin
					state_n = state_c ;
				end
			end 
			
			rdata_ready : begin
				if(rdata_ready2read_data) begin
					state_n = read_data ;
				end else begin
					state_n = state_c ;
				end 
			end 
			read_data : begin
				if(read_data2wrdata_wait) begin
					state_n = wrdata_wait ;
				end else begin
					state_n = state_c ;
				end
			end 
			
		endcase		
	end 
	
//	============================================================================ \       	
//	************************	output	**********************************    	         	
//	============================================================================ /	
	assign		WDATA			= write_data_valid	;
	assign		WADDR			= write_data_addr	;
	assign		WDATA			= write_data		;
		
	assign		RDATA_VALID		= read_data_valid	;
	assign		RADDR			= read_data_addr	;
	assign		SDO				= spi_rdata[7]		;
	assign		ADC_CLEAN		= adc_reg_clean		;
	
endmodule 	

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值