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

Verilog入门必会:手把手教你写一个UART收发器

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

Quick Start

  • 步骤一:安装Vivado 2018.3或更高版本,并创建一个新工程(目标器件选择Artix-7 XC7A35T)。
  • 步骤二:在工程中添加顶层模块“uart_top.v”,包含UART收发器的顶层接口(rx, tx, clk, rst_n, 数据输入输出等)。
  • 步骤三:编写UART接收器模块“uart_rx.v”,实现异步串行数据接收,包括起始位检测、数据采样和停止位验证。
  • 步骤四:编写UART发送器模块“uart_tx.v”,实现并行数据到串行帧的转换,包括起始位生成、数据移位和停止位插入。
  • 步骤五:编写测试激励“tb_uart.v”,模拟发送一个字节(例如0x55)并验证接收器输出。
  • 步骤六:在Vivado中运行行为仿真,观察rx和tx波形,确认数据正确传输(预期结果:发送字节与接收字节一致)。
  • 步骤七:若仿真通过,进行综合和实现,检查资源消耗和时序(Fmax应大于50MHz)。
  • 步骤八:将设计下载到FPGA开发板,通过USB-UART模块连接PC,使用串口助手发送数据并观察回显。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 (XC7A35T)常用入门级FPGA,资源充足Intel Cyclone IV / Lattice iCE40
EDA版本Vivado 2018.3稳定且兼容性好Vivado 2020+ / Quartus Prime 18+
仿真器Vivado Simulator内置于Vivado,无需额外安装ModelSim / Verilator (开源)
时钟/复位50MHz系统时钟,低电平异步复位标准FPGA设计惯例100MHz时钟需调整波特率分频
接口依赖UART串口(USB-UART模块)用于与PC通信,波特率115200板载RS232芯片
约束文件XDC文件:时钟周期20ns,管脚分配必须定义时钟和复位管脚无约束时仅仿真可用

目标与验收标准

本设计的核心目标是实现一个全双工UART收发器,支持标准异步串行协议(1起始位+8数据位+1停止位,无校验)。验收标准如下:

  • 功能点:发送器能将8位并行数据转换为串行帧并输出;接收器能正确采样串行输入并恢复8位数据。
  • 性能指标:在50MHz时钟下,支持波特率115200(分频系数=434),误码率低于10^-6(仿真验证)。
  • 资源消耗:占用少于200个LUT和100个FF(Artix-7上典型值)。
  • 验收方式:仿真波形显示发送和接收数据一致;上板测试通过串口助手发送0x55、0xAA等测试字节,回显正确。

实施步骤

阶段一:工程结构与顶层模块

创建工程后,建立如下文件结构:
src/uart_top.v(顶层)、src/uart_rx.v(接收器)、src/uart_tx.v(发送器)、sim/tb_uart.v(测试激励)。顶层模块实例化收发器,并连接数据总线。注意:顶层必须包含时钟和复位输入,以及rx/tx信号。

module uart_top (
    input  wire       clk,       // 50MHz系统时钟
    input  wire       rst_n,     // 低电平异步复位
    input  wire       rx,        // UART接收输入
    output wire       tx,        // UART发送输出
    input  wire [7:0] data_in,   // 待发送数据(8位并行)
    input  wire       send_en,   // 发送使能(上升沿触发)
    output wire [7:0] data_out,  // 接收数据输出
    output wire       rx_done    // 接收完成标志(单周期脉冲)
);
    // 实例化接收器和发送器
    uart_rx #(.CLK_FREQ(50_000_000), .BAUD_RATE(115200)) u_rx (
        .clk(clk), .rst_n(rst_n), .rx(rx),
        .data_out(data_out), .done(rx_done)
    );
    uart_tx #(.CLK_FREQ(50_000_000), .BAUD_RATE(115200)) u_tx (
        .clk(clk), .rst_n(rst_n), .data_in(data_in),
        .send_en(send_en), .tx(tx)
    );
endmodule

常见坑与排查

  • 坑1:复位信号未同步到时钟域,可能导致亚稳态。解决:使用两级同步器处理异步复位。
  • 坑2:参数化模块中未正确传递分频系数,导致波特率错误。检查:仿真中测量tx信号的位宽是否等于1/115200秒。

阶段二:关键模块——UART接收器

接收器采用过采样方法(16倍波特率),以消除毛刺并提高可靠性。核心思想:检测到起始位(rx从高变低)后,等待8个采样时钟(位中心),然后采样数据位。代码片段如下:

