基于FIFO使用UART实现FPGA板与PC通信

news/2024/10/12 16:01:53

基于FIFO使用UART实现FPGA板与PC通信

1. UART 简介

UART(通用异步收发传输器)是一种常用的串行通信协议,广泛用于FPGA与外部设备(如PC、传感器等)之间的通信。UART 通信的核心是将并行数据转换为串行数据传输,然后在接收端再将串行数据恢复为并行数据。

UART协议特点:

  • 异步通信:无需时钟信号
  • 双向数据传输
  • 起始位、数据位、停止位、校验位可配置

UART 一般传输速率较慢,但对于非实时、高速数据通信应用中,具有成本低、实现简单的优势。


2. UART 软件设计

在本设计中,我们使用 FIFO(先进先出队列)缓存数据,通过 UART 模块实现 FPGA 和 PC 之间的数据收发。设计主要分为三部分:串口接收模块、串口发送模块和 FIFO 控制模块,串口模块我是直接用的正点原子的代码,源码中也有注明,fifo_control代码是基于正点原子再修改的。各个模块的功能如下:

  1. uart_rx:串口接收模块,负责从 PC 接收串行数据并转换为并行数据,传入 FIFO。

  2. uart_tx:串口发送模块,负责从 FIFO 中读取并行数据,转换为串行数据并发送到 PC。

  3. fifo_control:控制 FIFO 的数据流,协调 uart_rx 和 uart_tx 模块之间的数据传输。

  4. uart_fifo(顶层文件):连接所有子模块,控制整个系统的工作流程。

    image-20241012155025921


3. 模块介绍

3.1 uart_rx 模块

功能介绍

uart_rx 模块用于接收来自 PC 的串行 UART 数据,并将其转换为 8 位并行数据输出。模块通过波特率计数来确定数据接收的时序,并生成接收完成标志信号 uart_rx_done,指示接收过程结束。

接口说明

module uart_rx(input               clk         ,  // 系统时钟input               rst_n       ,  // 系统复位,低电平有效input               uart_rxd    ,  // UART 接收端口output  reg         uart_rx_done,  // UART 接收完成信号output  reg  [7:0]  uart_rx_data   // UART 接收到的数据
);
  • clk:系统时钟信号。
  • rst_n:系统复位信号,低电平有效。
  • uart_rxd:从 PC 端接收的串行 UART 数据。
  • uart_rx_done:接收完成信号,高电平时表示接收到完整数据。
  • uart_rx_data:接收到的 8 位并行数据。

参数定义

parameter CLK_FREQ = 50000000;   // 系统时钟频率
parameter UART_BPS = 115200;     // UART 波特率
localparam BAUD_CNT_MAX = CLK_FREQ / UART_BPS;  // 每一位数据的计数周期
  • CLK_FREQ:FPGA 系统时钟频率,这里设定为 50MHz。
  • UART_BPS:UART 波特率,设定为 115200。
  • BAUD_CNT_MAX:通过系统时钟频率和波特率计算出的每一位数据所需的时钟周期。

主要信号及逻辑说明

异步信号同步处理

模块首先对 uart_rxd 进行多级同步处理,消除亚稳态:

always @(posedge clk or negedge rst_n) beginif (!rst_n) beginuart_rxd_d0 <= 1'b0;uart_rxd_d1 <= 1'b0;uart_rxd_d2 <= 1'b0;endelse beginuart_rxd_d0 <= uart_rxd;uart_rxd_d1 <= uart_rxd_d0;uart_rxd_d2 <= uart_rxd_d1;end
end

起始位检测

通过检测 uart_rxd 的下降沿来判断起始位:

assign start_en = uart_rxd_d2 & (~uart_rxd_d1) & (~rx_flag);

数据接收逻辑

当检测到起始位后,进入接收状态,模块通过 baud_cntrx_cnt 控制数据接收的时序。数据接收采用位移寄存的方式,逐位存储接收到的数据:

