UART学习之路(四)VerilogHDL实现的简单UART,VIVADO下完成仿真
用VerilogHDL实现UART并完成仿真就算是对UART整个技术有了全面的理解,同时也算是Verilog入门了。整个UART分为3部分完成,发送模块(Transmitter),接收模块(Receiver)和波特率发生模块(BuadRateGenerator)。发送模块相比于接收模块要简单一些,主要功能就是每1/9600s发送1bit的数据,接收模块就在采样时钟下完成数据的采样,波特率发送模块就是产生对应的波特率。UART的基本电路模型可以看UART学习之路(二) 基本时序介绍,当中对UART进行了完整的电路建模。
1.发送模块
模块代码:
`timescale 1ns / 1ps
module UART_TRANSMITTER(
input [:] DataIn,//并行数据输入
input baud16x,TxEn,rstn,//baud16x=波特率×16,TxEn是并行数据装入使能信号,rstn复位信号
output reg DataOut,//串行数据输出
output reg TxBusy// 说明串口正忙的信号,检测其下降沿就可以判断是否可以装入新的数据
);
reg [:] DataInReg;//输入数据寄存器
reg [:] cnt;//计数器 reg cnt_start;//计数开始标志位
reg TransEnIn0;//当前状态采样
reg TransEnIn1;//上一个状态采样 wire pos_en;
//采TxEn的上升沿
always@(posedge baud16x or negedge rstn)
if(!rstn)begin
TransEnIn0<='b0;
TransEnIn1<='b0;
end
else begin
TransEnIn0 <= TxEn;//now
TransEnIn1 <= TransEnIn0;// delay
end
// Description
// I0 I1
// 0 0 : 1&0=0
// 1 0 : 1&1=1
// 1 1 : 1&0=0
// 0 1 :0&0=0
assign pos_en = TransEnIn0 & !TransEnIn1;
//数据装入
always@(negedge rstn or posedge baud16x)
if(!rstn)begin
DataInReg <= 'd0;
TxBusy <= 'b0;
end
else if(pos_en == 'b1)begin
DataInReg <= DataIn;
cnt_start <= 'b1;
TxBusy <= 'b1;
end
else if(cnt >= 'd160) begin
cnt_start <= 'b0;
TxBusy <= 'b0;
end //计数
always@(posedge baud16x or negedge rstn)
if(!rstn)
cnt <= 'd0;
else if(cnt_start == 'b1)
cnt <= cnt + 'b1;
else cnt <= 'd0; //UART 发送
always@(posedge baud16x or negedge rstn)
if(!rstn)
DataOut <='b1;
else if(cnt_start == 'b1)
case(cnt)
'd0:DataOut <= 1'b0;
'd16:DataOut <= DataInReg[0];
'd32:DataOut <= DataInReg[1];
'd48:DataOut <= DataInReg[2];
'd64:DataOut <= DataInReg[3];
'd80:DataOut <= DataInReg[4];
'd96:DataOut <= DataInReg[5];
'd112:DataOut <= DataInReg[6];
'd128:DataOut <= DataInReg[7];
'd144:DataOut <= 1'b1;
endcase
else DataOut <= 'b1;
endmodule
TestBench
`timescale 1ns / 1ps
module TESTBENCH_UART_TRANSMITTER(
);
parameter CLKPERIOD = ; reg [:] TempData;
reg clk,en,nrst; wire TxDataOut;
wire TxOverFlag; initial begin
clk = ;
en = ;
nrst = ;
TempData = 'd0;
end //clk generate
always #(CLKPERIOD/) clk = ~clk; //复位
initial begin
#CLKPERIOD nrst = ;
end //数据使能
initial begin
#CLKPERIOD en = ;
TempData = 'b1010_0101;
#CLKPERIOD en = ; #(CLKPERIOD*) en = ;
TempData = 'b0101_1010;
#CLKPERIOD en = ;
end
//version 1.0 2018-12-4
//module initial
UART_TRANSMITTER U1(//input
.baud16x(clk),
.TxEn(en),
.rstn(nrst),
.DataIn(TempData),
//output
.DataOut(TxDataOut),
.TxBusy(TxOverFlag));
endmodule
仿真结果
分析:
需要发送的并行数据存储在TempData里面,第一次发送10100101,发送起始位bit0=0,之后是数据位bit1,bit2,bit3,bit4,bit5,bit6,bit7,bit8,最后是停止位bit9=1。第二次发送01011010,结果同第一次发送。第一张图片,当en信号输入由第变高,产生上升沿后,说明待发送的数据已经装入发送寄存器了,发送端开始发送,首先发送的是LSB =1,最后发送的是MSB=1。第二张图片中,同第一张图片的过程,首先发送LSB=0,最后发送MSB=0。TxOverFlag表明发送端的忙状态,高电平是忙,低电平是闲,通关检测该信号的下降沿可以判断发送是否完成。
2.接收模块
模块代码
`timescale 1ns / 1ps
/*2018-9-21 verson 1.0
baud rate = 9600 bit/s
1 bit takes 1/9600s = 104.167 us ~= 104167ns
input frequency of clk is 100Mhz*/ // baud rate T_bdr N System clock = 50M
// 9600 104167ns 104167/System_clk_priod 5208-1
// 19200 52083ns 52083/System_clk_priod 2604-1
// 38400 26041ns 26041/System_clk_priod 1302-1
// 57600 17361ns 17361/System_clk_priod 868-1
// 115200 8680ns 8680/System_clk_priod 434-1
//
//Input clock is 16x bound rate, the first sample data is at 24, and the next sample data is at 40
// 24,40,56,72,88,104,120,136,152
module UART_RECEIVE(
input baud16x,rstn,
input recv,
output reg [:] rdata,
output reg recv_ready
);
reg recvIn0;//下降沿捕捉,当前时刻值
reg recvIn1;//下降沿捕捉,上一个时刻值 reg[:] cnt_bit;
reg RecvNeFlag;
//起始位获取
always@(posedge baud16x or negedge rstn)
if(!rstn) begin
recvIn0 <= 'b1;
recvIn1 <= 'b1;
end
else begin
/*下降沿采样*/
recvIn0 <= recv;//当前时刻的recv给recvIn0
recvIn1 <= recvIn0;//前一个时刻的recv给recvIn1
end wire neg_rec;
assign neg_rec = !recvIn0 && recvIn1;
/*下降沿判断,打开接收功能标志*/
always@(negedge rstn or posedge baud16x)
if(!rstn) begin
RecvNeFlag <= 'b0;
recv_ready <= 'b1;
end
else if(neg_rec == 'b1)begin
RecvNeFlag <= 'b1;
recv_ready <= 'b0;
end
else if(cnt_bit == 'd152)begin
RecvNeFlag <= 'b0;
recv_ready <= 'b1;
end //bit计数
always@(posedge baud16x or negedge rstn)
if(!rstn)
cnt_bit <= 'b0;
else if(RecvNeFlag == 'b1)
cnt_bit <= cnt_bit + 'b1;
else cnt_bit <= 'd0; //采样接收
always@(posedge baud16x or negedge rstn)
if(!rstn)
rdata <= 'b0000_0000;
else case(cnt_bit)
'd24:rdata[0] <= recv;//数据位第1位
'd40:rdata[1] <= recv;
'd56:rdata[2] <= recv;
'd72:rdata[3] <= recv;
'd88:rdata[4] <= recv;
'd104:rdata[5] <= recv;
'd120:rdata[6] <= recv;
'd136:rdata[7] <= recv;//数据位第8位
endcase
endmodule
2.TestBench
`timescale 1ns / 1ps
module TESTBENCH_UART_RECEIVE(
);
parameter CLKPERIOD=; reg GCLK;
reg nrst;
reg DataIn;
wire [:] recv_data;
wire RecvDoneFlag;
//初始化
initial begin
nrst = ;
GCLK = ;
DataIn = ;
end
//生成时钟激励
always #(CLKPERIOD/) GCLK = ~GCLK; //复位使能
initial #(CLKPERIOD*) nrst = ; //模拟发送数据
initial begin
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
#(CLKPERIOD*) DataIn = ;
end UART_RECEIVE U1(//input
.baud16x(GCLK),
.rstn(nrst),
.recv(DataIn),
//output
.rdata(recv_data),
.recv_ready(RecvDoneFlag));
endmodule
3.仿真结果
分析:
模块复位输出8'b0000_0000,接收采样在输入的中点处。串行输入数据,转换成并行的数据。第一张图发送的数据是8'b1111_1111,在接受端依次接收为8'b0000_0001,8'b0000_0011,…… ……,8'b1111_1111。接收到起始位后RecvDoneFlag信号由高拉低,表明接收端正忙,处于接收状态,这个地方信号名字打错了,后面懒得改了。接收完成后,RecvDoneFlag将由低拉高,判断接收完成只需要检测该信号的上升沿就行。第二张图发送的数据是8'b1001_0101,过程如同第一张图。观察时序图,发现recv_data数据变化的节点并不是8个,而是4个,这是因为模块默认输出的信号是8'b0000_0000,只有出现1的地方数据才会发生跳转(出现采样的痕迹),8’b1001_0101中有4个1,所以只有4个。
3.波特率发生模块实际上就是对100Mhz的时钟进行分频,分成BaudRate*16的时钟提供给发送和接收模块。
4.参考代码:https://github.com/jamieiles/uart ,GitHub上别人的另外一种实现方式。
UART学习之路(四)VerilogHDL实现的简单UART,VIVADO下完成仿真的更多相关文章
- UART学习之路(一)基本概念
第一篇博客,首先记录一下这一个多星期来的学习内容. UART学习之路第一篇,是UART的基本概念介绍.后续会用STM32F103的串口与PC机通信.最后使用Verilog HDL写出串口发送模块和接收 ...
- Redis——学习之路四(初识主从配置)
首先我们配置一台master服务器,两台slave服务器.master服务器配置就是默认配置 端口为6379,添加就一个密码CeshiPassword,然后启动master服务器. 两台slave服务 ...
- UART学习之路(三)基于STM32F103的USART实验
关于STM32串口的资料可以在RM0008 Reference Manual中找到,有中文版的资料.STM32F103支持5个串口,选取USART1用来实验,其对应的IO口为PA9和PA10.这次的实 ...
- USB小白学习之路(10) CY7C68013A Slave FIFO模式下的标志位(转)
转自良子:http://www.eefocus.com/liangziusb/blog/12-11/288618_bdaf9.html CY7C68013含有4个大端点,可以用来处理数据量较大的传输, ...
- zigbee学习之路(四):按键控制(中断方式)
一.前言 通过上次的学习,我们学习了如何用按键控制led,但是在实际应用中,这种查询方式占用了cpu的时间,如果通过中断控制就可以解决这个问题,我们今天就来学习按键控制的中断方式. 二.原理分析 传统 ...
- [原创]java WEB学习笔记79:Hibernate学习之路--- 四种对象的状态,session核心方法:save()方法,persist()方法,get() 和 load() 方法,update()方法,saveOrUpdate() 方法,merge() 方法,delete() 方法,evict(),hibernate 调用存储过程,hibernate 与 触发器协同工作
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- python学习之路 四 :文件处理
本节重点 掌握文件的读.写.修改方法 掌握文件的处理模式的区别 一.文件读取 1.读取全部内容 # 一次性读取文件 f = open("test.txt",'r',en ...
- Java学习之路(四):面向对象
Java中的面向对象 概念:面向对象的原本的意思是“”万物皆对象“” 面向对象思想的特点: 是一种更符合我们思想习惯的思想,将复杂的事情简单化 使我们角色发生了转换,将我们从执行者变成了指挥者 面向对 ...
- UART学习之路(二)基本时序介绍
这次我们来介绍一下UART的基本时序,了解一下底层信号怎么传送的.方便以后使用Verilog HDL实现收发逻辑. 9600bit/s 的意思是每秒发送9600bit,因此可以理解为将1s分解为960 ...
随机推荐
- cocos2d-x 的api
最近,在学习cocos2d-x,发现没有一个很好的api手册.因为起初我们学习一些例子之类的内容,会很容易使用,也很容易明白,但是当我们需要用新的api的时候,第一就会疑问有没有这个api,比如:你使 ...
- https填坑之旅
Boss说,我们买了个权威证书,不如做全站式的https吧,让用户打开主页就能看到受信任的绿标.于是我们就开始了填坑之旅. [只上主域好不好?] 不好...console会报出一大堆warning因为 ...
- CentOS7 Firewall超详细使用方法
CentOs7改变的最大处就是防火墙了,下面列用了常用的防火墙规则,端口转发和伪装 一.Firewalld基础规则 --get-default-zone 打印已设置为默认区域的当前区域,默认情况下默认 ...
- 50. Pow(x, n) (recursion)
Implement pow(x, n), which calculates x raised to the power n (xn). Example 1: Input: 2.00000, 10 Ou ...
- 用w32tm设置服务器时间同步
服务器时间同步是一个容易被忽视的问题,但在企业级应用环境中,不同服务器之间的时间差很可能引发应用系统问题.Windows提供的w32tm程序可以用来设置时间同步服务器,其用法如下: 1.指定外部时间源 ...
- CSAPP Bomb Lab记录
记录关于CSAPP 二进制炸弹实验过程 (CSAPP配套教学网站Bomb Lab自学版本,实验地址:http://csapp.cs.cmu.edu/2e/labs.html) (个人体验:对x86汇编 ...
- MATLAB入门学习(五)
现在,我们来学画图吧.╭( ・ㅂ・)و ̑̑ 绘制函数图像最常用的命令是plot plot(x,y,s)x,y为同维向量,绘制分别以x为横坐标,y为纵坐标的曲线 如果x y 是矩阵的话则会绘制多条曲线 ...
- POJ-2992 Divisors---组合数求因子数目
题目链接: https://cn.vjudge.net/problem/POJ-2992 题目大意: 给出组合数Cnk,求出其因子个数,其中n,k不大于431,组合数的值在long long范围内 解 ...
- 五·管理mysql
在上一篇文章中 四·安装mysql-5.7.16-linux-glibc2.5-x86_64.tar.gz(基于Centos7源码安装) 已经安装好了mysql,也正常启动了.本篇文章主要内容是管理m ...
- 【[NOI2016]区间】
发现自己的离散化姿势一直有问题 今天终于掌握了正确的姿势 虽然这并不能阻挡我noip退役爆零的历史进程 还是先来看看离散化怎么写吧,我以前都是这么写的 for(std::set<int>: ...