基于verilog的uart协议实现

article/2025/9/17 11:59:20

目录

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)beginif(sys_rst)begincout <= 12'b0;endelse if(cout == DIV_NUM-1'b1)begincout <= 12'b0;endelse if(cstate == SEND)begincout <= 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)beginif(sys_rst)begintx_data_pre[10:0] <= 11'b1111_1111_11;shift_num <= 4'd0;endelse if(cstate == PRE)begintx_data_pre[10:0] <= {1'b0,tx_data,(^tx_data),1'b1};endelse if(shift_num == 4'd10 && baud_ndg)begintx_data_pre[10:0] <= 11'b1111_1111_11;shift_num <= 4'd0;endelse if(baud_ndg)begintx_data_pre[10:0] <= {tx_data_pre[9:0],1'b1};shift_num <= shift_num + 1'b1;endend	//state machine
always@(posedge clk_200m or posedge sys_rst)beginif(sys_rst)begincstate <= IDLE;endelse begincstate <= nstate;end
end
always@(*)begincase(cstate)//synthesis full_caseIDLE: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)beginif(sys_rst)begintx_done <= 1'b0;endelse begincase(cstate)IDLE:tx_done <= 1'b0;PRE :;SEND:beginif(shift_num == 4'd10 && baud_ndg)tx_done <= 1'b1;else tx_done <= 1'b0;endendcaseend
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)beginif(sys_rst)begincout <= 12'b0;endelse if(cout == DIV_NUM - 1'b1 )begincout <= 12'b0;endelse if(cstate == RECE)begincout <= 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)beginif(sys_rst)beginrx_d <= 2'b11;endelse beginrx_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)beginif(sys_rst)beginrx_data_rec[10:0] <= 11'b0;endelse if(baud_pdg)beginrx_data_rec[10:0] <= {rx_data_rec[9:0],rx};end
end	
always@(posedge clk_200m or posedge sys_rst)beginif(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)beginif(sys_rst)rx_data <= 8'd0;else if(rx_done && ~(^rx_data_rec[9:1]))rx_data <= rx_data_rec[9:2];
end//state machinealways@(posedge clk_200m or posedge sys_rst)beginif(sys_rst)begincstate <= IDLE;endelse begincstate <= nstate;end
end
always@(*)begincase(cstate)//synthesis full_caseIDLE: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)beginif(sys_rst)beginrx_done <= 1'b0;endelse begincase(cstate)IDLE:rx_done <= 1'b0;RECE:beginif(shift_num == 4'd10 && baud_ndg)rx_done <= 1'b1;else rx_done <= 1'b0;endendcaseend
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 综合后仿真


http://chatgpt.dhexx.cn/article/GGlqjppw.shtml

相关文章

UART协议快速扫盲(图文并茂+超详细)

文章目录 1 UART发展历史1.1 早期的串行通讯设备1.2 早期的芯片级UART1.3 现代UART的发展 2 预备知识3 协议层起始位数据校验位停止位波特率 4 传输过程5 物理层6 优缺点 1 UART发展历史 1.1 早期的串行通讯设备 早期的电报机器使用长度可变的脉冲信号进行数据传输&#xff0…

UART协议学习

通信协议分层 物理层 物理层规定通讯系统具有的机械、电子功能部分的特性&#xff0c;确保原始数据在物理媒体的传输。如RS232、RS485等就是电气协议&#xff0c;规定了数据传输时的电平标准&#xff0c;网络上许多博主将UART看作一个协议族&#xff0c;这些电气协议都是UART…

c语言模拟uart协议的收发

这篇文章注重思想的讲解,理解下来肯定对uart协议有一个更深的认识。 uart协议,通常用在嵌入式设备之间的通信。像下面这样: 问题一:uart是全双工还是半双工? 你完全可以将两个设备想象成两个人,上图中的两条线想象成A和B的对话。A对B说话和B对A说话的一个场景。问大家一个问题…

UART协议详解

通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;通常称作UART。 定义&#xff1a;UART是一种通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输和接收。在嵌入式设计中&#xff0c;UART用于主…

Linux·UART协议

目录 一、什么是UART&#xff1f; 二、UART的帧格式 2.1 为什么UART的传输需要起始位&#xff1f; 2.2 UART基本的数据形式 2.3 为什么UART的数据位可变&#xff1f; 三、UART的波特率 3.1 什么是波特率 3.2 如何换算波特率 3.3 波特率和采样频率是一样的吗&#xff1f…

通信协议(一)——UART协议

1、知识点 基础部分参考&#xff1a;UART串口发送模块设计Verilog_发光中请勿扰的博客-CSDN博客_uart设计verilog &#xff08;1&#xff09;什么是串口&#xff08;UART&#xff09;&#xff1f; 串口作为常用的三大低速总线&#xff08;UART、SPI、IIC&#xff09;之一&#…

