在上几节内容中介绍了异步串行口的协议,波特率等内容。本节结合开发板PRA006/010介绍如何将UART –TX的各个部分组合在一起在FPGA实现,并通过Tera term, SSCOM等串口测试工具与计算机通信。
- 继续使用上节创建的工程,并建立新文件uart.v, 并设为顶层文件
- 利用IP Catalog向导生成输入50Mhz, 输出72MHz的PLL,并在uart.v文件里例化
- 将上节设计的波特率生成器在uart.v里例化,并将在UART协议一节中设计的tx.v在uart.v文件里例化。例化后的源文件如下:
module uart
#(
parameter PARITY = "ODD"
)
(
input inclk,
input tx_req,
input [7:0] tx_data,
output tx_ack,
output tx,
input rx, //rx 端在本设计中没有实现
input rx_ack,
output rx_rdy,
output [7:0] rx_data,
output sys_clk, //将系统时钟输出,便于测试程序使用
output rst
);
wire tx_bd_en;
wire rx_bd_en;
wire locked;
assign rst=!locked;
uart_pll uart_pll_inst
(
.areset (1'b0),
.inclk0 (inclk), // inclk ----50M,
.c0 (sys_clk), // sys_clk----72M
.locked (locked)
);
baud_rate_en
#(
.BAUD_RATE (115200), //波特率:115200
.FREQUENCY (72000000) //时钟参数:72M
)
bau_rate_en_inst
(
.clk (sys_clk),
.rst (rst),
.tx_bd_en (tx_bd_en),
.rx_bd_en (rx_bd_en)
);
tx
#(
.PARITY (PARITY),
.STOP_BIT (1)
)
txd_inst
(
.rst (rst),
.clk (sys_clk),
.tx_bd_en (tx_bd_en),
.tx_data (tx_data),
.tx_req (tx_req),
.tx_ack (tx_ack),
.tx (tx)
);
assign rx_rdy = 0;
assign rx_data = 0;
endmodule
并将波特率发生器程序bd_rate_en.v与串口发送程序tx.v重新列出,如下:
- bd_rate_en.v
module baud_rate_en
#(
parameter BAUD_RATE = 115200,
parameter FREQUENCY = 100000000
)
(
input clk,
input rst,
output reg tx_bd_en,
output reg rx_bd_en
);
localparam TX_DIV_COE = FREQUENCY / (BAUD_RATE) - 1;
localparam RX_DIV_COE = FREQUENCY / (BAUD_RATE*8) - 1;
reg [18:0] tx_clk_div;
reg [18:0] rx_clk_div;
always@(posedge clk or posedge rst)
if(rst)
begin
tx_clk_div <= 0;
rx_clk_div <= 0;
tx_bd_en <= 0;
rx_bd_en <= 0;
end
else
begin
tx_bd_en <= 1'b0;
if(tx_clk_div == TX_DIV_COE)
begin
tx_clk_div <= 0;
tx_bd_en <= 1'b1;
end
else
tx_clk_div <= tx_clk_div + 1;
rx_bd_en <= 0;
if(rx_clk_div == RX_DIV_COE)
begin
rx_clk_div <= 0;
rx_bd_en <= 1'b1;
end
else
rx_clk_div <= rx_clk_div + 1;
end
endmodule
- tx.v
module tx
#(
parameter PARITY = "ODD", //"ODD"--odd parity, "EVEN"--even parity, others--no parity
parameter STOP_BIT = 1
)
(
input rst,
input clk,
input tx_bd_en,
input [7:0] tx_data,
input tx_req,
output reg tx_ack,
output reg tx
);
localparam [3:0]
IDLE = 0,
START = 1,
FIRST_BIT = 2,
SEC_BIT = 3,
THIRD_BIT = 4,
FOUTH_BIT = 5,
FIF_BIT = 6,
SIX_BIT = 7,
SEVN_BIT = 8,
EIGTH_BIT = 9,
PAR_BIT = 10,
STOP1_BIT = 11,
STOP2_BIT = 12;
reg [7:0] tx_data_r;
wire odd = ^tx_data_r;
reg [3:0] tx_st;
always@(posedge clk or posedge rst)
if(rst)
begin
tx <= 1'b1;
tx_st <= 0;
tx_data_r <= 0;
tx_ack <= 1'b0;
end
else
begin
if(tx_bd_en)
begin
case(tx_st)
IDLE:
begin
tx_ack <= 1'b0;
tx <= 1'b1;
if(tx_req)
begin
tx_data_r <= tx_data;
tx_ack <= 1'b1;
tx_st <= START;
end
end
START:
begin
tx <= 1'b0;
tx_ack <= 1'b0;
tx_st <= FIRST_BIT;
end
FIRST_BIT:
begin
tx <= tx_data_r[0];
tx_st <= SEC_BIT;
end
SEC_BIT:
begin
tx <= tx_data_r[1];
tx_st <= THIRD_BIT;
end
THIRD_BIT:
begin
tx <= tx_data_r[2];
tx_st <= FOUTH_BIT;
end
FOUTH_BIT:
begin
tx <= tx_data_r[3];
tx_st <= FIF_BIT;
end
FIF_BIT :
begin
tx <= tx_data_r[4];
tx_st <= SIX_BIT;
end
SIX_BIT:
begin
tx <= tx_data_r[5];
tx_st <= SEVN_BIT;
end
SEVN_BIT:
begin
tx <= tx_data_r[6];
tx_st <= EIGTH_BIT;
end
EIGTH_BIT:
begin
tx <= tx_data_r[7];
if( (PARITY=="ODD") || (PARITY=="EVEN") )
tx_st <= PAR_BIT;
else
tx_st <= STOP1_BIT;
end
PAR_BIT :
begin
tx_st <= STOP1_BIT;
if(PARITY == "ODD")
tx <= odd;
else
tx <= ~odd;
end
STOP1_BIT:
begin
tx <= 1'b1;
if( STOP_BIT == 1)
begin
if(tx_req)
begin
tx_data_r <= tx_data;
tx_ack <= 1'b1;
tx_st <= START;
end
else
tx_st <= IDLE;
end
else
tx_st <= STOP2_BIT;
end
STOP2_BIT :
begin
tx <= 1'b1;
if(tx_req)
begin
tx_data_r <= tx_data;
tx_ack <= 1'b1;
tx_st <= START;
end
else
tx_st <= IDLE;
end
default: tx_st <= IDLE;
endcase
end
end
endmodule
- 硬件设置
在FII-PRA006/010的开发板上采用了FT2232USB转串口芯片,如图1,