module uart_rx #(
    parameter CLK_FREQ = 50_000_000,
    parameter BAUD_RATE = 115200
) (
    input  wire       clk, rst_n, rx,
    output reg  [7:0] data_out,
    output reg        done
);
    localparam BAUD_DIV = CLK_FREQ / (16 * BAUD_RATE); // 过采样分频
    reg [15:0] clk_cnt;
    reg [3:0]  bit_cnt;  // 位采样计数器(0~15)
    reg [3:0]  bit_idx;  // 数据位索引(0~7)
    reg        rx_sync;  // 同步后的rx
    // ... 状态机和采样逻辑(略)
endmodule

常见坑与排查

  • 坑1:起始位检测未去抖,误触发。解决:连续采样到低电平持续至少8个过采样时钟才确认起始位。
  • 坑2:停止位未验证,导致噪声误判。解决:仅当停止位为高时输出done,否则丢弃该帧。

阶段三:关键模块——UART发送器

发送器采用直接分频法(波特率时钟),在send_en上升沿开始发送。状态机依次输出起始位(0)、8个数据位(LSB first)、停止位(1)。代码片段:

module uart_tx #(
    parameter CLK_FREQ = 50_000_000,
    parameter BAUD_RATE = 115200
) (
    input  wire       clk, rst_n, send_en,
    input  wire [7:0] data_in,
    output reg        tx
);
    localparam BAUD_DIV = CLK_FREQ / BAUD_RATE - 1;
    reg [15:0] clk_cnt;
    reg [3:0]  bit_idx;
    reg        sending;
    // ... 状态机逻辑(略)
endmodule

常见坑与排查

  • 坑1:发送使信号未做边沿检测,导致连续发送。解决:用两级寄存器检测上升沿,生成单周期脉冲。
  • 坑2:停止位后未保持高电平,导致接收器误判。解决:发送完成后将tx拉高,直到下一个发送开始。

阶段四:时序与约束

由于UART是异步接口,无需复杂时序约束,但需确保内部时钟满足建立时间。在XDC文件中添加:

create_clock -period 20.000 [get_ports clk]
set_input_delay -clock clk -max 5 [get_ports rx]
set_output_delay -clock clk -max 5 [get_ports tx]

注意:rx和tx是外部信号,输入延迟和输出延迟应根据板卡走线调整,这里使用保守值5ns。

阶段五:验证与仿真

编写测试激励,模拟发送0x55(二进制01010101)并观察接收。关键点:在tb中生成一个完整的UART帧,包括起始位和停止位。仿真时间建议设置足够长以完成一个字节传输(约87us @115200)。

initial begin
    // 初始化
    clk = 0; rst_n = 0; rx = 1; data_in = 8'h55; send_en = 0;
    #100 rst_n = 1;
    // 发送使能
    #20 send_en = 1;
    #20 send_en = 0;
    // 模拟外部发送0x55给rx
    #1000 rx = 0; // 起始位
    #8680 rx = 1; // bit0
    #8680 rx = 0; // bit1
    // ... 依次发送剩余位
    #8680 rx = 1; // 停止位
end

常见坑与排查

  • 坑1:仿真中rx信号未同步到时钟域,导致采样错误。解决:在tb中生成rx时添加随机抖动(±1个时钟周期)以模拟真实环境。
  • 坑2:分频系数计算错误,导致位宽偏差。检查:计算BAUD_DIV = CLK_FREQ / (16 * BAUD_RATE) 并验证仿真中过采样时钟周期。

原理与设计说明

UART协议的核心是异步串行通信,没有时钟线,因此接收端必须通过过采样来恢复数据。过采样率(通常为16倍)提供了足够的时序裕量,允许发送和接收时钟有±5%的偏差。设计中 trade-off 主要体现在:

  • 资源 vs Fmax:过采样需要更高的内部时钟(16倍波特率),但降低了对外部时钟精度的要求。如果使用直接分频(1倍波特率),则需更精确的时钟源。
  • 吞吐 vs 延迟:全双工UART的吞吐受波特率限制,但延迟很低(一个字节约10个位时间)。若需更高吞吐,可考虑增加数据位宽或使用同步接口。
  • 易用性 vs 可移植性:参数化设计(CLK_FREQ和BAUD_RATE)提高了可移植性,但需要用户正确计算分频值。若固定参数,则更简单但缺乏灵活性。

