UART:是一种硬件功能,是一种主要采用异步串行通信方式的通用异步收发传输器。它通过使用通信接口(例如 RS232、RS422、RS485)来处理通信(即时序要求和数据帧),UART主要的功能:在发送时将并行数据转换为串行数据来传输;在接收时将串行数据转换为并行数据。
UART的三个接口标准RS232、RS422、RS485主要区别:
接口标准 | 逻辑1 | 逻辑 | 说明 | 优缺点 |
---|---|---|---|---|
RS232 | -15V | +15V | 负逻辑电平; 3线全双工; 点对点双向通信。 |
传输速度相对较低; 传输距离短 。 |
RS422 | 差值电压 +(2~5)V | 差值电压 - (2~5)V | 差分传输; 4线全双工; 点对多,主从通信; |
抗干扰能力强; 传输速度高; 传输距离远。 |
RS485 | 差值电压 +(2~5)V | 差值电压 -(2~5)V | 差分传输; 2线半双工; 多点双向通信; |
能实现多个发送; 接收设备双向通信。 |
简单说明一下并行传输和串行传输:
(1)并行传输:数据的各个位用多条数据线同时进行传输。
(2)串行传输:将数据分成一位一位的在同一条传输线上逐个传输。
简单说明一下单工和半双工以及全双工
(1)单工:数据只能沿一个方向传输;
(2)半双工:数据传输可以沿两个方向,但需要分时进行;
(3)全双工:数据可以同时进行双向传输。
使用FPGA进行UART串口通信的收发实验时,对其硬件实现功能了解个大概就足够了。软件进行UART串口通信的收发实验,主要是在通信协议下的格式,以及数据的传输速率进行设计。
(1)数据格式
UART传输数据的格式:从一个低位起始位 开始,后面是5~8个数据位,一个可用的奇偶检验位和一个或几个高位停止位。只有当接收器发现起始位时,才准备发送数据,并尝试与发送器时钟频率同步。如果选择了奇偶,UART就在数据位后面加上奇偶位。奇偶位可用来帮助错误校验(本次实验没有添加校验位)。
在UART中,信号线上共有两种状态, 分别用逻辑1(高电平)和逻辑0(低电平)来区分,在空闲时, 数据线应该保持在逻辑高电平状态。
其中各部分的具体意义如下:
1)起始位(Start Bit):先发出一个逻辑0信号, 表示传输字符的开始。
2)数据位(Data Bits):可以是5~8位逻辑0或1. 如ASCII码(8位)。
3)校验位(Parity Bit):数据位加上这一位后, 使得1的位数应为偶数(偶校验)或奇数(奇校验)。
4)停止位(Stop Bit):它是一个字符数据的结束标志。 可以是1位、1.5位、2位的高电平。
5)空闲位:处于逻辑1状态, 表示当前线路上没有数据传输。
(2)波特率
串口通信的速率用波特率表示,它表示每秒传输的二进制数据的位数,单位是bps(位/秒),常用的波特率有9600、19200、38400、57600、115200等,本次实验只选用了9600波特率下的UART。
以FPGA的系统时钟频率50MHz为基础,在9600波特率传输速率下,发送模块和接收模块的发送接收的时钟频率(时钟宽度)计算为:时钟频率为50MHz,说明1s发送或接收50_000_000bit数据,数据传输的速率要求为1s发送或接收9600bit数据,所以可讲50_000_000 / 9600 即可得到9600波特率下的时钟频率(计数值的最大值)
FIFO (First Input First Output):先进先出队列,是 FPGA 开发中使用频率很高的单元,主要应用在需要数据缓冲且数据符合先进先出规律的同步或异步场合。对于FIFO,最简单的形象对比,可以比作成在超市排队结账,先来的先结账,后来的后结账,当然其中去掉了插队这种特种情况。
就个人理解,FIFO IP核使用频率高是因为它在作为缓存单元时,读写相对独立:写入数据时,只要FIFO未满,无论读出端是否付出数据,都不影响写入端写入数据;读出数据时,只要FIFO内有数据,无论写入端是否写入数据,都不影响读出端读出数据。这个条件,给予了读写两端相应的自由空间操作,例如不同的传送速率,不同的传输判断条件等等。
通过Quartus II软件调用IP核功能调出FIFO IP核设置页面:
通过从电脑上的串口助手中发送数据,经过UART传输到FPGA的接收端;FPGA接受数据,并检验数据,在FIFO未满时,将其写入FIFO中;当FIFO中存有数据,FPGA通过UART将检验后的数据回返到电脑,在电脑上可通过串口助手观察到检验之后的数据。
所以具体所需要实现的功能:
(1)接收功能:
根据UART传输的数据格式,在检测到开始标识后,将接受的数据(8bit)寄存,时钟到停止位时,将数据传到检验模块进行检验。模块内需要进行波特率计数,传输的1bit位数计数,通过计数值,每接收1byte的数据产生1byte数据接收结束标志给检验模块。
(2)检验功能
接收来之UART_RXD模块的接收数据,根据UART_RXD模块的1byte数据接收结束标志对每一位数据检验,并产生写使能,将数据写入FIFO。
(3)发送功能
只要判断FIFO有数据(未空),即可读出数据进行发送。其中模块内也需要进行波特率计数,传输的1bit位数计数,通过计数值,每发送1byte的数据产生读使能,读FIFO中的下一字节的数据。
(1)顶层模块
引脚 | I/0 | BIT | 引脚说明 |
---|---|---|---|
clk | I | 1 | 系统时钟 |
rst_n | I | 1 | 低电平复位 |
rxd | I | 1 | PC端输入的串行数据 |
txd | O | 8 | PC端接收的串行数据 |
引脚 | I/0 | BIT | 引脚说明 |
---|---|---|---|
clk | I | 1 | 系统时钟 |
rst_n | I | 1 | 低电平复位 |
rxd | I | 1 | PC端输入的串行数据 |
fifo_ray | I | 1 | FIFO未满,低电平有效 |
din | O | 8 | FPGA接收的并行数据 |
rxd_vld | O | 1 | 接收完有效的一包8bit并行数据,高有效 |
(3)UART VERIFY
引脚 | I/0 | BIT | 引脚说明 |
---|---|---|---|
clk | I | 1 | 系统时钟 |
rst_n | I | 1 | 低电平复位 |
din | 8 | FPGA接收的并行数据 | |
rxd_vld | I | 1 | 接收完有效的一包8bit并行数据,高有效 |
dout_verify | O | 8 | 检验完一包8bit的有效接收数据 |
fifo_wren | O | 1 | fifo的写使能,高有效 |
(4)FIFO
引脚 | I/0 | BIT | 引脚说明 |
---|---|---|---|
clock(clk) | I | 1 | 系统时钟 |
aclr(rst_n) | I | 1 | 低电平复位 |
rdreq(fifo_rden) | I | 1 | FIFO读使能,高有效 |
wrreq(fifo_wren) | I | 1 | FIFO写使能,低有效 |
data(dout_verify) | I | 8 | FPGA接收的并行数据 |
almost_full() | O | 1 | 接收完有效的一包8bit并行数据,高有效 |
empty() | O | 1 | 接收完有效的一包8bit并行数据,高有效 |
q(dout) | O | 8 | FPGA接收的并行数据 |
(5)UART TXD
引脚 | I/0 | BIT | 引脚说明 |
---|---|---|---|
clk | I | 1 | 系统时钟 |
rst_n | I | 1 | 低电平复位 |
dout | I | 8 | 从FIFO内读出的数据 |
fifo_rden | O | 1 | FIFO的读出使能,高有效 |
txd | O | 1 | FPGA输出的串行数据 |
在顶层模块中需要例化四个模块:
(1)uart_rxd:接收模块
(2)uart_verfy:FPGA内部校验模块
(3)fifo_8b_8b:调用FIFO IP核,8bit输入8bit输出
(4)uart_txd:发送模块
顶层模块代码
module UART_top(
//INPUT
input clk, //System clock
input rst_n, //Reset signal is inverted
input rxd, //uart rxd
//OUTPUT
output wire txd //uart txd
);
//=======================================================================//
wire fifo_rden; //FIFO send enable
wire din_vld; //AOS 1 byte number send end enable
wire almost_full;
wire almost_empty;
wire empty;
wire full;
wire [7:0] data; //AOS 1 byte send data
wire [7:0] q;
wire [7:0] usedw;
wire [7:0] dout_verify;
wire [7:0] din;
//=======================================================================//
//uart rxd
uart_rxd U1_uart_rxd(
.clk ( clk ),
.rst_n ( rst_n ),
.rxd ( rxd ),
.fifo_rdy ( almost_full ),
.din ( din ),
.din_vld ( din_vld )
);
//=======================================================================//
//inst a fifo: 8bit input and 8bit output
fifo_8b_8b fifo_8b_8b_inst (
.aclr ( ~rst_n ), //Reset signal is inverted
.clock ( clk ), //System clock
.data ( data ), //Write data
.rdreq ( fifo_rden ), //Read enable
.wrreq ( fifo_wren ), //Write enable
.empty ( empty ), //FIFO is null signal
.full ( full ), //FIFO full signal
.almost_full ( almost_full ),
.almost_empty ( almost_empty ),
.q ( q ), //Read data
.usedw ( usedw ) //Amount of available data
);
//=======================================================================//
//uart txd
uart_txd U2_uart_txd(
.clk ( clk ),
.rst_n ( rst_n ),
.txd_vld ( empty ),
.dout ( q ),
.fifo_rden ( fifo_rden ),
.txd ( txd )
);
//=======================================================================//
uart_verify U3_uart_verify(
.clk (clk ),
.rst_n (rst_n ),
.din_vld (din_vld ),
.din (din ),
.dout_verify (data ),
.fifo_wren (fifo_wren )
);
//=======================================================================//
endmodule
无论是接收模块还是发送模块至少都需要两种计数器,第一种功能的计数器是:根据UART的传输协议,以及确定好的传输波特率,基于FPGA的系统时钟频率,计数UART结束或发送每1bit数据的频率(时钟宽度):
if(rxd_flag!=0)
begin
if(clk_cnt==BPS_CNT-1)
begin
clk_cnt<=0;
end
else
begin
clk_cnt<=clk_cnt+32'd1;
end
end
else
begin
clk_cnt<=32'd0;
end
该计数器下可设置一个产生采集信号的计数器,每当计数值达到中间位置时,采集信号有效,对数据进行寄存:
if(clk_cnt==BPS_CNT_1-1) //在分频计数的中间部分启动信号采集
begin
rxd_sele<=1'b1; //采集信号时钟沿落在有效数据中间
end
else
begin
rxd_sele<=1'b0;
end
if(rxd_flag!=0)
begin
if( rxd_sele !=0 )
begin
rxd_num <= rxd_num+1'd1;
end
else
begin
rxd_num <= rxd_num;
end
end
else
begin
if(rxd_num==4'd10)
begin
rxd_num<=4'd0;
end
else
begin
rxd_num<=rxd_num;
end
end
对uart_flag进行说明,uart_flga是uart_rxd在作用时的标志符,高电平有效,当检测到接收数据的开始位,即从PC端传输的rxd信号为下降沿时开始有效,一直持续到接收到停止位(也就是第九位数据拉低),再一次检测到数据的开始位为下降沿,有效。主要是作为uart_rxd的一个有效标志位,接收数据存入FPGA的内存寄存器中:
if(rxd_en!=0)
begin
rxd_flag<=1'b1;
end
else
begin
if(rxd_num==4'd10)
begin
rxd_flag<=1'b0;
end
else
begin
rxd_flag<=rxd_flag;
end
end
数据的开始位为下降沿的判断实现代码,即当检测到,停止位之后(高电平),txd为低电平(起始位):
always @ (posedge clk or negedge rst_n)
begin
if(rst_n==0)
begin
rxd_d0<=1'b1;
rxd_d1<=1'b1;
end
else
begin
rxd_d0<=rxd;
rxd_d1<=rxd_d0;
end
end
// Detect falling edge //
assign rxd_en = rxd_d1 & ( ~rxd_d0 );
if( rxd_sele !=0 )
begin
case( rxd_num )
4'd0 : ;//忽略开始位
4'd1 : begin din_r[0] <= rxd; end //采集中间八位有效数据
4'd2 : begin din_r[1] <= rxd; end
4'd3 : begin din_r[2] <= rxd; end
4'd4 : begin din_r[3] <= rxd; end
4'd5 : begin din_r[4] <= rxd; end
4'd6 : begin din_r[5] <= rxd; end
4'd7 : begin din_r[6] <= rxd; end
4'd8 : begin din_r[7] <= rxd; end
4'd9 : begin din_d <= din_r; end//锁存采集的 8 位有效数据
default : ;
endcase
end
接收模块的整体代码
module uart_rxd(
input clk,
input rst_n,
input rxd,
input fifo_rdy,
output wire [7:0] din,
output wire din_vld
);
//=======================================================================//
parameter CLK = 50_000_000; //FPGA System clock frequency
parameter BPS = 9_600; //Baud rate
localparam BPS_CNT = CLK / BPS; //
localparam BPS_CNT_1 = BPS_CNT / 2; //
//=======================================================================//
reg din_vld_r;
reg [35:0] clk_cnt;
reg rxd_flag;
reg rxd_sele;
reg rxd_d0; //同步
reg rxd_d1; //打拍
reg [3:0] rxd_num;
reg [7:0] din_r;
reg [7:0] din_d;
//=======================================================================//
wire rxd_en; //Detect falling edge
//=======================================================================//
// Synchronization and 1 beat Send enable(rxd_vld)
always @ (posedge clk or negedge rst_n)
begin
if(rst_n==0)
begin
rxd_d0<=1'b1;
rxd_d1<=1'b1;
end
else
begin
rxd_d0<=rxd;
rxd_d1<=rxd_d0;
end
end
// Detect falling edge //
assign rxd_en = rxd_d1 & ( ~rxd_d0 );
//=======================================================================//
always @ ( posedge clk or negedge rst_n )
begin
if(rst_n==0)
begin
rxd_flag <= 1'b0;
end
else
begin
if( fifo_rdy ==0 )
begin
if(rxd_en!=0)
begin
rxd_flag<=1'b1;
end
else
begin
if(rxd_num==4'd10)
begin
rxd_flag<=1'b0;
end
else
begin
rxd_flag<=rxd_flag;
end
end
end
else
begin
rxd_flag <= 0;
end
end
end
//=======================================================================//
always @ (posedge clk or negedge rst_n)
begin
if(rst_n==0)
begin
clk_cnt<=31'd0;
end
else
begin
if(rxd_flag!=0)
begin
if(clk_cnt==BPS_CNT-1)
begin
clk_cnt<=0;
end
else
begin
clk_cnt<=clk_cnt+32'd1;
end
end
else
begin
clk_cnt<=32'd0;
end
end
end
//=======================================================================//
always@(posedge clk or negedge rst_n)
begin
if(rst_n==0)
begin
rxd_sele<=1'b0;
end
else
begin
if(clk_cnt==BPS_CNT_1-1) //在分频计数的中间部分启动信号采集
begin
rxd_sele<=1'b1; //采集信号时钟沿落在有效数据中间
end
else
begin
rxd_sele<=1'b0;
end
end
end
//=======================================================================//
// rxd_num: receive data bit count
always @ (posedge clk or negedge rst_n)
begin
if(rst_n==0)
begin
rxd_num<=4'd0;
end
else
begin
if(rxd_flag!=0)
begin
if( rxd_sele !=0 )
begin
rxd_num <= rxd_num+1'd1;
end
else
begin
rxd_num <= rxd_num;
end
end
else
begin
if(rxd_num==4'd10)
begin
rxd_num<=4'd0;
end
else
begin
rxd_num<=rxd_num;
end
end
end
end
//=======================================================================//
// uart receive data
always@(posedge clk or negedge rst_n)
begin
if(rst_n==0)
begin
din_d <= 8'd0;
din_r <= 8'd0;
end
else
if( rxd_sele !=0 )
begin
case( rxd_num )
4'd0 : ;//忽略开始位
4'd1 : begin din_r[0] <= rxd; end //采集中间八位有效数据
4'd2 : begin din_r[1] <= rxd; end
4'd3 : begin din_r[2] <= rxd; end
4'd4 : begin din_r[3] <= rxd; end
4'd5 : begin din_r[4] <= rxd; end
4'd6 : begin din_r[5] <= rxd; end
4'd7 : begin din_r[6] <= rxd; end
4'd8 : begin din_r[7] <= rxd; end
4'd9 : begin din_d <= din_r; end//锁存采集的 8 位有效数据
default : ;
endcase
end
end
//=======================================================================//
// Generate UART receive end enable
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n == 0 )
begin
din_vld_r <= 0;
end
else
begin
if( rxd_num == 10 )
begin
din_vld_r <= 1;
end
else
begin
din_vld_r <= 0;
end
end
end
//=======================================================================//
assign din_vld = din_vld_r;
assign din = din_d;
//=======================================================================//
endmodule
检验模块的实现非常简单,只要检测到接收模块的1byte数据接收接收变标志位就进行检验,检验完毕后产生FIFO的写使能,将检验完毕的数据写入FIFO中,检验的条件是接收的数据以ASCII码的形式为0~9和空格时,正常输出,不然数据输出为ASCII码的大写字母A。
检验模块的整体代码
module uart_verify(
input clk,
input rst_n,
input din_vld,
input [7:0] din,
output wire [7:0] dout_verify,
output wire fifo_wren
);
//=======================================================================//
reg data_vld;
reg fifo_wren_r1; //
reg fifo_wren_r2; //
reg fifo_wren_r3;
reg verify_d0; //
reg verify_en; //
reg [7:0] dout_verify_r;
//=======================================================================//
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n==0 )
begin
data_vld <= 0;
end
else
begin
if(( din<=8'b0011_1001 && din >= 8'b0011_0000 ) || ( din == 8'b0010_0000 ))
begin
data_vld <= 1;
end
else
begin
data_vld <= 0;
end
end
end
//=======================================================================//
//Delay 1 CLK clock, and start outputting dout after determining whether DIN is a number 0-9 and a space_ verify
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n == 0 )
begin
verify_d0 <= 1'b1;
verify_en <= 1'b1;
end
else
begin
verify_d0 <= din_vld;
verify_en <= verify_d0;
end
end
//=======================================================================//
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n == 0 )
begin
fifo_wren_r1 <= 1'b0;
fifo_wren_r2 <= 1'b0;
fifo_wren_r3 <= 1'b0;
end
else
begin
fifo_wren_r1 <= din_vld;
fifo_wren_r2 <= fifo_wren_r1;
fifo_wren_r3 <= fifo_wren_r2;
end
end
assign fifo_wren = fifo_wren_r3;
//=======================================================================//
always @ (posedge clk or negedge rst_n)
begin
if( rst_n == 0 )
begin
dout_verify_r <= 0;
end
else
begin
if( verify_en != 0 )
begin
if( data_vld != 0 )
begin
dout_verify_r <= din;
end
else
begin
dout_verify_r <= 8'b0100_0001;
end
end
else
begin
dout_verify_r <= dout_verify_r;
end
end
end
//=======================================================================//
assign dout_verify = dout_verify_r;
//=======================================================================//
endmodule
发送模块的整体结构与接收模块非常相似,同样至少需要两种功能的计数器,计数器的实现方式是一样的,不同的是发送模块的有效判断和数据发送的实现功能:
发送模块有效标志uart_txd_flag,将fifo有数据,有效标志就为高电平:
if( txd_vld == 0 )
begin
uart_txd_flag <= 1;
end
else
begin
uart_txd_flag <= 0;
end
发送模块实现发送功能代码,在采集信号有效下,将数据从低位到高位进行串行发送:
if(txd_sele!=1'b0)
begin
case(txd_num)
4'd0 : begin txd_r <= 1'b0; end //The start bit is low
4'd1 : begin txd_r <= dout[0]; end
4'd2 : begin txd_r <= dout[1]; end
4'd3 : begin txd_r <= dout[2]; end
4'd4 : begin txd_r <= dout[3]; end
4'd5 : begin txd_r <= dout[4]; end
4'd6 : begin txd_r <= dout[5]; end
4'd7 : begin txd_r <= dout[6]; end
4'd8 : begin txd_r <= dout[7]; end
4'd9 : begin txd_r <= 1'b1; end //The end bit is high
default : begin txd_r <= 1'b1; end
endcase
end
发送模块的整体代码
module uart_txd(
//INPUT
input clk, //System clock
input rst_n, //Reset signal is inverted
input txd_vld, //UART start sending enable
input [7:0] dout, //UART send data
//OUTPUT
output wire fifo_rden, //Enable UART to receive data sent by FIFO
output wire txd //UART txd
);
//=======================================================================//
parameter CLK = 50_000_000; //FPGA System clock frequency
parameter BPS = 9_600; //Baud rate
localparam BPS_CNT = CLK / BPS; //
localparam BPS_CNT_1 = BPS_CNT / 2; //
//=======================================================================//
reg fifo_rden_r;
reg txd_sele; //
reg uart_txd_flag; //UART start action identifier
reg txd_flag; //1 byte Send flag bit
reg txd_r;
reg [3:0] txd_num; //Send data bit count
reg [31:0] clk_cnt; //Baud rate count
//=======================================================================//
// uart_txd_flag:UART start action identifier , FIFO starts only when there is data in it
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n == 1'b0 )
begin
uart_txd_flag <=0 ;
end
else
begin
if( txd_vld == 0 )
begin
uart_txd_flag <= 1;
end
else
begin
uart_txd_flag <= 0;
end
end
end
//=======================================================================//
// txd_flag:Send enable is detected and send flag bit is generated
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n == 1'b0 )
begin
txd_flag <= 1'b0;
end
else
begin
if( uart_txd_flag != 0 )
begin
txd_flag <= 1'b1;
end
else
begin
if( txd_num == 4'd10 )
begin
txd_flag <= 1'b0;
end
else
begin
txd_flag <= txd_flag;
end
end
end
end
//=======================================================================//
// clk_cnt: Generate baud rate count according to baud rate calculation
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n == 1'b0 )
begin
clk_cnt <= 31'd0;
end
else
begin
if( txd_flag != 0 )
begin
if( clk_cnt <= BPS_CNT - 32'd1 )
begin
clk_cnt <= clk_cnt + 32'd1;
end
else
begin
clk_cnt <= 32'd0;
end
end
end
end
//=======================================================================//
// txd_sele: Collect in the middle of valid data
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n == 0 )
begin
txd_sele <= 1'b0;
end
else
begin
if( clk_cnt == BPS_CNT_1 ) //Start signal transmission in the middle part of frequency division counting
begin
txd_sele <= 1'b1; //The clock edge of the transmission signal falls in the middle of the valid data
end
else
begin
txd_sele <= 1'b0;
end
end
end
//=======================================================================//
// txd_num: Send data bit count
always @ (posedge clk or negedge rst_n)
begin
if( rst_n == 0 )
begin
txd_num <= 4'd0;
end
else
begin
if( txd_sele !=0 && txd_flag != 0 )
begin
txd_num <= txd_num + 4'd1;
end
else
begin
if( txd_num == 4'd10 )
begin
txd_num <= 4'd0;
end
else
begin
txd_num <= txd_num;
end
end
end
end
//=======================================================================//
// uart Send data
always@( posedge clk or negedge rst_n )
begin
if( rst_n == 1'b0 )
begin
txd_r <= 1'b1;
end
else
begin
if(txd_sele!=1'b0)
begin
case(txd_num)
4'd0 : begin txd_r <= 1'b0; end //The start bit is low
4'd1 : begin txd_r <= dout[0]; end
4'd2 : begin txd_r <= dout[1]; end
4'd3 : begin txd_r <= dout[2]; end
4'd4 : begin txd_r <= dout[3]; end
4'd5 : begin txd_r <= dout[4]; end
4'd6 : begin txd_r <= dout[5]; end
4'd7 : begin txd_r <= dout[6]; end
4'd8 : begin txd_r <= dout[7]; end
4'd9 : begin txd_r <= 1'b1; end //The end bit is high
default : begin txd_r <= 1'b1; end
endcase
end
else
begin
txd_r <= txd_r;
end
end
end
//=======================================================================//
// Generate FIFO send enablele
always @ ( posedge clk or negedge rst_n )
begin
if( rst_n == 0 )
begin
fifo_rden_r <= 0;
end
else
begin
if(txd_num == 9 )
begin
fifo_rden_r <= 0;
end
else
begin
fifo_rden_r <= 1;
end
end
end
//=======================================================================//
assign txd = txd_r;
assign fifo_rden = fifo_rden_r;
//=======================================================================//
endmodule
在进行modelsim仿真时,使用的是Quratus ii与Modelsim联合仿真,通过Quratus ii生成测试文件,在相应的地方进行修改,最后将文件添加到Quratus ii内的仿真测试文件,通过Quratus ii逻辑仿真功能启动Modelsim进行仿真。
测试文件的整体代码
`timescale 1 ns/ 1 ps
module UART_top_vlg_tst();
reg clk;
reg rst_n;
reg rxd;
wire txd;
UART_top i1 (
.clk(clk),
.rst_n(rst_n),
.rxd(rxd),
.txd(txd)
);
initial
begin
clk = 0;
rst_n = 0;
rxd= 1; //在复位阶段,将激励赋初值
#200 rst_n = 1; //延时 200ns 后停止复位
#110000 rxd= 0;
#110000 rxd= 0;//发送数据 8'ha4 (8'b0011_0000) ASII 码 0
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 1;//结束位
#1300000;
#110000 rxd= 0;
#110000 rxd= 1;//发送数据 8'ha4 (8'b0011_0101) ASII 码 2
#110000 rxd= 0;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 1;//结束位
#1300000;
#110000 rxd= 0;
#110000 rxd= 1;//发送数据 8'ha4 (8'b0110_0001) ASII 码 a
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 1;//结束位
#1300000;
#110000 rxd= 0;
#110000 rxd= 1;//发送数据 8'ha4 (8'b0110_1011) ASII 码 k
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 1;//结束位
#1300000;
#110000 rxd= 0;
#110000 rxd= 0;//发送数据 8'ha4 (8'b0111_1110) ASII 码 ~
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 1;//结束位
#1300000;
#110000 rxd= 0;
#110000 rxd= 0;//发送数据 8'ha4 (8'b0010_0000) ASII 码 空格
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 1;//结束位
#1300000;
#110000 rxd= 0;
#110000 rxd= 1;//发送数据 8'ha4 (8'b0011_1001) ASII 码 9
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 1;
#110000 rxd= 0;
#110000 rxd= 0;
#110000 rxd= 1;//结束位
end
always
begin
#10 clk = ~clk;
end
endmodule
在测试文件中模拟PC发送0、2、a、k、~空格 、9;
&emsp:可通过微软应用商店下载的串口调试助手作为实验的串口通信收发实验的PC端,FPGA的引脚配置根据FPGA开发板的开发手册上的引脚说明进行配置。
1、在uart_rxd和uart_txd之间加一个FIFO是为了提供一个数据缓存的区域,其中基于FIFO具有读写独立,可以将接受模块和发送模块的波特率设置为不同值来进行UART功能的测试。
2、其中在三个模块都进行了不同程度同步打拍,主要作用:
(1)为了让一些使能信号更加稳定,避免亚稳态;
(2)为了延时时钟,等待数据传输或是检验完毕后才进行操作。
3、可以在再增加一个波特率选者模块,来控制波特的大小选择。
4、通过在检验模块中加一个中断fifo的写入,中断过程中可以设置检验到错误的位置和个数,限于时间能力,只有概念而没有做出来。
1、UART和RS485串口通信原理与实践(ZYNQ、Vivado)
2、基于RS422通信的FPGA软件设计第四天
3、uart接口定义详解介绍(基本结构及工作原理)