基于verilog的uart协议实现

目录

1、理论介绍

2、架构设计

3、代码设计

一、发送模块代码

二、接收代码设计

三、顶层模块设计

 四、测试代码

 4、仿真实验


1、理论介绍

        uart:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),是一种串行的收发方式,由于没有时钟,因此需要双方约定好传输的速率,以及起始和停止,为了保证数据的可靠传输,还需要使用校验位。uart协议如图

图1 uart协议

        在协议未开始传输时,物理层通路上电平为高,起始位将电平拉低,意为着传输开始,这边使用大端传输方式 ,即先传输高字节数据,8位数据位传输完成后,使用奇偶校验位进行校验(是一种验错方式,但是不能纠错),最后传输1位/1.5位/2位停止位。

      以前总是想着通信协议应该是有一个统一结构标准的,这里我先斗胆这样定义,如果有问题随时改正,这里先定义物理层和链路层:

物理层:主要任务为确定与传输媒体的接口有关的一些特性

        机械特性:指明接口所用接线器的形状和尺寸、引脚数目和排列、固定和锁定装置等。

        电气特性:指明在接口电缆的各条线上出现的电压范围、阻抗匹配、传输速率等。

        功能特性:指明物理接口上各条信号线的功能分配和确切定义。

        过程特性:指明对于不同功能的各种可能事件的出现顺序(理解为建立物理连接、维持和交换信息)。  

数据链路层:将来源于物理层的数据进行可靠的传输。如何可靠的传输,就是协议里面具体规定的内容,以帧为单位进行传输。

这里理解的uart协议,是一个没有定义物理层,只定义了协议层的协议,因此我们可以使单端线,RS232,RS485或者其他接口传输,不同的接口也就造成了传输距离和速率的区别,可以根据自己的需要自由的选择接口。

2、架构设计

        在编写代码之前是需要先构建模块框图的,先分析一下uart需要哪些模块,这是一个异步全双工的协议,因此需要一个发送模块和一个接收模块,分别在各自模块内部实现波特率的生成,以及数据收发,模块以及其信号如下:

图2 发送模块

图3 接收模块

        在完成顶层模块框图划分后,需要对各个模块分析,发送模块内部主要电路原理图如图4所示,接收模块内部主要电路框图如图5所示。根据设计的电路框图开始编写程序。

 图4 发送模块内部主要电路图

 

图5 接收模块内部主要电路图

3、代码设计

一、发送模块代码

/***************************************
#
#			Filename:tx.v
#
#			Developer:annotater
#			Description:---
#			CreatTime:2021-08-09 23:00:12
#
***************************************/
module tx(
input clk_200m,
input sys_rst,
input[7:0] tx_data,
input oe,
output tx,
output reg tx_done
);

localparam BAUD = 115200;
localparam DIV_NUM = 200000000/BAUD;
localparam IDLE = 3'b001;
localparam PRE	= 3'b010;
localparam SEND = 3'b100;

reg[2:0] cstate,nstate;//定义当前状态和次态
reg[11:0] cout;//波特率分频计数
reg[3:0] shift_num;//移位次数
reg[10:0] tx_data_pre;//需要发送的一帧数据
wire baud_pdg;//波特率上升沿
wire baud_ndg;//波特率下降沿

