EDA课程设计实验报告
题 目:全双工UART电路的设计
院 系: XXXXXXX学院
班 级: XXXXXXX工程
学 号: XXXXXXXXX
姓 名: 张XXXX
指导老师: 林XXXX
时 间: 20##-05-29
目录
1 设计要求... 1
2 UART设计... 2
2.1 UART结构... 2
2.2 UART的帧格式... 3
3 UART的Verilog HDL语言设计... 4
3.1 UART分频器... 4
3.2 UART发送模块... 5
3.3 UART的接收模块... 10
3.4 UART的硬件测试... 14
4 总 结... 16
1 设计要求
一、设计一个全双工UART电路,具体要求如下:
1)支持数据格式:起始位(1bit)+数据(8bit)+奇偶校验位(1bit)+终止位(1bit)
2)奇/偶校验可配置
3)可配置支持115200以下的常见波特率
4)支持115200以下的波特率自适应,自适应过程如下:
a. 复位后,UART首先接收输入,不断自动调整波特率,直到以一定波特率正确连续接收到3个bytes的0x55
b. 接着UART以此波特率连续发送3个bytes 0xaa
c. 之后两端以此波特率进行通信
d. 波特率自适应只在电路复位后进行一次,如欲再次自适应波特率应对电路再次复位
e. 波特率自适应过程中不能对UART的波特率作任何设置,自适应完成后可以对波特率作设置
5)自动计算校验位用于发送数据;对接收到的校验位和数据进行校验,发现错误应设置错误标志,并丢弃数据
6)对接收不正常数据(如无终止位、无校验位、数据位数不正确等)应能自动识别并设置错误标志、丢弃
7)额外任何你认为有用又可以实现的功能
8)
二、设计工具:
1)所有电路采用VerilogHDL或原理图方法进行设计
2)在QuartusII下进行设计
3)综合和仿真可以采用其他工具,如综合可以采用Synplify,仿真可以采用ModelSim
4)目标器件采用与实验箱相同的器件
2 UART设计
通常设计数字电路大都采用自顶向下将系统按功能逐层分割的层次化设计方法,这比传统自下向上的EDA设计方法有更明显的优势(当时的主要设计文件是电路图)。因为由自顶向下的设计过程可以看出,从总体行为设计开始到最终逻辑综合,形成网络表为止。每一步都要进行仿真检查,这样有利于尽早发现系统设计中存在的问题,从而可以大大缩短系统硬件的设计周期。
UART(即Universal Asynchronous Receiver Transmitter 通用异步收发器)是一种应用广泛的短距离串行传输接口。UART允许在串行链路上进行全双工的通信。串行外设用到的RS232-C异步串行接口,一般采用专用的集成电路即UART实现。如8250、8251、NS16450等芯片都是常见的UART器件,这类芯片已经相当复杂,有的含有许多辅助的模块(如FIFO),有时我们不需要使用完整的UART的功能和这些辅助功能。或者设计上用到了FPGA/CPLD器件,那么我们就可以将所需要的UART功能集成到FPGA内部。使用VHDL将UART的核心功能集成,从而使整个设计更加紧凑、稳定且可靠。本文应用EDA技术,基于FPGA/CPLD器件设计与实现UART。
2.1 UART结构
UART主要有由数据总线接口、控制逻辑、波特率发生器、发送部分和接收部分等组成。本设计主要设计UART中最重要的发送部分和接收部分 ,结构如下图2-1-1:
图2-1-1
2.2 UART的帧格式
UART的帧格式如图2-2-1所示。
图2-2-1
发送数据过程:空闲状态,线路处于高电位;当收到发送数据指令后,拉低线路一个数据位的时间T,接着数据按低位到高位依次发送,数据发送完毕后,接着发送奇偶校验位和停止位(停止位为高电位),一帧资料发送结束。
接收数据过程:空闲状态,线路处于高电位;当检测到线路的下降沿(线路电位由高电位变为低电位)时说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,接着接收并比较奇偶校验位是否正确,如果正确则通知后续设备准备接收数据或存入缓存。
UART是异步传输,没有传输同步时钟。为了能保证数据传输的正确性,UART采用16倍数据波特率的时钟进行采样。每个数据有16个时钟采样,取中间的采样值,以保证采样不会滑码或误码。一般UART一帧的数据位数为8,这样即使每个数据有一个时钟的误差,接收端也能正确地采样到数据。
UART的接收数据时序为:当检测到数据的下降沿时,表明线路上有数据进行传输,这时计数器CNT开始计数,当计数器为24=16+8时,采样的值为第0位数据;当计数器的值为40时,采样的值为第1位数据,依此类推,进行后面6个数据的采样。如果需要进行奇偶校验,则当计数器的值为152时,采样的值即为奇偶位;当计数器的值为168时,采样的值为“1”表示停止位,一帧数据接收完成。
3 UART的Verilog HDL语言设计
3.1 UART分频器
假设数据的波特率为p,则所需时钟的频率为16*p。以波特率p为9600为例,系统时钟为12MHz,则分频系数为12000000/(16*9600) = 78.125,取整为78。Verilog HDL语言代码如下:
module clkdiv(clk, clkout);
input clk; //系统时钟
output clkout; //采样时钟输出
reg clkout;
reg [15:0] cnt;
always @(posedge clk) //分频进程
begin
if(cnt == 16'd38)
begin
clkout <= 1'b1;
cnt <= cnt + 16'd1;
end
else if(cnt == 16'd77)
begin
clkout <= 1'b0;
cnt <= 16'd0;
end
else
begin
cnt <= cnt + 16'd1;
end
end
endmodule
保存文件为clkdiv.v,单击Files → Create/Update → Create Symbol Files for Current File命令,为clkdiv.v生成原理图模块。新建一个原理图文件,在原理图空白处双击,在弹出的Symbol对话框中选择Project → clkidv模块,单击OK按钮退出Symbol对话框。在原理图的适当位置放置clkdiv模块,并添加输入输出模块。保存原理图为uartrxtx.bdf。编译工程文件,编译无误后单击Processing → Generate Functional Simulation Netlist,产生功能仿真网表。新建波形仿真文件,加入输入输出信号,设置系统时钟信号clk的周期为20ns,保存波形文件为 uartrxtx.vwf,单击按钮进行分频器的波形仿真。
图3-1-1
3.2 UART发送模块
UART发送模块的功能:接收到发送指令后,把数据按UART协议输出,先输出一个低电平的起始位,然后从低到高输出8个数据位,接着是可选的奇偶校验位,最后是高电平的停止位。Verilog HDL语言代码如下:
module uarttx(clk, datain, wrsig, idle, tx);
input clk; //UART时钟
input [7:0] datain; //需要发送的数据
input wrsig; //发送命令,上升沿有效
output idle; //线路状态指示,高为线路忙,低为线路空闲
output tx; //发送数据信号
reg idle, tx;
reg send;
reg wrsigbuf, wrsigrise;
reg presult;
reg[7:0] cnt; //计数器
parameter paritymode = 1'b0;
//检测发送命令是否有效
always @(posedge clk)
begin
wrsigbuf <= wrsig;
wrsigrise <= (~wrsigbuf) & wrsig;
end
always @(posedge clk)
begin
if (wrsigrise && (~idle)) //当发送命令有效且线路为空闲时,启动新的数据发送进程
begin
send <= 1'b1;
end
else if(cnt == 8'd176) //一帧资料发送结束
begin
send <= 1'b0;
end
end
always @(posedge clk)
begin
if(send == 1'b1)
begin
case(cnt) //产生起始位
8'd0:
begin
tx <= 1'b0;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd16:
begin
tx <= datain[0]; //发送数据0位
presult <= datain[0]^paritymode;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd32:
begin
tx <= datain[1]; //发送数据1位
presult <= datain[1]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd48:
begin
tx <= datain[2]; //发送数据2位
presult <= datain[2]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd64:
begin
tx <= datain[3]; //发送数据3位
presult <= datain[3]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd80:
begin
tx <= datain[4]; //发送数据4位
presult <= datain[4]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd96:
begin
tx <= datain[5]; //发送数据5位
presult <= datain[5]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd112:
begin
tx <= datain[6]; //发送数据6位
presult <= datain[6]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd128:
begin
tx <= datain[7]; //发送数据7位
presult <= datain[7]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd144:
begin
tx <= presult; //发送奇偶校验位
presult <= datain[0]^paritymode;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd160:
begin
tx <= 1'b1; //发送停止位
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd176:
begin
tx <= 1'b1;
idle <= 1'b0; //一帧资料发送结束
cnt <= cnt + 8'd1;
end
default:
begin
cnt <= cnt + 8'd1;
end
endcase
end
else
begin
tx <= 1'b1;
cnt <= 8'd0;
idle <= 1'b0;
end
end
endmodule
保存文件为uarttx.v,单击Files → Create/Update → Create Symbol Files for Current File命令,为uarttx.v生成原理图模块。为了测试UART发送模块的正确性,需要编写一个测试模块来测试UART发送模块,Verilog HDL语言代码如下:
module testuart(clk, dataout, wrsig);
input clk;
output[7:0] dataout;
output wrsig;
reg [7:0] dataout;
reg wrsig;
reg [7:0] cnt;
always @(posedge clk)
begin
if(cnt == 254)
begin
dataout <= dataout + 8'd1; //每次数据加“1”
wrsig <= 1'b1; //产生发送命令
cnt <= 8'd0;
end
else
begin
wrsig <= 1'b0;
cnt <= cnt + 8'd1;
end
end
endmodule
保存文件为testuart.v,单击Files → Create/Update → Create Symbol Files for Current File命令,为testuart.v生成原理图模块。新建一个原理图文件,在原理图空白处双击,在弹出的Symbol对话框中选择Project → testuart模块和uarttx模块,单击OK按钮退出Symbol对话框。在原理图的适当位置放置testuart模块和uarttx模块,并添加输入输出模块。为了仿真方便,把原来分频模块的分频系数更改为4,各个模块的连接如图3-2-1所示。
图3-2-1 UART发送模块
保存原理图为uartrxtx.bdf。编译工程文件,编译无误后单击Processing → Generate Functional Simulation Netlist,产生功能仿真网表。新建波形仿真文件,加入输入输出信号,设置系统时钟信号clk的周期为20ns,保存波形文件为uartrxtx.vwf,单击按钮进行UART数据发送的波形仿真,波形仿真图如下图3-2-2:
图3-2-2 UART发送模块的波形仿真图
波形仿真说明:
当发送命令wrsig的上升沿有效时,启动发送数据。串行数据的波形与发送数据dataout相一致,UART的发送模块得到正确验证。
3.3 UART的接收模块
UART接收模块的功能:时时检测线路,当线路产生下降沿时,即认为线路有数据传输,启动接收数据进程进行接收,按从低位到高位接收数据。UART接收模块的Verilog HDL语言代码如下:
module uartrx(clk, rx, dataout, rdsig, dataerror, frameerror);
input clk; //采样时钟
input rx; //UART数据输入
output dataout; //接收数据输出
output rdsig;
output dataerror; //资料出错指示
output frameerror; //帧出错指示
reg[7:0] dataout;
reg rdsig, dataerror;
reg frameerror;
reg [7:0] cnt;
reg rxbuf, rxfall, receive;
parameter paritymode = 1'b0;
reg presult, idle;
always @(posedge clk) //检测线路的下降沿
begin
rxbuf <= rx;
rxfall <= rxbuf & (~rx);
end
always @(posedge clk)
begin
if (rxfall && (~idle)) //检测到线路的下降沿并且原先线路为空闲,启动接收数据进程
begin
receive <= 1'b1;
end
else if(cnt == 8'd175) //接收数据完成
begin
receive <= 1'b0;
end
end
always @(posedge clk)
begin
if(receive == 1'b1)
begin
case (cnt)
8'd0:
begin
idle <= 1'b1;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd24: //接收第0位数据
begin
idle <= 1'b1;
dataout[0] <= rx;
presult <= paritymode^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd40: //接收第1位数据
begin
idle <= 1'b1;
dataout[1] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd56: //接收第2位数据
begin
idle <= 1'b1;
dataout[2] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd72: //接收第3位数据
begin
idle <= 1'b1;
dataout[3] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd88: //接收第4位数据
begin
idle <= 1'b1;
dataout[4] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd104: //接收第5位数据
begin
idle <= 1'b1;
dataout[5] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd120: //接收第6位数据
begin
idle <= 1'b1;
dataout[6] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd136: //接收第7位数据
begin
idle <= 1'b1;
dataout[7] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b1;
end
8'd152: //接收奇偶校验位
begin
idle <= 1'b1;
if(presult == rx)
dataerror <= 1'b0;
else
dataerror <= 1'b1; //如果奇偶校验位不对,表示数据出错
cnt <= cnt + 8'd1;
rdsig <= 1'b1;
end
8'd168:
begin
idle <= 1'b1;
if(1'b1 == rx)
frameerror <= 1'b0;
else
frameerror <= 1'b1; //如果没有接收到停止位,表示帧出错
cnt <= cnt + 8'd1;
rdsig <= 1'b1;
end
default:
begin
cnt <= cnt + 8'd1;
end
endcase
end
else
begin
cnt <= 8'd0;
idle <= 1'b0;
rdsig <= 1'b0;
end
end
endmodule
保存文件为uartrx.v,单击Files → Create/Update → Create Symbol Files for Current File命令,为uartrx.v生成原理图模块。新建一个原理图文件,在原理图空白处双击,在弹出的Symbol对话框中选择Project → uartrx模块,单击OK按钮退出Symbol对话框。在原理图的适当位置放置uartrx模块,并添加输入输出模块,各个模块的连接如图3-3-1:
图3-3-1 UART接收模块
保存原理图为uartrxtx.bdf。编译工程文件,编译无误后单击Processing → Generate Functional Simulation Netlist,产生功能仿真网表。新建波形仿真文件,加入输入输出信号,设置系统时钟信号clk的周期为20ns,保存波形文件为 uartrxtx.vwf,单击按钮进行UART数据接收的波形仿真,波形仿真报告如图3-3-2所示。
图 3-3-2 UART接收模块的波形仿真图
波形仿真说明:
UART接收模块接收到的数据与UART发送模块发送的数据相一至,每接收到一个数据都有一个读取数据指示rdisg,UART接收模块得到正确验证。
3.4 UART的硬件测试
为了测试UART与PC通信的正确性,本例测试方法是,PC将数据发送到FPGA,FPGA接收到数据再发送给PC。FPGA与PC通信模块连接原理图如图3-4-1所示。
图3-4-1 FPGA与PC通信模块连接原理图
根据PC的串口调试工具的设置,决定代码是否需要奇偶校验位,以及分频系数。分配FPGA引脚,编译工程,连接好开发板及下载线缆,接上电源,下载配置FPGA,打开PC的串口调试工具,发送数据,观察接收到的数据,如图3-4-2所示。
图 3-4-2 PC串口调试工具
PC串口调试工具中发送“34”,同时接收窗口显示“34”,说明FPGA的接收和发送模块与PC的通信正确。至此,UART收发器的验证全部完成。
4 总 结
本设计由于采用了Verilog HDL语言作为输入方式并结合FPGA/CPLD,大大缩短了设计周期,提高了设计的可靠性、灵活性,使用户可根据自己的需求,方便、高效地设计出适合的串行接口电路。用FPGA 来对UART接口进行开发,可以减小系统的PCB面积,降低系统的功耗,提高设计的稳定性和可靠性,并可以充分利用了FPGA 的剩余资源。该设计具有很大的灵活性,虽然这一UART接口工作在每秒9600波特,但通过调整锁相环的参数,就可以使其工作在其他频率,十分方便。这仅仅是一个简单的通信接口,可根据不同系统的需要,增加FIFO等内容。该模块也可以作为一个完整的IP核,灵活地移植进各种型号的FPGA中,通用性很强。