uart协议学习,从了解到入门,看这篇文章

uart协议从了解到入门 背景知识介绍&#xff1a;1、并行和串行的意思&#xff1a;2、串转并和并转串传输&#xff1a;3、单工、半双工、全双工区别&#xff1a; uart协议介绍1、uart简介2、uart通信3、uart工作原理 uart的优缺点 背景知识介绍&#xff1a; 1、并行和串行的意思…

FPGA实现uart协议

简介 使用verilog实现uart协议&#xff0c;能够和pc进行通信&#xff0c;实现串口回环功能&#xff0c;各参数设置如下&#xff1a; 波特率&#xff1a;115200数据位&#xff1a;8停止位&#xff1a;任意校验位&#xff1a;无 系统时钟为50M&#xff0c;115200波特率下&…

协议篇之UART协议

协议篇之UART协议 一、写在前面二、UART协议简介三、UART协议数据帧结构3.1 UART发送过程3.2 UART接收过程3.3 UART传输速率 四、UART收发模块设计4.1 UART接收模块设计4.2 UART发送模块设计4.3 UART回环顶层模块4.4 UART回环上板验证 五、写在最后 一、写在前面 由于设计需要&…

UART通信协议

UART通信协议 UART ( universal asynchronous receiver-transmitter&#xff09;是一种采用异步串行通信方式的通用异步收发传输器;它在发送数据时将并行数据转换成串行数据来传输&#xff0c;在接收数据时将接收到的串行数据转换成并行数据。UART串口通信需要两根信号线来实现…

UART协议讲解

UART协议讲解 一、什么是UART协议二、通信方式的分类1、串行通信&#xff08;一&#xff09;、同步通信和异步通信&#xff08;二&#xff09;、单工和半双工以及全双工 2、并行通信 三、UART协议具体介绍1、UART数据传输的格式2、UART电平标准 三、UART的优缺点四、参考 一、什…

【数字IC】深入浅出理解UART协议

深入浅出理解UART协议 一、什么是UART&#xff1f;二、UART的帧格式2.1 为什么UART的传输需要起始位&#xff1f;2.2 UART基本的数据形式2.3 为什么UART的数据位可变&#xff1f; 三、UART的波特率3.1 什么是波特率3.2 如何换算波特率3.3 波特率和采样频率是一样的吗&#xff1…

最详细的 UART协议 分析在这里!

1. 协议基础 1.1. 协议简介 UART是“Universal Asynchronous Receiver/Transmitter”&#xff0c;通用异步收发器的缩写。在19世纪60年代&#xff0c;为了解决计算机和电传打字机通信&#xff0c;Bell发明了UART协议&#xff0c;将并行输入信号转换成串行输出信号。因为U…

UART协议

UART协议 简介 UART是通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;通常称作UART&#xff0c;是一种异步收发传输器,是设备间进行异步通信的关键模块。UART负责处理数据总线和串行口之间的串/并、并/串转换&#xff0c;并规定了帧…

【云域网络社区】云域网络社区APP

【软件名称】云域社区 【功能介绍】资源分享&#xff0c;游戏交流交友 【下载地址】http://sss.shmmec.com/apk.apk 图片

怎么划分领域、子域、核心域、通用域和支撑域

怎么划分领域、子域、核心域、通用域和支撑域 如何理解领域和子域&#xff1f;什么是领域什么是子域怎么划分领域和子域如何理解核心域、通用域和支撑域&#xff1f;为什么要划分核心域、通用域和支撑域 总结 DDD 的知识体系提出了很多的名词&#xff0c;像&#xff1a;领域、子…

网络安全-域服务器(二)

域服务器&#xff1a; 域服务器&#xff08;一&#xff09;&#xff1a;网络安全-域&#xff08;一&#xff09;_IT之一小佬的博客-CSDN博客域服务器&#xff08;二&#xff09;&#xff1a;网络安全-域服务器&#xff08;二&#xff09;_IT之一小佬的博客-CSDN博客 域 11.O…

网课管理系统

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 3. 功能简介 用户中心 1.1用户注册&#xff1a;用户需要注册才能登陆进入web 1.2用户登录&#xff1a;通过判断匹配来进…

如何搭建域服务器

实验名称&#xff1a;如何搭建域服务器 实验环境&#xff1a;wind2003*2 实验过程&#xff1a; 由于Windows Server 2003在默认的安装过程中DNS是不被安装的&#xff0c;所以我们需要手动去添加&#xff0c; 添加方法如下:“开始—设置—控制面板—添加删除程序”&#xff…

搭建域环境(win)

目录 常见域环境是用&#xff1a;win2012 win7 win2003 本blog环境&#xff1a;Windows Server 2012 R2 and Win2008 1.Window Server 2012 R2 服务器配置 1&#xff09;变更计算机名DC&#xff08;用于存储活动目录数据库的计算机&#xff09; 2&#xff09;设置服务器…