图1-1

图1-2
图1中J2是mini USB接口,可以通过USB传输电缆连接到PC或笔记本上,FT2232的右侧提供了两套TTL接口,分别接到FPGA的JTAG接口,和UART口(JTAG_TXD_O,JTAG_RXD_I)上。在本节内容只是讲解JTAG_TXD_O,JTAG_RXD_I如何使用。这两个管脚在FPGA上的映射如表1所示:
表1
| UART名称 | RXD | TXD |
| 原理图名称 | JTAG_TXD_O | JTAG_RXD_I |
| FPGA 管脚 | 144 | 143 |
| 说明 | FPGA接收端 | FPGA发送端 |
- 测试程序
测试目的是提供数据,观察UART的TX是否按照协议进行发送数据。为了观测方便,这里提供的测试数据为秒脉冲计数器,将计数值作为UART的输入数据,并完善tx_req,tx_ack等握手协议,见图2。

图2
程序如下:
module test_uart
(
input inclk,
output tx
);
parameter PARITY = "none";
wire rst;
wire sys_clk;
reg tx_req;
reg [7:0] tx_data;
wire tx_ack;
reg [27:0] div_sf; //Second division from 72MHz
reg s_p ;//second pulse
reg [1:0] tx_st; //
always@(posedge sys_clk or posedge rst)
if(rst)
begin
div_sf <= 0;
s_p <= 0;
end
else
begin
s_p <= 0;
if(div_sf == 71999999)
begin
s_p <= 1'b1;
div_sf <= 0;
end
else
div_sf <= div_sf + 1;
end
always@(posedge sys_clk or posedge rst)
if(rst)
begin
tx_st <= 0;
tx_req <= 0;
tx_data <= 0;
end
else
begin
case(tx_st)
0:
begin
tx_data <= 0;
tx_req <= 0;
if(s_p)
tx_st <= 1;
end
1:
begin
tx_req <= 1'b1;
tx_st <= 2;
end
2:
begin
if(tx_ack)
begin
tx_req <= 1'b0;
tx_st <= 3;
end
end
3:
begin
if(s_p)
begin
tx_data <= tx_data + 1;
tx_st <= 1;
end
end
endcase
end
uart
#(
.PARITY (PARITY)
)
uart_inst
(
.inclk (inclk),
.tx_req (tx_req),
.tx_data (tx_data),
.tx_ack (tx_ack),
.tx (tx),
.rx (),
.rx_ack (),
.rx_rdy (),
.rx_data (),
.sys_clk (sys_clk),
.rst (rst)
);
endmodule
仿真工程:
`timescale 1 ns / 1 ps
module tb
(
);
parameter PERIOD = 20 ; //50Mhz
reg inclk;
wire tx;
initial
begin
inclk = 0;
#(PERIOD/2);
forever
#(PERIOD/2) inclk = ~inclk;
end
initial
begin
// #60000;
// $stop;
end
test_uart test_uart_inst
(
.inclk (inclk),
.tx (tx)
);
endmodule
- 测试步骤
- 单击菜单Projects–> Revisions… 在弹出的对话框中设置工程的更新版本,将工程的顶层实体设置为test_UART,如图3,

图3
- 重新编译,管脚锁定,编译下载。
- 打开SSCOM 串口调试软件(PC或笔记本上使用),工具设置如图4

图4
注意事项:
-
- 由于FT2232在PC上映射的串口端口可能会变化,应注意选择正确的端口号,如图3显示2个端口(COM22,COM23),一般第一个已被JTAG使用,因此选择COM23。如果错误选择了COM22并打开,可能会影响JTAG下载,Signal Tap Logic Analyzer的调试等。
- 图4是英文版的界面,中文版可以按照同样的方式设置。
练习:
(1) 编写testbench 程序,仿真tx的协议发送过程。观察波形。
(2)用Signal Tap Logic Analyzer调试UART 的tx过程,观察波形。
Quartus 参考代码:
[content_control]
[/content_control]
老师,这个uart 模块里面的tx_req 好像应该改为tx_rdy
谢谢你的回复,已经改正!