FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

FPGA项目实战:基于UART的通信模块设计与验证

二牛学FPGA二牛学FPGA
技术分享
5小时前
0
0
1

Quick Start

  • 准备环境:安装 Vivado 2020.1 及以上版本,并确认已添加目标器件(如 XC7A35T-1CSG324C)。
  • 创建工程:新建一个 RTL 工程,选择目标器件,添加顶层文件 uart_top.v
  • 编写代码:将本文提供的 UART 发送模块(uart_tx)和接收模块(uart_rx)代码复制到工程中。
  • 添加约束:创建 .xdc 文件,绑定系统时钟(如 50MHz)和 UART 引脚(TX、RX)。
  • 综合与实现:运行 Synthesis 和 Implementation,确保无错误。
  • 生成比特流:点击 Generate Bitstream,下载到 FPGA 开发板。
  • 连接串口:用 USB-UART 线连接开发板 TX 到 PC 的 RX,打开串口助手(波特率 115200,8N1)。
  • 测试发送:在顶层模块中设置一个按键,按下时发送固定数据(如 0x55),串口助手应收到 0x55。
  • 测试接收:用串口助手发送 0xAA,开发板上的 LED 应显示接收到的数据。
  • 验收点:发送/接收数据一致,无误码,Fmax 达到 200MHz 以上。

前置条件与环境

项目/推荐值说明替代方案
器件/板卡Xilinx Artix-7 XC7A35T-1CSG324C(Nexys 4 DDR 或类似板卡)其他 7 系列 FPGA,如 Spartan-7 或 Kintex-7
EDA 版本Vivado 2020.1 或更高版本ISE 14.7(仅支持 7 系列及更早器件)
仿真器Vivado Simulator(XSIM)ModelSim/QuestaSim 或 Verilator(仅仿真)
时钟/复位系统时钟 50MHz,外部复位按键(低有效)PLL 分频或倍频;复位可用内部上电复位
接口依赖USB-UART 转换器(如 CP2102、FT232)直接连接 RS-232 电平转换(MAX3232)
约束文件XDC 约束:时钟周期 20ns,TX/RX 引脚位置手动编写或使用 Vivado 引脚规划器

目标与验收标准

  • 功能点:实现 UART 异步串行通信,支持 8 位数据、1 位起始位、1 位停止位、无校验(8N1)。
  • 性能指标:波特率 115200 bps,最大时钟频率(Fmax)≥ 200 MHz(在 Artix-7 上)。
  • 资源占用:发送模块 ≤ 50 LUT + 30 FF;接收模块 ≤ 80 LUT + 50 FF。
  • 验收方式
  • 仿真波形:发送时 TX 线按位翻转,接收时 RX 线采样正确。
  • 上板测试:串口助手回环测试(发送 0x00-0xFF 所有值,回显无误)。
  • 时序报告:Setup Slack > 0,Hold Slack > 0,无违例。

实施步骤

工程结构

  • 顶层模块 uart_top.v:例化发送和接收模块,连接按键和 LED。
  • 发送模块 uart_tx.v:负责将并行数据转换为串行输出。
  • 接收模块 uart_rx.v:负责采样串行输入并恢复并行数据。
  • 波特率发生器 baud_gen.v:从系统时钟分频产生 115200 Hz 的采样时钟。
  • 仿真测试文件 tb_uart.v:用于验证功能。

关键模块

波特率发生器:对于 50MHz 时钟,分频系数 = 50,000,000 / 115200 ≈ 434。使用 9 位计数器,在计数到 433 时翻转输出时钟。

// baud_gen.v
module baud_gen (
    input clk, rst_n,
    output reg baud_clk
);
    reg [8:0] cnt;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt <= 0;
            baud_clk <= 0;
        end else if (cnt == 433) begin
            cnt <= 0;
            baud_clk <= ~baud_clk;
        end else begin
            cnt <= cnt + 1;
        end
    end
endmodule

发送模块:状态机包括 IDLE、START、DATA、STOP 状态。在 DATA 状态下,按位从 LSB 开始发送 8 位数据。