//buad generater
always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		cout <= 12'b0;
	end
	else if(cout == DIV_NUM-1'b1)begin
		cout <= 12'b0;
	end
	else if(cstate == SEND)begin
		cout <= cout + 1'b1;
	end
end
assign baud_pdg = (cout == DIV_NUM >> 1'b1 )?1'b1:1'b0;
assign baud_ndg = (cout == DIV_NUM -  1'b1 )?1'b1:1'b0;

//shift
always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		tx_data_pre[10:0] <= 11'b1111_1111_11;
		shift_num <= 4'd0;
	end
	else if(cstate == PRE)begin
		tx_data_pre[10:0] <= {1'b0,tx_data,(^tx_data),1'b1};
	end
    else if(shift_num == 4'd10 && baud_ndg)begin
        tx_data_pre[10:0] <= 11'b1111_1111_11;
        shift_num <= 4'd0;
    end
	else if(baud_ndg)begin
		tx_data_pre[10:0] <= {tx_data_pre[9:0],1'b1};
		shift_num <= shift_num + 1'b1;
	end

end	

//state machine
always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		cstate <= IDLE;
	end
	else begin
		cstate <= nstate;
	end
end
always@(*)begin
	case(cstate)//synthesis full_case
		IDLE:nstate = (oe)?PRE:IDLE;
		PRE :nstate = SEND;
		SEND:nstate = (shift_num == 4'd10 && baud_ndg)?IDLE:SEND;
	endcase
end
always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		tx_done <= 1'b0;
	end
	else begin
		case(cstate)
			IDLE:tx_done <= 1'b0;
			PRE :;
			SEND:begin
				if(shift_num == 4'd10 && baud_ndg)
					tx_done <= 1'b1;
				else 
					tx_done <= 1'b0;
			end
		endcase
	end
end
assign tx = (cstate == SEND)?tx_data_pre[10]:1'b1;
endmodule

二、接收代码设计

/***************************************
#
#			Filename:rx.v
#
#			Developer:annotater
#			Description:---
#			CreatTime:2021-08-10 17:44:59
#
***************************************/
module rx(
input clk_200m,
input sys_rst,
input rx,

output reg[7:0] rx_data,
output reg rx_done
);

localparam BAUD = 115200;
localparam DIV_NUM = 200000000/BAUD;
localparam IDLE = 2'b01;
localparam RECE = 2'b10;

reg[1:0] cstate,nstate;//定义当前状态和次态
reg[11:0] cout;//波特率分频计数
reg[3:0] shift_num;//移位次数
reg[10:0] rx_data_rec;//接收一帧数据
reg[1:0] rx_d;//同步器,用来消除亚稳态和检测下降沿
wire baud_pdg;//波特率上升沿
wire baud_ndg;//波特率下降沿
wire rx_ndg;//起始位下降沿
wire rx_error;//起始位接收错误
//buad generater
always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		cout <= 12'b0;
	end
	else if(cout == DIV_NUM - 1'b1 )begin
		cout <= 12'b0;
	end
	else if(cstate == RECE)begin
		cout <= cout + 1'b1;
	end
end
assign baud_pdg = (cout == DIV_NUM >> 1'b1 )?1'b1:1'b0;
assign baud_ndg = (cout == DIV_NUM -  1'b1 )?1'b1:1'b0;

//同步器
always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		rx_d <= 2'b11;
	end
	else begin
		rx_d <= {rx_d[0],rx};
	end
end
assign rx_ndg = rx_d[1] & ~(rx_d[0]);

assign rx_error = (baud_pdg&&(cstate == RECE)&&rx&&(shift_num == 4'b0000));//判断起始位是否检测错误

//shift
always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		rx_data_rec[10:0] <= 11'b0;
	end
	else if(baud_pdg)begin
		rx_data_rec[10:0] <= {rx_data_rec[9:0],rx};
	end
end	
always@(posedge clk_200m or posedge sys_rst)begin
    if(sys_rst)
        shift_num <= 0;
    else if(shift_num == 10 && baud_ndg)
        shift_num <= 0;
    else if(baud_ndg)
        shift_num <= shift_num + 1;
end
always@(posedge clk_200m or posedge sys_rst)begin
    if(sys_rst)
        rx_data <= 8'd0;
    else if(rx_done && ~(^rx_data_rec[9:1]))
        rx_data <= rx_data_rec[9:2];
end

//state machine

always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		cstate <= IDLE;
	end
	else begin
		cstate <= nstate;
	end
end
always@(*)begin
	case(cstate)//synthesis full_case
		IDLE:nstate = (rx_ndg)?RECE:IDLE;
		RECE:nstate = (shift_num == 4'd10 && baud_ndg || rx_error)?IDLE:RECE;
	endcase
end
always@(posedge clk_200m or posedge sys_rst)begin
	if(sys_rst)begin
		rx_done <= 1'b0;
	end
	else begin
		case(cstate)
			IDLE:rx_done <= 1'b0;
			RECE:begin
				if(shift_num == 4'd10 && baud_ndg)
					rx_done <= 1'b1;
				else 
					rx_done <= 1'b0;
			end
		endcase
	end
end
endmodule

三、顶层模块设计

顶层模块中需注意避免胶连逻辑(glue logic)。

/***************************************
#
#			Filename:uart_top.v
#
#			Developer:annotater
#			Description:---
#			CreatTime:2021-08-10 18:15:58
#
***************************************/
module uart_top(
input clk_200m,
input sys_rst,

input rx,
input [7:0] tx_data,
input oe,

output [7:0] rx_data,
output tx,
output rx_done,
output tx_done
);


rx U_RX(
    .clk_200m  ( clk_200m  ), //i
    .sys_rst   ( sys_rst   ), //i
    .rx        ( rx        ), //i
    .rx_data   ( rx_data   ), //o
    .rx_done   ( rx_done   )  //o
);


tx U_TX(
    .clk_200m  ( clk_200m  ), //i
    .sys_rst   ( sys_rst   ), //i
    .tx_data   ( tx_data   ), //i
    .oe        ( oe        ), //i
    .tx        ( tx        ), //o
    .tx_done   ( tx_done   )  //o
);

endmodule

 四、测试代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/08/10 18:21:32
// Design Name: 
// Module Name: tb_uart_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
module tb_uart_top();
reg      clk_200m    ;
reg      sys_rst     ;
reg      rx          ;
reg[7:0] tx_data     ;
reg      oe          ;
wire[7:0] rx_data     ;
wire      tx          ;
wire      rx_done     ;
wire      tx_done     ;
initial begin
clk_200m = 0;
sys_rst = 1;
rx = 0;
tx_data= 0;
oe = 0;
#100 sys_rst = 0;
forever #2.5 clk_200m = ~clk_200m;
end
initial begin
#200 tx_data = 8'b1010_1010;
#10 oe = 1;
#10 oe = 0;

#200000 tx_data = 8'b0101_0101;
#10 oe = 1;
#10 oe = 0;
end
uart_top U_UART_TOP(
    .clk_200m  ( clk_200m  ), //i
    .sys_rst   ( sys_rst   ), //i
    .rx        ( 0         ), //i
    .tx_data   ( tx_data   ), //i
    .oe        ( oe        ), //i
    .rx_data   (           ), //o
    .tx        ( tx        ), //o
    .rx_done   (           ), //o
    .tx_done   ( tx_done   )  //o
);

uart_top U_UART_TOP2(
    .clk_200m  ( clk_200m  ), //i
    .sys_rst   ( sys_rst   ), //i
    .rx        ( tx        ), //i
    .tx_data   ( 0         ), //i
    .oe        ( 0         ), //i
    .rx_data   ( rx_data   ), //o
    .tx        (           ), //o
    .rx_done   ( rx_done   ), //o
    .tx_done   (           )  //o
);

endmodule

 4、仿真实验

        设计代码首先要进行功能仿真验证其功能,功能验证后进行综合,综合是一个将RTL代码转换为门级网表的过程,不同的元件库会综合出不同的门级网表,因此有时候会出现一种库综合通过,另一种库综合不通过的现象,这里直接观察综合后仿真。

        仿真文件里面写的仿真过程为在两个时刻分别由一个串口发送AA和55数据,并由另一个串口接收,可以看到,仿真结果没有问题,其中一个bit占用时间8.7us左右。

 图6 综合后仿真

  • 9
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Annotater

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值