Quick Start
- 步骤一:搭建开发环境。安装Vivado(推荐2020.1及以上版本)并确保板卡驱动正常。
- 步骤二:新建Vivado工程,选择目标器件(如Xilinx Artix-7 XC7A35T)。
- 步骤三:创建顶层模块,例化UART收发器IP(或自行编写RTL)。
- 步骤四:编写UART发送模块(tx),支持8位数据、1位停止位、无校验。
- 步骤五:编写UART接收模块(rx),同步采样并恢复数据。
- 步骤六:连接顶层模块,将发送与接收对接,形成回环测试。
- 步骤七:编写testbench仿真,验证发送与接收波形正确。
- 步骤八:综合、实现并生成比特流,下载到FPGA板卡。
- 步骤九:使用串口调试助手(如Putty)发送0x55,观察回显数据是否一致。
- 步骤十:验收:发送0x00~0xFF所有字节,回显无误即为成功。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T(如Nexys A7) | 低成本、资源充足,适合UART实验 | Intel Cyclone IV / Lattice iCE40 |
| EDA版本 | Vivado 2020.1 | 稳定,支持IP集成 | Vivado 2019.1 / 2022.1 |
| 仿真器 | Vivado Simulator | 内置于Vivado,无需额外安装 | ModelSim / Questa |
| 时钟/复位 | 50MHz系统时钟,低电平异步复位 | 板载晶振,复位按键 | 100MHz时钟(需调整波特率分频) |
| 接口依赖 | USB-UART(FT232) | 板载或外接,用于PC通信 | RS232电平转换器 |
| 约束文件 | XDC约束:时钟周期、引脚分配 | 必须指定UART TX/RX引脚 | 自动引脚分配(仅仿真) |
目标与验收标准
- 功能点:实现UART发送与接收,支持波特率9600/115200可配,8位数据、1位停止位、无校验。
- 性能指标:发送与接收无误码率,连续传输1000字节无丢包。
- 资源占用:LUT ≤ 200,FF ≤ 150,BRAM 0(纯逻辑实现)。
- Fmax:≥ 200MHz(基于Artix-7速度等级-1)。
- 验收方式:上板后,串口调试助手发送0x55,回显0x55;发送0x00~0xFF循环,回显一致。
实施步骤
工程结构
project/
├── src/
│ ├── uart_top.v // 顶层模块(例化tx和rx)
│ ├── uart_tx.v // 发送模块
│ ├── uart_rx.v // 接收模块
│ └── baud_gen.v // 波特率发生器
├── sim/
│ └── tb_uart.v // 测试平台
├── constr/
│ └── uart.xdc // 时序与引脚约束
└── ip/ // 可选IP(如FIFO)关键模块:波特率发生器
波特率发生器是一个计数器,根据系统时钟和波特率计算分频值。例如,50MHz时钟,波特率115200时,分频系数 = 50_000_000 / 115200 ≈ 434。实际使用中需考虑采样点位置,通常使用16倍过采样(即分频系数 = 时钟频率 / (波特率 × 16))。
// baud_gen.v
module baud_gen #(
parameter CLK_FREQ = 50_000_000,
parameter BAUD_RATE = 115200
)(
input wire clk,
input wire rst_n,
output reg baud_tick // 每个数据位宽度内产生16个脉冲
);
localparam DIV = CLK_FREQ / (BAUD_RATE * 16);
reg [15:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 0;
baud_tick <= 0;
end else if (cnt < DIV - 1) begin
cnt <= cnt + 1;
baud_tick <= 0;
end else begin
cnt <= 0;
baud_tick <= 1;
end
end
endmodule关键模块:UART发送模块
UART发送模块负责将并行数据转换为串行比特流,并按照起始位、数据位、停止位的格式输出。核心机制:当检测到发送使能信号时,模块拉低TX线作为起始位,随后依次输出8位数据(LSB first),最后拉高TX线作为停止位。每次状态切换由波特率时钟(baud_tick)同步。
// uart_tx.v
module uart_tx #(
parameter DATA_BITS = 8,
parameter STOP_BITS = 1
)(
input wire clk,
input wire rst_n,
input wire [DATA_BITS-1:0] data_in,
input wire tx_start,
output reg tx,
output reg tx_busy
);
localparam IDLE = 0, START = 1, DATA = 2, STOP = 3;
reg [1:0] state;
reg [3:0] bit_cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
tx <= 1;
tx_busy <= 0;
bit_cnt <= 0;
end else case (state)
IDLE: begin
tx <= 1;
tx_busy <= 0;
if (tx_start) begin
state <= START;
tx_busy <= 1;
end
end
START: begin
tx <= 0;
state <= DATA;
bit_cnt <= 0;
end
DATA: begin
tx <= data_in[bit_cnt];
if (bit_cnt == DATA_BITS-1) begin
state <= STOP;
end else begin
bit_cnt <= bit_cnt + 1;
end
end
STOP: begin
tx <= 1;
state <= IDLE;
tx_busy <= 0;
end
endcase
end
endmodule关键模块:UART接收模块
UART接收模块通过过采样(16倍)检测起始位下降沿,并在数据位中点采样以恢复数据。核心机制:空闲时RX线为高,检测到下降沿后启动接收状态机,在每个数据位的中间点(第8个baud_tick脉冲)采样RX值,最终组合成并行数据输出。
// uart_rx.v
module uart_rx #(
parameter DATA_BITS = 8,
parameter STOP_BITS = 1
)(
input wire clk,
input wire rst_n,
input wire rx,
output reg [DATA_BITS-1:0] data_out,
output reg data_valid
);
localparam IDLE = 0, START = 1, DATA = 2, STOP = 3;
reg [1:0] state;
reg [3:0] bit_cnt;
reg [3:0] sample_cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
data_out <= 0;
data_valid <= 0;
bit_cnt <= 0;
sample_cnt <= 0;
end else case (state)
IDLE: begin
data_valid <= 0;
if (rx == 0) begin
state <= START;
sample_cnt <= 0;
end
end
START: begin
if (sample_cnt == 7) begin // 采样中点
state <= DATA;
bit_cnt <= 0;
sample_cnt <= 0;
end else begin
sample_cnt <= sample_cnt + 1;
end
end
DATA: begin
if (sample_cnt == 15) begin
data_out[bit_cnt] <= rx;
if (bit_cnt == DATA_BITS-1) begin
state <= STOP;
sample_cnt <= 0;
end else begin
bit_cnt <= bit_cnt + 1;
sample_cnt <= 0;
end
end else begin
sample_cnt <= sample_cnt + 1;
end
end
STOP: begin
if (sample_cnt == 15) begin
state <= IDLE;
data_valid <= 1;
end else begin
sample_cnt <= sample_cnt + 1;
end
end
endcase
end
endmodule顶层模块与回环测试
顶层模块例化发送和接收模块,并将接收到的数据直接反馈到发送输入端,形成回环。这样,PC发送的字节会立即被回显,便于验证通信链路完整性。
// uart_top.v
module uart_top (
input wire clk,
input wire rst_n,
input wire rx,
output wire tx
);
wire [7:0] rx_data;
wire rx_valid;
wire tx_busy;
reg [7:0] tx_data;
reg tx_start;
uart_rx u_rx (
.clk(clk),
.rst_n(rst_n),
.rx(rx),
.data_out(rx_data),
.data_valid(rx_valid)
);
uart_tx u_tx (
.clk(clk),
.rst_n(rst_n),
.data_in(tx_data),
.tx_start(tx_start),
.tx(tx),
.tx_busy(tx_busy)
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_data <= 0;
tx_start <= 0;
end else if (rx_valid && !tx_busy) begin
tx_data <= rx_data;
tx_start <= 1;
end else begin
tx_start <= 0;
end
end
endmodule验证结果
仿真波形应显示:发送模块在tx_start脉冲后依次输出起始位(低)、8位数据(LSB first)、停止位(高);接收模块在检测到起始位后,在每个数据位中点采样,输出并行数据并置位data_valid。上板测试时,串口调试助手发送0x55(二进制01010101),应回显相同字节。连续发送0x00~0xFF循环,回显一致即表示验证通过。
排障指南
- 问题:回显数据错误或乱码。原因:波特率不匹配或时钟分频计算错误。解决:核对分频系数,确保PC端波特率与FPGA一致。
- 问题:无回显。原因:引脚约束错误或硬件连接问题。解决:检查XDC文件中TX/RX引脚分配,确认USB-UART驱动正常。
- 问题:仿真波形异常。原因:状态机逻辑错误或复位时序问题。解决:检查状态转移条件,确保复位信号低电平有效。
扩展建议
- 增加FIFO缓冲:使用Block RAM实现FIFO,支持连续数据流传输,避免数据丢失。
- 支持多波特率:通过寄存器配置分频系数,实现运行时切换波特率。
- 集成CRC校验:在数据包末尾添加CRC字段,增强通信可靠性。
参考
- Xilinx UG949:Vivado Design Suite用户指南
- UART协议标准:EIA RS-232规范
- FPGA设计实战:UART通信模块实现(相关技术博客)
附录
附录A:完整工程代码包(含约束文件、测试平台)可在项目仓库获取。附录B:常见问题FAQ持续更新中。




