目录
- 一、UART协议基础
- 二、系统模块划分
- 三、代码实现
- 1、uart顶层设计模块
- 2、uart_rx串口数据接收模块
- 3、control控制模块
- 4、uart_tx串口数据发送模块
- 四、仿真
- 五、上板验证
- 六、踩坑事项
一、UART协议基础
关于UART协议的基础理论部分已经在上一篇文章中讲述,不再重复介绍。
UART通信协议
本文主要介绍如何使用Verlilog编程,通过FPGA实现UART串口通信回环工程。在本工程中所使用的系统时钟为50MHz,如果选择115200的波特率进行数据传输,那么传输1bit所用的时钟周期数就是50_000_000 / 115200。
二、系统模块划分
uart模块: uart串口通信顶层设计模块,包含uart_tx、uart_rx、control模块。
uart_rx模块: UART串口数据接收模块,将上位机发送的串行数据接收后转换成并行数据发送给control模块。
control模块: UART控制模块,将接收到的并行数据存储到FIFO中,当读FIFO条件满足时输出。
uart_tx模块: UART串口数据发送模块,将从control读出的并行数据转换成串行数据发送给上位机。
三、代码实现
1、uart顶层设计模块
module uart (
input clk ,
input rst_n ,
input uart_rxd ,
output uart_txd
);
wire [7:0] rx_dout ;
wire rx_dout_vld ;
wire [7:0] ctrl_dout ;
wire ctrl_dout_vld ;
wire ready ;
uart_rx u_uart_rx (
.clk (clk ),
.rst_n (rst_n ),
.uart_rx (uart_rxd ),
.rx_dout (rx_dout ),
.rx_dout_vld (rx_dout_vld )
);
control u_control (
.clk (clk ),
.rst_n (rst_n ),
.rx_din (rx_dout ),
.rx_din_vld (rx_dout_vld ),
.ready (ready ),
.ctrl_dout (ctrl_dout ),
.ctrl_dout_vld (ctrl_dout_vld )
);
uart_tx u_uart_tx (
.clk (clk ),
.rst_n (rst_n ),
.tx_din (ctrl_dout ),
.tx_din_vld (ctrl_dout_vld ),
.ready (ready ),
.uart_tx (uart_txd )
);
endmodule
2、uart_rx串口数据接收模块
`include "param.v"
module uart_rx (
input clk ,
input rst_n ,
input uart_rx ,
output [7:0] rx_dout ,
output rx_dout_vld
);
reg [19:0] cnt_baud ;
wire add_cnt_baud ;
wire end_cnt_baud ;
reg [3:0] cnt_bit ;
wire add_cnt_bit ;
wire end_cnt_bit ;
reg rx_flag ;
reg [1:0] uart_rx_r ;
wire rx_nedge ;
reg [9:0] dout_data ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 0 ;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 0 ;
end
else begin
cnt_baud <= cnt_baud + 1 ;
end
end
end
assign add_cnt_baud = rx_flag ;
assign end_cnt_baud = add_cnt_baud && (cnt_baud == `BAUD - 1) ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0 ;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0 ;
end
else begin
cnt_bit <= cnt_bit + 1 ;
end
end
end
assign add_cnt_bit = end_cnt_baud ;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == 9 || dout_data[0] == 1'b1) ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_rx_r <= 0 ;
end
else begin
uart_rx_r <= {uart_rx_r[0],uart_rx} ;
end
end
assign rx_nedge = ~uart_rx_r[0] & uart_rx_r[1] ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_flag <= 1'b0 ;
end
else if(rx_nedge)begin
rx_flag <= 1'b1 ;
end
else if(end_cnt_bit)begin
rx_flag <= 1'b0 ;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_data <= 0 ;
end
else if(rx_flag & cnt_baud == 0)begin
dout_data[cnt_bit] <= uart_rx_r[0] ;
end
end
assign rx_dout = dout_data[8:1] ;
assign rx_dout_vld = end_cnt_bit && dout_data[0] == 1'b0 ;
endmodule
3、control控制模块
`include "param.v"
module control (
input clk ,
input rst_n ,
input [7:0] rx_din ,
input rx_din_vld ,
input ready ,
output [7:0] ctrl_dout ,
output ctrl_dout_vld
);
wire rdreq ;
wire wrreq ;
wire [7:0] fifo_q ;
wire rdempty ;
wire [2:0] rdusedw ;
wire wrfull ;
wire [2:0] wrusedw ;
reg rd_flag ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_flag <= 1'b0 ;
end
else if(rdusedw > 4)begin
rd_flag <= 1'b1 ;
end
else if(rdempty)begin
rd_flag <= 1'b0 ;
end
end
fifo fifo_inst (
.aclr ( ~rst_n ),
.data ( rx_din ),
.rdclk ( clk ),
.rdreq ( rdreq ),
.wrclk ( clk ),
.wrreq ( wrreq ),
.q ( fifo_q ),
.rdempty ( rdempty ),
.rdusedw ( rdusedw ),
.wrfull ( wrfull ),
.wrusedw ( wrusedw )
);
assign wrreq = ~wrfull && rx_din_vld ;
assign rdreq = rd_flag && ready ;
assign ctrl_dout = fifo_q ;
assign ctrl_dout_vld = rdreq ;
endmodule
4、uart_tx串口数据发送模块
`include "param.v"
module uart_tx (
input clk ,
input rst_n ,
input [7:0] tx_din ,
input tx_din_vld ,
output ready ,
output uart_tx
);
reg [19:0] cnt_baud ;
wire add_cnt_baud ;
wire end_cnt_baud ;
reg [3:0] cnt_bit ;
wire add_cnt_bit ;
wire end_cnt_bit ;
reg tx_flag ;
reg [9:0] tx_data ;
reg dout ;
reg vld ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 0 ;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 0 ;
end
else begin
cnt_baud <= cnt_baud + 1 ;
end
end
end
assign add_cnt_baud = tx_flag ;
assign end_cnt_baud = add_cnt_baud && (cnt_baud == `BAUD - 1) ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0 ;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0 ;
end
else begin
cnt_bit <= cnt_bit + 1 ;
end
end
end
assign add_cnt_bit = end_cnt_baud ;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == 9) ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_flag <= 1'b0 ;
end
else if(tx_din_vld)begin
tx_flag <= 1'b1 ;
end
else if(end_cnt_bit)begin
tx_flag <= 1'b0 ;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
vld <= 1'b0 ;
end
else begin
vld <= tx_din_vld ;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 0 ;
end
else if(vld)begin
tx_data <= {1'b1,tx_din,1'b0} ;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 1'b1 ;
end
else if(tx_flag)begin
dout <= tx_data[cnt_bit] ;
end
end
assign uart_tx = dout ;
assign ready = ~tx_flag ;
endmodule
四、仿真
仿真代码如下:
`timescale 1ns/1ps
module uart_tb ();
reg tb_clk ;
reg tb_rst_n ;
reg [7:0] data ;
reg data_vld ;
wire ready ;
wire tx_data ;
wire uart_txd ;
wire [7:0] rx_dout ;
wire rx_dout_vld ;
uart_tx uart_tx_pc (
.clk (tb_clk ),
.rst_n (tb_rst_n ),
.tx_din (data ),
.tx_din_vld (data_vld ),
.ready (ready ),
.uart_tx (tx_data )
);
uart u_uart (
.clk (tb_clk ),
.rst_n (tb_rst_n ),
.uart_rxd (tx_data ),
.uart_txd (uart_txd )
);
parameter CYCLE = 20 ;
always #(CYCLE / 2) tb_clk = ~tb_clk ;
initial begin
tb_clk = 1'b1;
tb_rst_n = 1'b1;
data = 0;
data_vld = 1'b0;
# (CYCLE * 2);
tb_rst_n = 1'b0;
# (CYCLE * 2);
# 2;
tb_rst_n = 1'b1;
# (CYCLE * 10);
Send(8'hf9);
Send(8'h99);
Send(8'hAE);
Send(8'hBC);
Send(8'h55);
Send(8'hE1);
# (CYCLE * 100);
$stop;
end
task Send;
input [7:0] send_data ;
begin
data = send_data ;
data_vld = 1'b1 ;
# CYCLE ;
data_vld = 1'b0 ;
# (CYCLE*440*10) ;
end
endtask
endmodule
五、上板验证
这里通过串口调试助手来与FPGA进行串口通信测试,可以看见通过串口收发数据正确。
六、踩坑事项
在调试串口的时候发现设备管理器中串口无法识别,显示PL2303HXA自2012已停产,请联系供应商,如下图:
这时需要下载一个旧版本的驱动,下载链接如下:
链接:https://pan.baidu.com/s/1FTtfgc2k2fw-9Ck1_tRbRQ
提取码:7kf2
下载完成后解压安装,点击更新驱动程序→浏览电脑选择旧版本双击安装即可:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)