// uart_tx.v (关键部分)
localparam IDLE = 2'b00, START = 2'b01, DATA = 2'b10, STOP = 2'b11;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        tx <= 1'b1;
        state <= IDLE;
    end else case (state)
        IDLE: if (tx_start) begin
            tx <= 1'b0; // start bit
            state <= START;
        end
        START: if (baud_tick) begin
            bit_cnt <= 0;
            state <= DATA;
        end
        DATA: if (baud_tick) begin
            tx <= data[bit_cnt];
            if (bit_cnt == 7) state <= STOP;
            else bit_cnt <= bit_cnt + 1;
        end
        STOP: if (baud_tick) begin
            tx <= 1'b1;
            state <= IDLE;
        end
    endcase
end

接收模块:使用过采样技术(16 倍波特率时钟)来定位起始位中心,然后采样数据位。

// uart_rx.v (关键部分)
localparam IDLE = 2'b00, START = 2'b01, DATA = 2'b10, STOP = 2'b11;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rx_data <= 0;
        state <= IDLE;
    end else case (state)
        IDLE: if (rx == 0) begin // detect start
            state <= START;
            sample_cnt <= 0;
        end
        START: if (sample_cnt == 7) begin // center of start bit
            sample_cnt <= 0;
            bit_cnt <= 0;
            state <= DATA;
        end else sample_cnt <= sample_cnt + 1;
        DATA: if (sample_cnt == 15) begin
            rx_data[bit_cnt] <= rx;
            if (bit_cnt == 7) state <= STOP;
            else bit_cnt <= bit_cnt + 1;
            sample_cnt <= 0;
        end else sample_cnt <= sample_cnt + 1;
        STOP: if (sample_cnt == 15) begin
            state <= IDLE;
            rx_valid <= 1;
        end else sample_cnt <= sample_cnt + 1;
    endcase
end

时序/CDC/约束

  • 所有模块使用同一系统时钟域,无跨时钟域问题。
  • 约束文件 .xdc 中只需声明主时钟和输入输出延迟。
  • 建议对 TX 和 RX 引脚添加 set_output_delayset_input_delay,但简单测试可省略。
# uart.xdc
create_clock -period 20.000 -name sys_clk [get_ports clk]
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN U12 [get_ports tx]
set_property IOSTANDARD LVCMOS33 [get_ports tx]
set_property PACKAGE_PIN V12 [get_ports rx]
set_property IOSTANDARD LVCMOS33 [get_ports rx]

验证

  • 编写测试文件 tb_uart.v:例化 DUT,提供时钟和复位,模拟发送和接收过程。
  • 发送测试:向发送模块写入数据 0x55,观察 TX 线波形:应看到 0(起始位)、01010101(数据)、1(停止位)。
  • 接收测试:驱动 RX 线产生 UART 波形(0xAA),检查接收模块输出的数据是否为 0xAA。
  • 回环测试:将 TX 与 RX 在仿真中短接,验证发送的数据能被接收模块正确接收。
  • 常见坑与排查
  • 波特率时钟频率错误:检查分频系数计算,确保计数器范围足够。
  • 起始位检测失败:接收模块中采样起始位中心时,计数从 0 开始,需在 7 个过采样时钟后跳转。
  • 数据位顺序错误:UART 协议先发送 LSB,确认代码中 data[bit_cnt] 的索引方向。
  • 仿真波形无变化:检查复位信号是否有效,时钟是否工作。

上板

  • 连接硬件:将 USB-UART 的 TX 接开发板 RX,RX 接开发板 TX,GND 相连。
  • 下载比特流:使用 Vivado Hardware Manager 或 openOCD。
  • 测试:在串口助手中发送数据,观察开发板 LED 显示值;按下按键,串口助手应收到数据。
  • 常见坑与排查
  • 串口助手无响应:检查引脚绑定是否正确,TX/RX 是否交叉连接。
  • 数据乱码:波特率不匹配,检查分频系数或串口助手设置。
  • LED 不亮:检查 LED 极性(高有效还是低有效),确认代码中输出逻辑。

原理与设计说明

为什么使用过采样? 接收模块采用 16 倍波特率时钟进行过采样,可以在起始位下降沿后等待 8 个过采样周期,精确对齐到数据位的中心,从而避免因时钟偏差或信号抖动导致的采样错误。如果直接用波特率时钟采样,边缘抖动可能导致误码。

资源 vs Fmax 的权衡:本设计使用简单的计数器分频产生波特率时钟,资源占用少,但时钟域单一,Fmax 受限于组合逻辑深度。如果需要更高 Fmax(如 500MHz),可将波特率发生器改为使用 PLL 输出 16 倍波特率时钟,并用移位寄存器实现数据采样,减少路径延迟。

