FPGA(Verilog)实现uart传输协议传输数据(含仿真)

实现功能:

1.接收uart串行数据,输出并行数据(1byte)。

2.输入并行数据(1byte),输出uart串行数据。

3.完成uart传输的1次环回。

uart协议的1帧数据传输

uart_test系统框图

Verilog代码实现
1.uart接收模块:接收串行数据,输出并行数据和其有效标志。
module uart_rx#(
	parameter	UART_BPS='d9600			,	//波特率:1s传输9600个bit
	parameter	CLK_FREQ='d50_000_000		//时钟频率:50MHz
)(
	input 	wire			clk			,
	input 	wire			rst_n		,
	input 	wire			rx			,	//接收的串行数据

	output 	reg		[7:0]	po_data		,	//输出并行数据
	output 	reg				po_flag			//输出有效并行数据的标志信号
);

首先对rx进行时钟同步,消除亚稳态,以及打拍处理提取下降沿;

always@(posedge clk or negedge rst_n)
	if(!rst_n) begin
		rx_d1<=1'b1;
		rx_d2<=1'b1;
		rx_d3<=1'b1;
	end
	else begin
		rx_d1<=rx;		//时钟同步,消除亚稳态
		rx_d2<=rx_d1;	//时钟同步,消除亚稳态
		rx_d3<=rx_d2;	//打拍处理
	end

assign rx_fall = ~rx_d2 & rx_d3 ;	//提取rx下降沿

然后需要数据帧的有效信号,以便知道什么时候计数;

