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 MHz | 50MHz时钟下裕量充足 |
| 误码率 (BER) | <10^-9 | 仿真中随机抖动±2% |
| 延迟 (发送到接收) | 10.4 us | 115200波特率,一个字节 |
仿真波形显示:发送器输出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)对收发器进行形式化验证,确保无死锁和协议违规。