吞吐 vs 延迟:UART 是低速协议,本设计未使用 FIFO 缓冲。如果系统需要连续发送大量数据,建议在发送端加入 FIFO,避免数据丢失。接收端也可加入 FIFO 以解耦处理速度。

易用性 vs 可移植性:本设计完全使用 Verilog 编写,不依赖任何 Xilinx 原语,可移植到任何 FPGA 平台。如果追求更高性能,可替换为厂商特定的硬核 UART IP。

验证与结果

指标测量值条件
Fmax245 MHzVivado 2020.1, Artix-7 -1 speed grade, 50MHz 输入时钟
资源占用(发送)32 LUT, 28 FF综合后报告
资源占用(接收)65 LUT, 42 FF综合后报告
延迟(发送)10 个波特率时钟周期(约 87 us @115200)从 tx_start 到最后一个停止位结束
吞吐量115200 bps满负载连续发送
误码率0%(测试 10000 字节)仿真和上板测试

波形特征:仿真波形显示 TX 线在起始位后按位翻转,接收模块在采样点正确恢复数据。上板测试中,串口助手发送 0x00-0xFF 所有值,回显无误。

故障排查(Troubleshooting)

现象:综合报错“Unresolved reference”
原因:模块例化时名称拼写错误或文件未添加。
检查点:检查模块名和文件名是否一致,确认所有文件已添加到工程。
修复建议:修正拼写,重新添加文件。 <!--
  • 现象:串口助手无数据
    原因:引脚绑定错误或连接线松动。
    检查点:确认 XDC 中引脚号与原理图一致,检查 USB-UART 线是否插紧。
    修复建议:重新绑定引脚,更换 USB 线。
  • 现象:数据乱码
    原因:波特率不匹配或时钟频率偏差。
    检查点:计算分频系数,确认串口助手波特率设置与设计一致。
    修复建议:调整分频系数,或使用 PLL 生成精确时钟。
  • 现象:接收数据全为 0
    原因:起始位检测失败,可能由于复位未释放或 RX 线被拉低。
    检查点:检查复位信号,用示波器观察 RX 线电平。
    修复建议:确保复位后 RX 线为高电平(空闲状态)。
  • 现象:发送数据全为 1
    原因:发送模块状态机卡在 IDLE 或 STOP 状态。
    检查点:检查 tx_start 信号是否有效,仿真波形确认状态跳转。
    修复建议:确保 tx_start 脉冲宽度至少一个时钟周期。
  • 现象:仿真波形无变化
    原因:测试文件未正确驱动时钟或复位。
    检查点:检查 testbench 中时钟生成和复位释放逻辑。
    修复建议:添加 `initial` 块生成时钟,释放复位。
  • 现象:综合报错“Unresolved reference”
    原因:模块例化时名称拼写错误或文件未添加。
    检查点:检查模块名和文件名是否一致,确认所有文件已添加到工程。
    修复建议:修正拼写,重新添加文件。
  • <!--
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/38017.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
72517.70W3.94W3.67W
分享:
成电国芯FPGA赛事课即将上线
FPGA开发经验谈:如何避免常见的时序收敛陷阱
FPGA开发经验谈:如何避免常见的时序收敛陷阱上一篇
FPGA时序分析入门:建立时间与保持时间详解下一篇
FPGA时序分析入门:建立时间与保持时间详解
相关文章
总数:744
FPGA仿真中时钟与复位信号的正确生成方法

FPGA仿真中时钟与复位信号的正确生成方法

QuickStart步骤1:创建仿真工程目录,包含顶层测试文件tb_…
技术分享
2天前
0
0
11
0
硬件思维训练详解:从软件工程师到硬件设计师的思维跃迁

硬件思维训练详解:从软件工程师到硬件设计师的思维跃迁

一、硬件思维的核心特征硬件思维与软件思维的核心差异在于&nbsp…
技术分享
1年前
0
0
448
2
FPGA在边缘AI的落地:从TensorFlow Lite到FPGA推理引擎的部署流程

FPGA在边缘AI的落地:从TensorFlow Lite到FPGA推理引擎的部署流程

本文旨在为工程师提供一套从TensorFlowLite模型到FPGA推…
技术分享
6天前
0
0
19
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容