SPI通信在Verilog中的实现(简单版)

本文分享了一个使用Verilog实现的SPI通信模块,包括代码、仿真代码及仿真结果解析。SPI时钟clk_1M由sys_clk_10M分频得到,flag信号触发通信,spi_enOut和spi_clkOut具有延迟。 spi_dataIn数据在spi_clkOut上升沿传输。关键词涉及SPI通信、Verilog、时钟分频、信号延迟和仿真。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家好,最近试了一些spi通信在verilog中的实现方案,最终写了一个比较简单的方案。分享给大家,代码需要可自取。

一、上代码:

`timescale 1ns / 1ps
//

module spi
 #(parameter T_bit = 15)

(
input					sys_rst,
input					sys_clk_10M,//sys_clk=10M,作为主频
input	[T_bit-1:0]	spi_dataIn,
output		reg	spi_dataOut = 1'b0,
output		reg	spi_enOut = 1'b0 ,
output		reg	 spi_clkOut =1'b0  ,
output			flag,
output	 [15:0] cnt_bit,
output	 [15:0] cnt,
output				clk_1M,
output				cnt_clk

);

reg	[3:0]	cnt_clk =4'b0; //对时钟进行分频
always@(posedge sys_clk_10M)
begin
		cnt_clk <= cnt_clk + 1'b1;
end

reg clk_1M = 1'b0;
always@(posedge sys_clk_10M)
begin
	if(cnt_clk == 3'd5) //
			clk_1M <= ~clk_1M;		
end




reg	 [15:0]	cnt_bit = 16'd0;
always@(posedge clk_1M)
begin
	if(sys_rst ||(cnt_bit == (T_bit-1)) || (spi_enOut == 1)) //系统复位 高有效
			cnt_bit <= 16'd0;		
	else	
			cnt_bit <= cnt_bit + 1'b1;
end


reg	 [15:0]	cnt = 16'd0;
always@(posedge clk_1M)
begin
	if(sys_rst) //系统复位 高有效
		begin
			cnt <= 16'd0;
		end
	else	if(cnt == 16'd100) 
				cnt <= 16'd0;
			else  
				cnt <= cnt + 1'b1;
end

reg	flag =1'b0   ;
always@(posedge clk_1M)
begin
	if(sys_rst) //系统复位 高有效
		begin
			flag <= 1'd0;
		end
	else	if( (cnt >= 16'd10)  && (cnt <= 16'd10 + 16'd14 )) //flag在10us-30us之间有效
				flag <= 1'd1;
			else  
				flag <= 1'd0 ;
end

always@(posedge clk_1M)
begin
	if(sys_rst || (spi_enOut ==1)) //系统复位 高有效
		begin
			spi_dataOut <= 1'd0;
		end
	else	if( flag) 
				spi_dataOut <= spi_dataIn[T_bit - 1 - cnt_bit]; //从高位开始传
			else  
				spi_dataOut <= spi_dataOut ;
end

reg flag_r1 =1'b0,flag_r2=1'b0,flag_r3=1'b0,flag_r4=1'b0,flag_r5=1'b0,flag_r6=1'b0;
reg clk_1M_r1=1'b0,clk_1M_r2=1'b0,clk_1M_r3=1'b0;
always@(posedge sys_clk_10M)
begin
	if(sys_rst) //系统复位 高有效
		begin
				spi_enOut <= 1'd0;				
		end
	else
		begin	
				flag_r1 <= flag;   //
				flag_r2 <= flag_r1; 
				flag_r3 <= flag_r2; 
				flag_r4 <= flag_r3;
				flag_r5 <= flag_r4;
				flag_r6 <= flag_r5;
				spi_enOut <= ~flag_r6; 		
		end
end

always@(posedge sys_clk_10M)
begin
	if(sys_rst) //系统复位 高有效
		begin
			spi_clkOut <= 1'd0;
		end
		else	if( (cnt >= 16'd12)  && (cnt <= 16'd10 + 16'd16 )) //flag在??us-??us之间有效
				spi_clkOut <= clk_1M;
			else  
				spi_clkOut <= 1'd0 ;
end


endmodule

二、上仿真代码:

`timescale 1ns / 1ps

//

module spi_test;

	// Inputs
	reg sys_rst;
	reg sys_clk_10M;
	reg [14:0] spi_dataIn;

	// Outputs
	wire spi_dataOut;
	wire spi_enOut;
	wire spi_clkOut;
	wire	flag;
	wire [15:0] cnt_bit;
	wire [15:0] cnt;
	wire clk_1M;
	wire [3:0] cnt_clk;
	
	// Instantiate the Unit Under Test (UUT)
	spi uut (
		.sys_rst(sys_rst), 
		.sys_clk_10M(sys_clk_10M), 
		.spi_dataIn(spi_dataIn), 
		.spi_dataOut(spi_dataOut), 
		.spi_enOut(spi_enOut), 
		.spi_clkOut(spi_clkOut),
		.flag(flag),
		.cnt_bit(cnt_bit),
		.cnt(cnt),
		.clk_1M(clk_1M),
		.cnt_clk(cnt_clk)
	);

	initial begin
		// Initialize Inputs
		sys_rst = 1;
		sys_clk_10M = 0;
		spi_dataIn = 15'b1111_1010_0101_001;

		// Wait 100 ns for global reset to finish
		#100;
        sys_rst = 0;
		  
		  forever #50 sys_clk_10M = ~sys_clk_10M;
		// Add stimulus here

	end
      
endmodule

三、上仿真结果:

图1:仿真结果

 四、对上述仿真结果进行一个简单的解释:

从图中可以看出,当flag信号来临后,一定延时后spi_enOut拉低,spi_clkOut开始在clk_1M上升沿传输数据(一共15个数据)。传输结束后,spi_enOut拉高,spi_clkOut为零。

①clk_1M是spi通信协议的时钟,sys_clk_10M是系统的时钟。其中clk_1M是由sys_clk_10M进行分频得到,分频计数器是cnt_clk。

②flag是spi通信模块接受到的外部使能信号,当flag是1的时候,spi开始通信。本代码中flag信号利用cnt实现了固定位置的触发。

③spi_enOut是spi模块内部使能信号(即片选),低为有效值。本代码中spi_enOut相对于flag具有一定的延时(通过打拍实现,增加打拍数量可以改变延时量)。

④spi_clkOut是spi模块的输出时钟。在spi_clkOut上升沿对spi_dataOut进行读取。其中spi_clkOut相对于clk_1M具有一定的延时。

⑤spi_dataIn是spi模块的输入数据。本代码中是15'b1111_1010_0101_001,在仿真代码中给出。

⑥cnt_bit是传输数据数量,本代码中cnt_bit实现了0-14的循环。

感谢关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值