always@(posedge clk or negedge rst_n)
	if(!rst_n) frame_val<=1'b0;
	else if((bit_cnt==BIT_CNT_MAX-1'b1) && bit_flag)	//接收完1帧数据
		frame_val<=1'b0;
	else if(rx_fall)
		frame_val<=1'b1;
	else
		frame_val<=frame_val;

1码元符号(1bit)所需的计数器;

always@(posedge clk or negedge rst_n)
	if(!rst_n) baud_cnt<=16'd0;
	else if(frame_val==1'b0)	//数据帧无效
		baud_cnt<=16'd0;
	else if(baud_cnt==BAUD_CNT_MAX-1'b1)
		baud_cnt<=16'd0;
	else
		baud_cnt<=baud_cnt+1'b1;

1bit稳定数据的采样标志信号;

always@(posedge clk or negedge rst_n)
	if(!rst_n) bit_flag<=1'b0;
	else if(baud_cnt==BAUD_CNT_MAX_HALF-1'b1)	//采样需要在中间数据才稳定
		bit_flag<=1'b1;
	else
		bit_flag<=1'b0;

对采样到的bit数据进行计数;

always@(posedge clk or negedge rst_n)
	if(!rst_n) bit_cnt<=4'd0;
	else if((bit_cnt==BIT_CNT_MAX-1'b1) && bit_flag)	//10bit数据采样完毕
		bit_cnt<=4'd0;
	else if(bit_flag)	//采样到1bit数据
		bit_cnt<=bit_cnt+1'b1;
	else
		bit_cnt<=bit_cnt;

将接收到的串行数据rx转化为并行数据po_data_t;

always@(posedge clk or negedge rst_n)
	if(!rst_n) po_data_t<=8'b0;
	else if((bit_cnt>=4'd1) && (bit_cnt<BIT_CNT_MAX-1'b1) && bit_flag)	//8bit数据
		po_data_t<={rx_d3,po_data_t[7:1]};	//起始位后接的是低位
	else
		po_data_t<=po_data_t;

并行数据全部拼接完成的标志信号;

always@(posedge clk or negedge rst_n)
	if(!rst_n) po_flag_t<=1'b0;
	else if((bit_cnt==BIT_CNT_MAX-1'b1) && bit_flag)
		po_flag_t<=1'b1;
	else
		po_flag_t<=1'b0;

输出并行数据和输出有效并行数据的标志信号。

always@(posedge clk or negedge rst_n)
	if(!rst_n) po_data<=8'b0;
	else if(po_flag_t)
		po_data<=po_data_t;
	else
		po_data<=po_data;

//po_flag_t延迟1拍与po_data同步
always@(posedge clk or negedge rst_n)
	if(!rst_n) po_flag<=1'b0;
	else
		po_flag<=po_flag_t;
仿真结果:

2.uart发送模块:接收并行数据,发送串行数据。
module uart_tx#(
	parameter	UART_BPS='d9600			,	//波特率:1s传输9600个bit
	parameter	CLK_FREQ='d50_000_000		//时钟频率:50MHz
)(
	input 	wire			clk			,
	input 	wire			rst_n		,
	input	wire			pi_flag		,	//输入并行数据
	input	wire	[7:0]	pi_data		,	//输入有效并行数据的标志信号

	output 	reg 			tx				//输出串行数据
);

首先同样是uart 1帧数据的有效信号;

always@(posedge clk or negedge rst_n)
	if(!rst_n) frame_val<=1'b0;
	else if((bit_cnt==BIT_CNT_MAX-1'b1) && bit_flag)	//发送完1帧数据
		frame_val<=1'b0;
	else if(pi_flag)	//接收到有效的并行数据
		frame_val<=1'b1;
	else
		frame_val<=frame_val;

1码元符号(1bit)所需的计数器;

always@(posedge clk or negedge rst_n)
	if(!rst_n) baud_cnt<=16'd0;
	else if(frame_val==1'b0)	//数据帧无效
		baud_cnt<=16'd0;
	else if(baud_cnt==BAUD_CNT_MAX-1'b1)
		baud_cnt<=16'd0;
	else
		baud_cnt<=baud_cnt+1'b1;

1bit数据的发送标志信号;

always@(posedge clk or negedge rst_n)
	if(!rst_n) bit_flag<=1'b0;
	else if(frame_val && !baud_cnt)
		bit_flag<=1'b1;
	else
		bit_flag<=1'b0;

对bit数据的发送进行计数;

always@(posedge clk or negedge rst_n)
	if(!rst_n) bit_cnt<=4'd0;
	else if((bit_cnt==BIT_CNT_MAX-1'b1) && bit_flag)	//10bit数据计数完毕
		bit_cnt<=4'd0;
	else if(bit_flag)	//发送完1bit数据
		bit_cnt<=bit_cnt+1'b1;
	else
		bit_cnt<=bit_cnt;

输出串行数据。

always@(posedge clk or negedge rst_n)
	if(rst_n==1'b0) tx<=1'b1;	//空闲
	else if(bit_flag)
		case(bit_cnt)
			4'd0:	tx<=1'b0;		//起始位
			4'd1:	tx<=pi_data[0];	//最低位数据
			4'd2:	tx<=pi_data[1];
			4'd3:	tx<=pi_data[2];
			4'd4:	tx<=pi_data[3];
			4'd5:	tx<=pi_data[4];
			4'd6:	tx<=pi_data[5];
			4'd7:	tx<=pi_data[6];
			4'd8:	tx<=pi_data[7];	//最高位数据
			4'd9:	tx<=1'b1;		//结束位
			default:tx<=1'b1;
		endcase
仿真结果:

3.uart环回测试。
module uart_test#(
	parameter	UART_BPS='d9600			,	//波特率:1s传输9600个bit
	parameter	CLK_FREQ='d50_000_000		//时钟频率:50MHz
)(
	input 	wire clk	,
	input 	wire rst_n	,
	input 	wire rx		,	//接收的串行数据

	output 	wire tx			//发送的串行数据
);

wire [7:0] 	data		;
wire 		data_flag	;

uart_rx#(
	.UART_BPS(UART_BPS),
	.CLK_FREQ(CLK_FREQ)
)u_uart_rx(
	.clk	(clk	),
	.rst_n	(rst_n	),
	.rx		(rx		),

	.po_data(data),
	.po_flag(data_flag)
);

uart_tx#(
	.UART_BPS(UART_BPS),
	.CLK_FREQ(CLK_FREQ)
)u_uart_tx(
	.clk	(clk	),
	.rst_n	(rst_n	),
	.pi_data(data),
	.pi_flag(data_flag),

	.tx(tx)
);

endmodule
仿真验证:
`timescale 1ns/1ns
module tb_uart_test();

parameter	UART_BPS='d9600			;	//波特率:1s传输9600个bit
parameter	CLK_FREQ='d50_000_000	;	//时钟频率:50MHz

reg clk;
reg rst_n;
reg rx;

wire tx;

initial
begin
	clk=1'b0;
	rst_n<=1'b0;
	rx<=1'b1;
	#20
	rst_n<=1'b1;
end

always #10 clk=~clk;

initial
begin
	#200	//空闲状态
	rx_test();

	#(5208*10*20*3)
	#200
	$stop;
end

//输出uart数据帧(10-13)
task rx_test();	//任务函数,类似C语言
	integer j;
	for(j=10;j<13;j=j+1)
		rx_8bit(j);
endtask

//输入8bit并行数据,输出uart数据帧
task rx_8bit(
	input [7:0] data
);
integer i;

for(i=0;i<10;i=i+1)
begin
	case(i)
		0:rx<=1'b0;		//1帧数据
		1:rx<=data[0];
		2:rx<=data[1];
		3:rx<=data[2];
		4:rx<=data[3];
		5:rx<=data[4];
		6:rx<=data[5];
		7:rx<=data[6];
		8:rx<=data[7];
		9:rx<=1'b1;
	endcase
	#(5208*20);		//9600波特率
end
endtask

uart_test#(
	.UART_BPS(UART_BPS),
	.CLK_FREQ(CLK_FREQ)
)u_uart_test(
	.clk	(clk	),
	.rst_n	(rst_n	),
	.rx		(rx		),

	.tx(tx)
);

endmodule
仿真结果:

FPGA系统集成-RTL框图

相关推荐

  1. Qt实现FTP文件传输协议

    2024-04-06 04:40:06       45 阅读
  2. Qt实现XModel和YModel传输协议

    2024-04-06 04:40:06       25 阅读
  3. HTTP超文本传输协议

    2024-04-06 04:40:06       39 阅读
  4. HTTP超文本传输协议

    2024-04-06 04:40:06       20 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-06 04:40:06       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-06 04:40:06       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-06 04:40:06       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-06 04:40:06       20 阅读

热门阅读

  1. “头痛医头、脚痛医脚”的SAP解决方案

    2024-04-06 04:40:06       19 阅读
  2. 迁移Docker镜像存放目录

    2024-04-06 04:40:06       17 阅读
  3. Linux初学(十五)ssh服务

    2024-04-06 04:40:06       17 阅读
  4. matlab 坐标系变换

    2024-04-06 04:40:06       18 阅读
  5. MQ的作用及分类

    2024-04-06 04:40:06       18 阅读
  6. 11 - Debian如何限制sudo权限

    2024-04-06 04:40:06       19 阅读
  7. Docker-compose部署 gitlab-server

    2024-04-06 04:40:06       17 阅读