1. Verilog串口控制器设计背景与需求分析
在工业控制和嵌入式系统开发中,串口通信因其简单可靠的特性,仍然是设备间数据交换的主流方式之一。我最近完成的一个工业传感器项目就遇到了这样的需求:需要在FPGA上实现一个稳定的串口通信模块,用于与上位机进行数据交互。经过多次迭代,最终设计出了一个无奇偶校验、支持多种波特率的通用串口控制器。
这个设计有几个关键特点特别值得分享:首先,它通过了-40℃到85℃的高低温循环测试,证明其可靠性;其次,整个设计采用纯Verilog实现,不依赖特定厂商的IP核,移植性极佳;最后,我们提供了完整的工程文件和测试平台,方便其他开发者直接使用或二次开发。
2. 串口通信协议核心原理
2.1 异步串行通信基础
串口通信本质上是将并行数据转换为串行比特流进行传输。以一个典型的8位数据帧为例(无奇偶校验),其帧结构包括:
- 起始位(1位,逻辑0)
- 数据位(8位,LSB先发)
- 停止位(1位,逻辑1)
这种NRZ(非归零)编码方式的最大特点是依靠精确的波特率时钟来同步数据采样。在设计Verilog实现时,我们需要特别注意三点:
- 波特率时钟生成要准确
- 起始位检测要可靠
- 数据采样点要避开信号跳变沿
提示:实际项目中,我推荐将采样时钟设置为波特率的16倍,这样可以在数据位中间位置采样,有效避开信号边沿。
2.2 关键时序参数计算
假设系统时钟为50MHz,目标波特率为115200bps,那么分频系数计算如下:
code复制分频系数 = 系统时钟频率 / (波特率 × 过采样率)
= 50,000,000 / (115200 × 16)
≈ 27
对应的Verilog代码实现:
verilog复制// 波特率时钟生成
reg [15:0] baud_counter;
always @(posedge clk) begin
if(baud_counter == 16'd26) begin
baud_counter <= 0;
baud_clk_en <= 1;
end else begin
baud_counter <= baud_counter + 1;
baud_clk_en <= 0;
end
end
3. Verilog实现核心模块设计
3.1 顶层模块架构
整个设计采用典型的有限状态机(FSM)架构,主要包含以下子模块:
code复制 +-------------------+
| 波特率发生器 |
+-------------------+
|
+-------------------+
| 接收状态机 |
+-------------------+
|
+-------------------+
| 发送状态机 |
+-------------------+
|
+-------------------+
| 数据缓冲FIFO |
+-------------------+
3.2 接收状态机实现
接收状态机是设计的核心难点,需要处理以下状态转换:
verilog复制localparam [2:0]
IDLE = 3'b000,
START_BIT = 3'b001,
DATA_BITS = 3'b010,
STOP_BIT = 3'b011;
always @(posedge clk) begin
case(rx_state)
IDLE:
if(!rxd) begin // 检测起始位
rx_state <= START_BIT;
bit_count <= 0;
sample_count <= 0;
end
START_BIT:
if(sample_count == 8) begin // 起始位中点采样
rx_state <= DATA_BITS;
sample_count <= 0;
end
DATA_BITS:
if(sample_count == 15) begin
rx_buffer[bit_count] <= rxd;
sample_count <= 0;
if(bit_count == 7) rx_state <= STOP_BIT;
else bit_count <= bit_count + 1;
end
STOP_BIT:
if(sample_count == 15) begin
rx_state <= IDLE;
rx_ready <= 1;
end
endcase
end
3.3 发送模块设计
发送模块相对简单,但需要注意在切换发送方向时的时序控制:
verilog复制always @(posedge clk) begin
if(tx_en & baud_clk_en) begin
case(tx_state)
0: begin // 起始位
txd <= 0;
tx_state <= 1;
bit_count <= 0;
end
1: begin // 数据位
txd <= tx_data[bit_count];
if(bit_count == 7) tx_state <= 2;
else bit_count <= bit_count + 1;
end
2: begin // 停止位
txd <= 1;
tx_state <= 0;
tx_ready <= 1;
end
endcase
end
end
4. 仿真验证与实测结果
4.1 Modelsim仿真平台搭建
我们构建了一个完整的测试平台,包含以下组件:
- 串口控制器DUT(被测单元)
- 虚拟串口发送器
- 虚拟串口接收器
- 随机数据生成器
- 误码率统计模块
测试用例包括:
- 单字节传输测试
- 连续数据流测试
- 波特率容错测试
- 帧间隔压力测试
4.2 关键仿真波形分析
下图展示了完整的收发时序:
code复制 ___ ___ ___ ___ ___
clk | |___| |___| |___| |___| |
_______ ______
rxd ______| |_______________|
_ _ _ _ _ _ _
txd ________| |_| |_| |_| |_| |_| |_| |__
4.3 实测性能数据
经过实际板级测试,我们获得了以下关键数据:
| 测试项目 | 测试条件 | 结果 |
|---|---|---|
| 最高波特率 | 25℃环境温度 | 3Mbps稳定工作 |
| 低温可靠性 | -40℃, 连续工作24h | 零误码 |
| 电源波动适应性 | 3.3V±10% | 通信不受影响 |
| 长期稳定性 | 85℃, 100小时老化 | 性能无衰减 |
5. 工程实践中的经验总结
5.1 常见问题排查指南
在实际项目中,我遇到过几个典型问题及解决方案:
-
起始位误检测:
- 现象:空闲状态下偶尔误触发接收
- 解决方法:增加起始位验证逻辑,要求连续3个采样点都为低才确认
-
波特率偏差导致误码:
- 现象:高波特率下出现随机误码
- 解决方法:使用更精确的时钟源,或改用小数分频技术
-
多设备冲突:
- 现象:总线挂载多个设备时通信异常
- 解决方法:增加三态输出控制和冲突检测机制
5.2 性能优化技巧
通过项目实践,我总结了几个提升性能的关键点:
-
过采样技术:
- 常规16倍过采样可改为3倍过采样+数字滤波
- 节省逻辑资源的同时提高最高工作频率
-
双缓冲设计:
- 接收端采用双缓冲机制
- 允许在读取前一个数据时接收下一个数据
-
动态波特率调整:
verilog复制// 自动检测波特率示例 always @(negedge rxd) begin if(!baud_locked) begin baud_counter <= $time; if(prev_time != 0) measured_baud <= 1/($time - prev_time); prev_time <= $time; end end
6. 完整工程文件结构说明
提供的工程包包含以下内容:
code复制/rtl
uart_core.v - 串口控制器顶层模块
uart_rx.v - 接收模块
uart_tx.v - 发送模块
baud_gen.v - 波特率发生器
fifo.v - 数据缓冲FIFO
/tb
tb_uart.sv - 主测试平台
uart_stimulus.sv - 测试激励生成
uart_monitor.sv - 响应监测
/doc
spec.pdf - 设计规格书
timing.xlsx - 时序分析表
pinout.md - 引脚分配说明
这个设计已经在多个实际项目中得到验证,包括工业传感器网络、智能家居网关等场景。它的优势在于纯HDL实现带来的可移植性,以及经过严苛环境验证的可靠性。对于需要定制串口功能的开发者,可以很方便地基于此设计进行二次开发,比如添加硬件流控、多协议支持等扩展功能。