从嵌入式到IC设计:用Verilog手把手实现一个可配置的UART收发器(附完整代码)
从零构建可配置UART收发器Verilog实战指南在数字系统设计中UART通用异步收发器作为最基础的串行通信接口之一其重要性不言而喻。不同于市面上大多数停留在理论层面的UART教程本文将带领读者深入硬件描述语言的实践领域用Verilog完整实现一个支持多参数配置的UART模块。这个设计不仅包含标准的收发功能还特别注重可配置性——波特率、数据位宽、校验方式等关键参数均可通过寄存器灵活设置真正打通嵌入式软件与硬件设计的桥梁。1. UART核心架构设计1.1 模块整体框图我们的UART设计采用典型的收发分离架构主要包含以下功能单元module uart_core ( input wire clk, input wire rst_n, // 配置接口 input wire [15:0] baud_div, input wire [1:0] data_bits_sel, input wire parity_en, input wire parity_sel, // 数据接口 input wire rx_pin, output wire tx_pin, // 状态指示 output wire rx_ready, output wire tx_busy ); // 接收机实例化 uart_rx receiver ( .clk(clk), .rst_n(rst_n), .baud_div(baud_div), .data_bits_sel(data_bits_sel), .parity_en(parity_en), .parity_sel(parity_sel), .rx_pin(rx_pin), .rx_ready(rx_ready) ); // 发送机实例化 uart_tx transmitter ( .clk(clk), .rst_n(rst_n), .baud_div(baud_div), .data_bits_sel(data_bits_sel), .parity_en(parity_en), .parity_sel(parity_sel), .tx_pin(tx_pin), .tx_busy(tx_busy) ); endmodule1.2 关键参数配置机制为实现灵活的配置能力我们设计了以下寄存器映射方案寄存器位域功能描述可选值baud_div波特率分频系数根据系统时钟频率计算得出data_bits_sel数据位宽选择2b00:5位, 2b01:6位, etcparity_en奇偶校验使能1b0:禁用, 1b1:启用parity_sel校验类型选择1b0:奇校验, 1b1:偶校验提示波特率分频系数计算公式为baud_div (系统时钟频率)/(目标波特率×过采样率) - 12. 接收机设计与实现2.1 异步信号同步化处理由于UART信号是异步输入首先需要进行同步化处理以避免亚稳态// 两级同步器设计 reg rx_sync1, rx_sync2; always (posedge clk or negedge rst_n) begin if (!rst_n) begin rx_sync1 1b1; rx_sync2 1b1; end else begin rx_sync1 rx_pin; rx_sync2 rx_sync1; end end2.2 可配置波特率采样时钟生成接收机采用16倍过采样技术提高抗干扰能力reg [15:0] baud_counter; reg sample_en; always (posedge clk or negedge rst_n) begin if (!rst_n) begin baud_counter 16d0; sample_en 1b0; end else if (state ! IDLE) begin if (baud_counter baud_div) begin baud_counter 16d0; sample_en 1b1; end else begin baud_counter baud_counter 1b1; sample_en 1b0; end end else begin baud_counter 16d0; sample_en 1b0; end end2.3 数据位宽动态处理支持5-8位数据位宽的核心逻辑实现reg [7:0] rx_shift_reg; reg [2:0] bit_count; always (posedge clk or negedge rst_n) begin if (!rst_n) begin rx_shift_reg 8h00; bit_count 3d0; end else if (state DATA_BITS sample_en) begin case (data_bits_sel) 2b00: if (bit_count 3d4) begin rx_shift_reg {rx_sync2, rx_shift_reg[7:1]}; bit_count bit_count 1b1; end 2b01: if (bit_count 3d5) begin // 类似处理6位模式 end // 其他位宽模式... endcase end end3. 发送机设计与实现3.1 发送状态机设计发送机采用经典的状态机架构包含以下状态IDLE等待发送请求START_BIT发送起始位DATA_BITS依次发送数据位PARITY_BIT发送校验位如果使能STOP_BIT发送停止位localparam [2:0] IDLE 3b000, START_BIT 3b001, DATA_BITS 3b010, PARITY_BIT 3b011, STOP_BIT 3b100; reg [2:0] state; reg [7:0] tx_shift_reg; reg [2:0] bit_count; reg tx_parity; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; tx_pin 1b1; end else begin case (state) IDLE: begin if (tx_start) begin state START_BIT; tx_pin 1b0; end end START_BIT: begin if (baud_done) begin state DATA_BITS; tx_pin tx_shift_reg[0]; end end // 其他状态处理... endcase end end3.2 动态校验位生成根据配置动态生成奇校验或偶校验位always (*) begin if (parity_en) begin case (data_bits_sel) 2b00: tx_parity ^tx_data[4:0]; 2b01: tx_parity ^tx_data[5:0]; // 其他位宽模式... endcase if (parity_sel) // 偶校验 tx_parity ~tx_parity; end end4. 验证与调试技巧4.1 自环测试方案为验证UART功能完整性推荐采用自环测试方法将发送端与接收端直接相连发送随机生成的数据包比较发送与接收数据的一致性遍历测试所有配置组合// 测试用例示例 initial begin // 测试9600波特率8位数据无校验 config_uart(16d104, 2b11, 1b0, 1b0); send_data(8hA5); check_received(8hA5); // 测试115200波特率7位数据奇校验 config_uart(16d8, 2b10, 1b1, 1b0); send_data(7h55); check_received(7h55); end4.2 常见问题排查指南在实际调试中可能会遇到以下典型问题数据错位检查波特率配置是否一致时钟精度是否足够偶发错误增加接收端过采样次数优化信号完整性校验失败确认发送端和接收端校验配置一致时序违例添加适当的跨时钟域同步处理注意在FPGA实现时建议使用厂商提供的IO约束工具对UART引脚添加适当的时序约束5. 性能优化进阶5.1 双缓冲设计为提升吞吐量可采用双缓冲机制// 接收双缓冲实现 reg [7:0] rx_buffer[0:1]; reg buffer_sel; always (posedge clk or negedge rst_n) begin if (!rst_n) begin buffer_sel 1b0; end else if (rx_done) begin rx_buffer[buffer_sel] rx_shift_reg; buffer_sel ~buffer_sel; end end5.2 自适应波特率检测高级应用中可实现波特率自动检测功能测量起始位持续时间计算实际波特率动态调整接收参数反馈给发送端同步// 波特率检测核心逻辑 reg [15:0] start_bit_width; always (posedge clk or negedge rst_n) begin if (rx_falling_edge) begin start_bit_width 16d0; end else if (!rx_sync2) begin start_bit_width start_bit_width 1b1; end else begin detected_baud (sys_clk_freq / (start_bit_width * 16)); end end在实际项目中这个UART设计已经成功应用于多个工业通信模块其可配置特性显著减少了不同产品线间的代码维护成本。特别是在需要现场升级通信参数的场景中寄存器配置接口发挥了关键作用。