1. UART IP核项目概述
UART(Universal Asynchronous Receiver/Transmitter)作为最基础的串行通信协议之一,在嵌入式系统和FPGA开发中扮演着重要角色。这个项目使用Verilog HDL实现了一个完整的UART IP核,支持可配置的波特率、数据位和校验位设置,并通过VCS(Verilog Compiler Simulator)进行了功能验证。该设计最大的特点是高度模块化和可移植性,能够无缝集成到不同厂商的FPGA平台中。
我在实际项目中多次使用类似设计,发现一个可靠的UART IP核能显著简化嵌入式系统与上位机的调试接口开发。这个实现特别注重三个方面:首先是严格的时序控制,确保在高速时钟域下仍能稳定工作;其次是完备的错误检测机制,包括帧错误、溢出错误等常见问题的处理;最后是简洁的APB总线接口设计,便于集成到SoC系统中。
2. 核心架构设计解析
2.1 整体模块划分
这个UART IP核采用经典的三层架构:
code复制 +---------------+
| APB接口层 | <-> 总线交互
+---------------+
|
+---------------+
| 控制寄存器层 | <-> 配置管理
+---------------+
|
+---------------+
| UART核心层 | <-> 串行通信
+---------------+
APB接口层处理与系统总线的交互,采用标准的APB3协议,支持32位数据宽度。控制寄存器层包含以下关键寄存器:
- CR (Control Register):配置波特率、数据位宽等参数
- SR (Status Register):反映当前状态和错误标志
- TXDATA/RXDATA:收发数据缓冲区
UART核心层又细分为:
- 波特率发生器:使用分频计数器实现
- 发送器FSM:处理并转串过程
- 接收器FSM:处理串转并过程
- 中断控制器:生成各类中断信号
2.2 关键时序设计
波特率时钟生成采用经典的计数器分频方案:
verilog复制always @(posedge clk or posedge rst) begin
if(rst) begin
baud_counter <= 0;
baud_tick <= 0;
end else begin
if(baud_counter == CLK_DIVIDER-1) begin
baud_counter <= 0;
baud_tick <= 1;
end else begin
baud_counter <= baud_counter + 1;
baud_tick <= 0;
end
end
end
其中CLK_DIVIDER = 系统时钟频率/(16×目标波特率),这种16倍过采样的设计能有效提高接收端的抗干扰能力。
3. Verilog实现细节
3.1 发送器状态机设计
发送器采用五状态FSM:
verilog复制localparam [2:0] IDLE = 3'b000,
START = 3'b001,
DATA = 3'b010,
PARITY = 3'b011,
STOP = 3'b100;
always @(posedge clk or posedge rst) begin
if(rst) begin
state <= IDLE;
tx_shift <= 8'hFF;
tx_out <= 1'b1;
end else if(baud_tick) begin
case(state)
IDLE: if(tx_start) begin
state <= START;
tx_shift <= tx_data;
tx_out <= 1'b0; // 起始位
end
START: begin
state <= DATA;
tx_out <= tx_shift[0];
tx_shift <= {1'b0, tx_shift[7:1]};
bit_cnt <= DATA_BITS-1;
end
// 其他状态转换...
endcase
end
end
3.2 接收器同步设计
为避免亚稳态问题,接收端采用三级同步器:
verilog复制reg rx_sync1, rx_sync2, rx_sync3;
always @(posedge clk or posedge rst) begin
if(rst) begin
rx_sync1 <= 1'b1;
rx_sync2 <= 1'b1;
rx_sync3 <= 1'b1;
end else begin
rx_sync1 <= rx_in;
rx_sync2 <= rx_sync1;
rx_sync3 <= rx_sync2;
end
end
接收采样点选择在波特率周期的中点附近,通过采样多数表决提高可靠性:
verilog复制wire sample_point = (baud_counter == CLK_DIVIDER/2);
reg [2:0] sample_reg;
always @(posedge clk) begin
if(sample_point) begin
sample_reg <= {sample_reg[1:0], rx_sync3};
end
end
wire sampled_bit = (sample_reg[2] & sample_reg[1]) |
(sample_reg[2] & sample_reg[0]) |
(sample_reg[1] & sample_reg[0]);
4. VCS仿真验证方案
4.1 测试平台架构
测试平台采用层次化验证方法:
code复制test_top
├── uart_dut (被测设计)
├── uart_bfm (总线功能模型)
├── uart_monitor (监测器)
└── scoreboard (记分板)
关键测试用例包括:
- 寄存器读写测试
- 不同波特率下的自发自收测试
- 帧错误注入测试
- 溢出错误测试
- 中断功能测试
4.2 自动化验证脚本
使用Makefile管理仿真流程:
makefile复制SIM = vcs
TOP = test_top
UVM = -ntb_opts uvm-1.2
all: compile run
compile:
$(SIM) $(UVM) -sverilog -debug_access+all -timescale=1ns/1ps \
-f filelist.f -top $(TOP) -l compile.log
run:
./simv +UVM_TESTNAME=base_test -l run.log
典型波形检查点:
- 起始位检测是否正确
- 数据采样点是否准确
- 停止位是否完整
- 错误标志是否及时置位
5. 可移植性设计要点
5.1 参数化设计
核心模块采用SystemVerilog参数化设计:
verilog复制module uart_core #(
parameter CLK_FREQ = 50_000_000,
parameter BAUD = 115200,
parameter DATA_BITS = 8,
parameter PARITY_EN = 1,
parameter STOP_BITS = 1
) (
// 端口声明...
);
5.2 跨平台适配层
针对不同FPGA平台的时钟管理:
verilog复制`ifdef XILINX
BUFG u_bufg(.I(clk_in), .O(sys_clk));
`elsif ALTERA
altclkctrl u_clkctrl(.inclk(clk_in), .outclk(sys_clk));
`else
assign sys_clk = clk_in;
`endif
5.3 标准接口封装
提供两种接口选项:
- APB总线接口(适合SoC集成)
- 独立信号接口(适合简单系统)
接口转换示例:
verilog复制generate
if(USE_APB) begin
// APB接口逻辑
always @(posedge pclk) begin
if(psel && penable && pwrite)
reg_write(paddr[7:0], pwdata);
if(psel && !penable)
prdata <= reg_read(paddr[7:0]);
end
end else begin
// 独立信号接口
assign tx_data = ext_tx_data;
assign ext_rx_data = rx_data;
end
endgenerate
6. 实际应用中的经验分享
6.1 波特率误差控制
实测发现,当波特率误差超过3%时通信可靠性显著下降。建议:
- 系统时钟至少是波特率的16倍
- 使用分数分频器减小误差(特别是非标准波特率)
- 在CR寄存器中添加校准位控制
6.2 中断优化策略
常见问题:频繁小数据量传输导致中断风暴
解决方案:
- 设置FIFO阈值中断(如半满、四分之三满)
- 实现中断聚合功能
- 提供轮询模式选择
6.3 功耗优化技巧
针对低功耗场景:
verilog复制// 动态时钟门控
always @(*) begin
if(state == IDLE && !rx_in)
baud_clk_en = 1'b1;
else
baud_clk_en = |{tx_busy, rx_busy};
end
7. 典型问题排查指南
7.1 无数据接收
检查步骤:
- 确认波特率配置正确(检查CR寄存器)
- 测量RX引脚物理信号
- 检查采样点设置(用VCS波形调试)
- 验证同步链工作正常
7.2 数据传输错误
常见原因:
- 时钟域交叉未处理好
- 波特率误差过大
- 信号完整性问题(PCB设计)
- 抗干扰措施不足(建议添加迟滞比较器)
7.3 性能优化记录
实测数据对比:
| 优化措施 | 最大稳定波特率提升 |
|---|---|
| 添加收发FIFO | 3.2x |
| 优化状态机编码 | 1.5x |
| 使用流水线处理 | 2.1x |
这个UART IP核经过多次迭代,目前在Artix-7平台上实测稳定支持到3Mbps波特率。关键是把接收器的过采样逻辑优化为并行处理,同时发送端采用预取机制隐藏延迟。对于需要更高性能的场景,建议考虑使用USART或添加DMA支持。