always @(posedge clk or negedge rst_n) beginif (rx_flag && baud_cnt == BAUD_CNT_MAX/2 - 1'b1) begincase(rx_cnt)4'd1: rx_data_t[0] <= uart_rxd_d2;4'd2: rx_data_t[1] <= uart_rxd_d2;//...4'd8: rx_data_t[7] <= uart_rxd_d2;endcaseend
end

接收完成信号

rx_cnt 达到 9 位(1 位起始位,8 位数据位)时,模块拉高 uart_rx_done 信号,表明数据接收完成:

always @(posedge clk or negedge rst_n) beginif (rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX/2 - 1'b1) beginuart_rx_done <= 1'b1;uart_rx_data <= rx_data_t;end
end

模块总结

uart_rx 模块采用标准的 UART 接收流程,通过波特率计数器控制时序,并同步接收数据。该模块的设计充分考虑了异步信号的同步处理,确保了数据接收的稳定性。


3.2 uart_tx 模块

功能介绍

uart_tx 模块用于将并行数据转换为串行 UART 数据并发送至 PC 端。通过波特率计数器控制数据的发送速率,模块根据输入的 8 位并行数据进行数据位、起始位和停止位的顺序发送,同时输出发送忙状态信号 uart_tx_busy 来指示数据发送状态。

接口说明

module uart_tx(input               clk         , // 系统时钟input               rst_n       , // 系统复位,低电平有效input               uart_tx_en  , // UART 发送使能input     [7:0]     uart_tx_data, // 要发送的并行数据output  reg         uart_txd    , // UART 发送端口output  reg         uart_tx_busy  // 发送忙状态信号
);
  • clk:系统时钟信号。
  • rst_n:系统复位信号,低电平有效。
  • uart_tx_en:发送使能信号,高电平时开始发送数据。
  • uart_tx_data:待发送的 8 位并行数据。
  • uart_txd:UART 串行发送端口。
  • uart_tx_busy:发送忙状态信号,高电平表示正在发送数据。

参数定义

parameter CLK_FREQ = 50000000;   // 系统时钟频率
parameter UART_BPS = 115200;     // UART 波特率
localparam BAUD_CNT_MAX = CLK_FREQ / UART_BPS;  // 波特率周期计数
  • CLK_FREQ:FPGA 系统时钟频率,设定为 50MHz。
  • UART_BPS:UART 波特率,设定为 115200。
  • BAUD_CNT_MAX:根据系统时钟频率和波特率计算得到的每一位数据的时钟周期。

主要信号及逻辑说明

发送数据寄存器和忙信号

uart_tx_en 使能信号有效时,输入的并行数据 uart_tx_data 会被存入发送寄存器 tx_data_t,同时 uart_tx_busy 信号拉高,表示开始发送数据:

always @(posedge clk or negedge rst_n) beginif (!rst_n) begintx_data_t <= 8'b0;uart_tx_busy <= 1'b0;endelse if (uart_tx_en) begintx_data_t <= uart_tx_data;uart_tx_busy <= 1'b1;endelse if (tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX - 1) beginuart_tx_busy <= 1'b0;end
end

波特率计数器

波特率计数器 baud_cnt 控制数据发送的速率。当处于发送状态时,波特率计数器循环计数,每到一个波特率周期时发送下一位数据:

always @(posedge clk or negedge rst_n) beginif (!rst_n) baud_cnt <= 16'd0;else if (uart_tx_busy) beginif (baud_cnt < BAUD_CNT_MAX - 1)baud_cnt <= baud_cnt + 16'b1;else baud_cnt <= 16'd0;end
end

数据发送逻辑

tx_cnt 计数器用于跟踪数据发送的进度,每发送一位数据,计数器加 1。当 tx_cnt 达到 9 时,表示数据发送完成,包括 1 位起始位、8 位数据位和 1 位停止位:

always @(posedge clk or negedge rst_n) beginif (!rst_n) tx_cnt <= 4'd0;else if (uart_tx_busy && baud_cnt == BAUD_CNT_MAX - 1) tx_cnt <= tx_cnt + 1'b1;
end

根据 tx_cnt 的值,控制发送端口 uart_txd 的输出,发送 1 位起始位、8 位数据位和 1 位停止位:

always @(posedge clk or negedge rst_n) beginif (!rst_n)uart_txd <= 1'b1;else if (uart_tx_busy) begincase (tx_cnt)4'd0 : uart_txd <= 1'b0;           // 起始位4'd1 : uart_txd <= tx_data_t[0];   // 数据位最低位4'd2 : uart_txd <= tx_data_t[1];//...4'd8 : uart_txd <= tx_data_t[7];   // 数据位最高位4'd9 : uart_txd <= 1'b1;           // 停止位endcaseend
end

模块总结

uart_tx 模块根据设定的波特率,将并行数据逐位转换为串行数据进行发送,并提供忙信号 uart_tx_busy 来表明发送过程是否完成。该模块实现了从起始位到停止位的完整 UART 发送过程。


3.3 fifo_control 模块

功能介绍

fifo_control 模块用于在 UART 接收和发送之间引入 FIFO 缓存,实现数据的异步缓存和流控处理。该模块接收 UART 数据,并将其存入 FIFO,当 FIFO 中有足够的数据或达到一定条件时,通过 uart_tx_en 发送使能信号控制数据发送。模块还设计了一个超时机制,当超过指定时间没有收到新的数据时,自动触发数据发送。

接口说明

module fifo_control(// 系统信号input              	clk			,	// 时钟信号input              	rst_n		,	// 复位信号// UART 相关信号input          	  	uart_rx_done,	// UART 接收完成信号input      [7:0] 	uart_rx_data,	// UART 接收到的数据input            	uart_tx_busy,	// UART 忙信号output       		uart_tx_en 	,	// UART 发送使能信号output     [7:0] 	uart_tx_data	// 要发送给 UART 的数据
);
  • clk:系统时钟信号。
  • rst_n:复位信号,低电平有效。
  • uart_rx_done:UART 接收完成信号,高电平时表示接收到完整数据。
  • uart_rx_data:UART 接收到的 8 位数据。
  • uart_tx_busy:UART 发送忙信号,高电平表示 UART 正在发送数据。
  • uart_tx_en:UART 发送使能信号,高电平时启动 UART 发送。
  • uart_tx_data:需要通过 UART 发送的数据。

参数和信号说明

参数定义

localparam TIMEOUT_THRESHOLD = 16'd50000;  	// 设置超时阈值(根据时钟频率调整)
  • TIMEOUT_THRESHOLD:超时阈值,用于控制数据的发送时机。如果超过此阈值没有接收到新的数据,模块将主动触发数据发送。

信号定义

  • wr_req:写请求信号,当 UART 接收完成且 FIFO 未满时,拉高该信号,将数据写入 FIFO。
  • rd_req:读请求信号,当 FIFO 有数据且 UART 处于空闲状态时,拉高该信号,将数据从 FIFO 读出。
  • uart_tx_en:UART 发送使能信号,与 rd_req 相同,当拉高时启动 UART 发送。
  • wr_data:写入 FIFO 的数据,来自 UART 接收到的并行数据 uart_rx_data
  • uart_tx_data:从 FIFO 读出的数据,发送给 UART。

主要逻辑说明

超时计数器逻辑

模块引入了超时机制,当 UART 接收到数据时,复位超时计数器;如果在指定时间内没有接收到新的数据,超时计数器达到阈值后,将触发发送信号。

always @(posedge clk or negedge rst_n) beginif (!rst_n)timeout_cnt <= 16'd0;else if (uart_rx_done)timeout_cnt <= 16'd0;  // 接收到数据时,复位超时计数器else if (timeout_cnt < TIMEOUT_THRESHOLD)timeout_cnt <= timeout_cnt + 1'b1;  // 如果没接收到数据,开始计时
end

读启动信号逻辑

当超时计数器达到阈值时,标志信号 flag 被拉高,触发 FIFO 的读请求信号 rd_req。当 rd_req 被拉高且 UART 空闲时,UART 发送开始。

always @(posedge clk or negedge rst_n) beginif (!rst_n)flag <= 1'b0;else if (timeout_cnt == TIMEOUT_THRESHOLD - 1'b1)flag <= 1'b1;  // 超时后,拉高读启动信号
end

FIFO 缓存管理

模块中例化了一个 FIFO IP核,采用的异步等宽数据FIFO,但在实际连接中写和读用的是同一个时钟,如果想要使用不同时钟,需要再调用PLL锁相环IP核,通过 wr_reqrd_req 信号控制数据的写入和读取。写入的数据来自 UART 接收到的并行数据,读取的数据通过 UART 发送出去。

fifo	fifo_inst (.aclr 		( ~rst_n	),.data 		( wr_data 	),.rdclk 		( clk 		),.rdreq 		( rd_req 	),.wrclk 		( clk 		),.wrreq 		( wr_req 	),.q 			( rd_data	),.rdempty 	( rd_empty 	),.rdfull 	( rd_full 	),.rdusedw 	( rd_usedw 	),.wrempty 	( wr_empty 	),.wrfull 	( wr_full 	),.wrusedw 	( wr_usedw 	)
);

写请求信号逻辑

当 UART 接收到数据且 FIFO 未满时,写请求信号 wr_req 被拉高,数据被写入 FIFO:

assign wr_req = uart_rx_done && (~wr_full);

读请求信号逻辑

当 FIFO 中有数据且 UART 空闲时,读请求信号 rd_req 被拉高,数据从 FIFO 中读出并发送:

assign rd_req = flag && (~uart_tx_busy) && (~rd_empty);
assign uart_tx_en = rd_req;

模块总结

fifo_control 模块通过 FIFO 实现数据缓冲,避免了 UART 接收和发送速度不匹配的问题。同时,通过超时计数器实现了自动触发数据发送的机制,确保在长时间没有新数据到来时也能及时发送缓存中的数据。


4. ModelSim 仿真

在仿真阶段,我们使用 ModelSim 对整个系统进行仿真,验证设计的正确性。

注意仿真涉及到IP核时需要添加仿真库文件altera_mf.v,如下图中所示:

image-20241012124230478

仿真步骤:

  1. 使用 tb 文件编写测试用例,验证 UART 模块的发送和接收功能。
  2. 查看波形,验证 FIFO 的数据流和 UART 串行通信的正确性。
  3. 验证数据传输的完整性,检查接收到的数据是否与发送的数据一致。

仿真结果如下图所示:

image-20241012115624344

仿真测试模块功能解读:

  1. 时钟与复位设置:

    • 通过sys_clk模拟一个50MHz的系统时钟,每个周期为20ns。时钟信号以10ns的间隔取反,保证时钟的稳定。
    • sys_rst_n作为复位信号,在仿真开始时为低电平,经过200ns后变为高电平,表示系统解除复位。
  2. UART信号发送过程:

    • uart_rxd 模拟UART接收端的信号输入,最开始为1(空闲状态),并通过逐个时钟周期发送数据位来模拟一个UART字节的传输过程。每个数据传输周期为8680ns,对应于9600波特率下的1个bit时间。
    • 发送的字节数据分别为A, B, C等对应的二进制ASCII码,通过每8位数据加1个起始位和1个停止位进行传输。
  3. 测试过程:

    • uart_tb模块每发送完一个字节,保持一段空闲状态,随后开始下一个字节的发送过程。这样,可以测试UART接收模块是否能正确接收和解析数据位。
    • 最终,发送数据经过FIFO后,通过uart_txd接口再次发送出去,验证UART发送功能。

模块实例化:

  • 顶层模块uart_fifo在仿真中被实例化,连接系统时钟、复位、以及UART的收发信号。

5. Quartus II 综合

在FPGA设计中,综合是将Verilog代码转化为FPGA硬件资源配置的过程,下面是基于Quartus II的综合步骤:

5.1 工程创建与设置

  1. 打开Quartus II,选择File -> New Project Wizard,创建一个新的工程。

  2. 在"New Project Wizard"中,指定项目路径、名称和顶层模块文件名。

  3. 选择合适的FPGA芯片型号,例如Cyclone IVCyclone V,这里我用的是正点原子新起点V2开发板,因此我选择Cyclone IV E

    image-20241012155413079

    image-20241012121041609
  4. 点击Finish完成项目创建。

5.2 生成FIFO IP核

为了实现UART和FPGA之间的FIFO缓存,可以使用Quartus II中的MegaWizard Plug-In Manager生成FIFO IP核,具体步骤如下:

1. 打开MegaWizard Plug-In Manager

  1. 在Quartus II的菜单中,选择Tools -> MegaWizard Plug-In Manager,打开IP核生成器。
  2. 选择Create a new custom megafunction variation,然后点击Next

2. 选择FIFO类型

  1. 在弹出的窗口中,选择Memory Compiler -> FIFO,然后点击Next
  2. 根据设计需求选择合适的FIFO模式,通常可以选择FIFO (Single Clock)FIFO (Dual Clock),取决于数据写入和读取时钟是否一致。

3. 配置FIFO参数

  1. FIFO宽度:设置数据位宽,比如对于UART的8位数据,FIFO宽度可以设置为8。

  2. FIFO深度:根据设计需求选择FIFO的深度,比如设置为16、32或64,这将决定FIFO可以存储多少数据。

  3. 其他参数可以根据需求配置。

    image-20241012123531301 image-20241012123627103 image-20241012123831535 image-20241012123935279 image-20241012124043250

4. 生成FIFO

  1. 点击Next,按照向导完成剩余的配置,通常可以选择默认设置。
  2. 最后点击Finish,Quartus会生成相应的FIFO IP核文件,生成的文件通常包括一个.v.vhd文件以及配套的.qip文件。

5. 添加FIFO到工程中

在项目中,右键Files,选择Add/Remove Files in Project,将生成的FIFO文件(如fifo.v)添加到工程中。

5.3 添加Verilog代码

  1. 在项目目录下,右键点击Files,选择Add/Remove Files in Project,将你的Verilog代码(如uart_fifo.v)和其他模块代码(如uart_rx.vuart_tx.vfifo_control.v)添加到工程中。

    image-20241012124341532
  2. 确保所有代码文件都已正确添加并编译通过。

5.4 Pin Assignment (引脚分配)

  1. 打开Assignments -> Pin Planner,根据FPGA开发板的实际情况,分配UART的发送、接收引脚(如uart_txduart_rxd)。

  2. 通过查阅开发板手册确定引脚的编号,并在Pin Planner中对应分配。

    image-20241012124606266

5.5 综合与编译

  1. 在Quartus II中点击Processing -> Start Compilation,开始综合和编译过程。

  2. 编译完成后,可以在编译报告中查看资源使用情况,例如LE(逻辑单元)利用率、时钟频率、延迟等。

    image-20241012124642710

6. 上板验证

最后一步是将比特流文件烧录到 FPGA 开发板上,进行实际硬件验证。

6.1 硬件连接

  1. 硬件准备:确保有一个支持UART通信的FPGA开发板和用于数据传输的PC。使用FPGA板自带的UART接口(如USB-UART模块)与PC连接。
  2. 连接说明
    • 使用USB线将FPGA开发板与电脑连接,确保板载UART通信模块能够正确识别。
    • 如果FPGA板上有多个串口或通信模块,请选择正确的UART接口。

6.2 Quartus II 下载配置

  1. Bitstream 文件生成:首先在Quartus II中完成综合(Compile)步骤,确保工程没有任何错误。生成相应的.sof文件。

  2. 配置下载:通过Quartus II中的“Programmer”工具,将生成的.sof文件下载到FPGA开发板上。

    image-20241012122806701
  3. 确保复位:下载完成后,按下FPGA开发板上的复位按钮,使板卡进入复位状态。

6.3 终端软件调试

  1. 终端工具:在PC上运行串口调试工具,这里我使用的是SSCOM5.13.1版本,设置串口的波特率、数据位、停止位和校验位,确保与FPGA端的UART配置相同。

    • 波特率:115200

    • 数据位:8

    • 停止位:1

    • 校验:无

    • 数据流:无

      image-20241012122311254
  2. 连接串口:在串口工具中选择正确的COM口,并点击“打开串口”连接,确保能够与FPGA进行通信。

    image-20241012122416343

6.4 数据通信验证

  1. 数据发送测试:通过PC的终端工具发送预设的测试数据(如前面仿真中所使用的8'h55或者直接发送字符串),观察FPGA是否能够接收数据,并在FPGA板上进行处理。

    • FPGA应该能够接收数据,并通过FIFO缓存数据。
  2. 数据回传测试:FPGA收到数据后,FIFO应该将数据回传给PC。终端工具中应显示FPGA返回的数据,并与发送的原始数据一致。

    • 测试发送例如字符LilMonsterOvO,观察返回数据是否也是LilMonsterOvO

      image-20241012122949271

通过以上步骤,我们成功实现了基于 FIFO 使用 UART 进行 FPGA 板与 PC 之间的通信。该设计可以应用于多种串行通信场景,为数据收发提供稳定可靠的解决方案。

GitHub源码:LilMonsterOvO (github.com)

参考文章:

[1] 【FPGA】UART串口通信---基于FIFO_uart fifo-CSDN博客

[2] 使用UART实现FPGA板与PC通信 - kentle - 博客园 (cnblogs.com)

[3] 第22.1讲 FIFO IP核简介_哔哩哔哩_bilibili


这篇博客用于记录自己的学习生活,方便自己回顾学过的知识。同时,也希望能帮助大家理解 UART 和 FIFO 在 FPGA 中的应用。如有问题或建议,欢迎留言讨论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/70701.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

软件构造,生成算式采用CSV、XML、JSON三种形式进行存储并读取。

编写代码完成将生成的算式及习题长期保存下来,采用CSV、XML、JSON三种形式进行存储并读取。提交相关代码及运行截图。import random import csv import json import xml.etree.ElementTree as ET from xml.dom import minidom# 生成随机算式数据 def generate_exercises(count…

一文详述:AI 网关与 API 网关到底有什么区别?

近年来AI 发展火热,大模型已经成为推动各行各业业务创新和增长的关键力量。随之而来问题是“企业该如何安全管理和部署AI应用的挑战?”AI基础架构的设计不仅要支持现有的业务需求,还要能够适应未来技术的快速发展。在这样的背景下,AI网关的概念应运而生,AI 网关在AI应用的…

Armitage:MSF图形界面神器

原创 自然嗨 嗨嗨安全免责声明 请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与文章作者无关。Armitage Armitage是一款Java写的Metasploit图形界面化的攻击软件,可以用它结合 Metasploit中已知的exploit来针对主机存在的漏洞自动化攻击。通过命令行的方式…

不一样的事务

事务处理几乎在每一个信息系统中都会涉及,它存在的意义是为了保证系统中所有的数据都是符合期望的,且相互关联的数据之间不会产生矛盾,即数据状态的一致性。按照数据库的经典理论,要达成这个目标,需要三方面共同努力来保障。原子性(Atomic):在同一项业务处理过程中,事务…

java获取当前时间(年月日)

转自:https://blog.csdn.net/lynn_Kun/article/details/76997856在java程序中常常需要获取的时间和设置时间的格式 1、获取当前的时间Date date=new Date();//此时date为当前的时间 2、设置时间的格式Date date=new Date();//此时date为当前的时间System.out.println(date);Si…

沉浸式娱乐新纪元,3DCAT推出5G+实时云渲染VR大空间解决方案

随着5G、人工智能和云计算等技术的不断成熟,VR大空间体验的发展前景愈发广阔.3DCAT实时渲染云作为这一领域的赋能者,将持续创新,为用户提供更高效、更灵活的5G+实时云渲染VR大空间解决方案.近年来,虚拟现实(VR)技术在娱乐、教育、医疗等多个领域展现出巨大的潜力,尤其是VR大…

一种基于alpine、支持ARM架构64位的镜像构建方法及其构建系统

一种基于alpine、支持ARM架构64位的镜像构建方法及其构建系统,包括以下步骤: 步骤1:准备arm64位基础镜像包本文分享自天翼云开发者社区《一种基于alpine、支持ARM架构64位的镜像构建方法及其构建系统》,作者:郑****团 一种基于alpine、支持ARM架构64位的镜像构建方法及其构…