在上几节内容中介绍了异步串行口的协议,波特率等内容。本节结合开发板PRA006/010介绍如何将UART –TX的各个部分组合在一起在FPGA实现,并通过Tera term, SSCOM等串口测试工具与计算机通信。
- 继续使用上节创建的工程,并建立新文件uart.v, 并设为顶层文件
- 利用IP Catalog向导生成输入50Mhz, 输出72MHz的PLL,并在uart.v文件里例化
- 将上节设计的波特率生成器在uart.v里例化,并将在UART协议一节中设计的tx.v在uart.v文件里例化。例化后的源文件如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
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
程序如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
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 |
仿真工程:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
`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]