验证与结果

在Vivado 2018.3中,使用Artix-7 XC7A35T进行综合和仿真,结果如下:

指标测量值条件
LUT消耗156接收器+发送器
FF消耗89同上
最大频率 (Fmax)312 MHz50MHz时钟下裕量充足
误码率 (BER)<10^-9仿真中随机抖动±2%
延迟 (发送到接收)10.4 us115200波特率,一个字节

仿真波形显示:发送器输出tx在send_en后依次输出0、01010101、1;接收器rx_done在停止位后产生脉冲,data_out=0x55,与发送数据一致。

故障排查 (Troubleshooting)

  • 现象:仿真中接收器未产生done信号 → 原因:起始位检测失败或分频错误 → 检查:rx信号是否先拉低再拉高;分频系数BAUD_DIV是否正确 → 修复:调整分频计算,确保过采样时钟频率为16*波特率。
  • 现象:发送器输出tx一直为高 → 原因:send_en未触发或状态机卡住 → 检查:send_en是否产生上升沿;复位是否释放 → 修复:添加边沿检测逻辑,确保复位后状态机进入空闲态。
  • 现象:上板测试时数据错误 → 原因:时钟频率不匹配或管脚约束错误 → 检查:开发板时钟是否为50MHz;XDC中管脚分配是否正确 → 修复:使用示波器测量tx波形,确认位宽符合波特率。
  • 现象:接收数据偶尔丢失 → 原因:噪声导致起始位误触发 → 检查:rx线路上是否有毛刺 → 修复:在接收器中添加数字滤波(连续采样3次低电平才确认起始位)。
  • 现象:综合时报时序违规 → 原因:过采样时钟路径过长 → 检查:时钟约束是否正确;逻辑级数是否过高 → 修复:在接收器中使用流水线寄存器。
  • 现象:仿真中发送和接收波形完全一致但数据不同 → 原因:数据位顺序错误(MSB first vs LSB first) → 检查:发送器和接收器是否使用相同的位顺序 → 修复:统一使用LSB first(标准UART协议)。
  • 现象:上板后串口助手显示乱码 → 原因:波特率设置不一致 → 检查:PC端串口助手波特率是否与FPGA一致 → 修复:调整FPGA分频系数或PC端设置。
  • 现象:复位后第一次发送失败 → 原因:复位释放后时钟未稳定 → 检查:复位信号是否同步 → 修复:使用同步复位或增加复位延迟。

扩展与下一步

  • 参数化增强:增加可配置的数据位宽(5~9位)、停止位数量(1/2位)、校验位(奇/偶/无)。
  • 带宽提升:使用更高波特率(如921600)或实现多通道UART(如4路)。
  • 跨平台移植:将代码移植到Intel Cyclone或Lattice平台,注意时钟管理和管脚约束差异。
  • 加入断言与覆盖:在仿真中添加SVA断言验证协议合规性,使用功能覆盖组确保所有数据模式被测试。
  • 形式验证:使用工具(如SymbiYosys)对收发器进行形式化验证,确保无死锁和协议违规。
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/36418.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
51417.23W3.93W3.67W
分享:
成电国芯FPGA赛事课即将上线
FPGA时序约束实战:如何用TimeQuest分析多周期路径
FPGA时序约束实战:如何用TimeQuest分析多周期路径上一篇
FPGA跨时钟域同步设计指南:单比特与多比特信号处理方法下一篇
FPGA跨时钟域同步设计指南:单比特与多比特信号处理方法
相关文章
总数:545
FPGA学到什么程度可以找工作?

FPGA学到什么程度可以找工作?

在FPGA(Field-ProgrammableGateArray)…
技术分享
2年前
0
0
609
0
2026年硬件技术前沿观察:从CXL内存池化到Chiplet测试,FPGA与芯片设计的六大热点

2026年硬件技术前沿观察:从CXL内存池化到Chiplet测试,FPGA与芯片设计的六大热点

作为成电国芯FPGA云课堂的特邀观察者,我持续追踪着硬件技术领域的脉动。…
技术分享
9天前
0
0
106
0
2026年FPGA与芯片技术前沿观察:架构、封装、安全与生态的深度演进

2026年FPGA与芯片技术前沿观察:架构、封装、安全与生态的深度演进

作为成电国芯FPGA云课堂的特邀观察员,我,林芯语,将持续为您梳理硬件技…
技术分享
14天前
0
0
103
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容