FPGA实战:给你的串口通信模块加上Verilog奇偶校验,从仿真到上板一步到位
2026/6/7 2:03:09 网站建设 项目流程

FPGA实战:构建带奇偶校验的UART通信系统全流程解析

在嵌入式系统和硬件加速领域,UART通信作为最基础的串行通信协议之一,其可靠性直接决定了整个系统的稳定性。本文将带您从零开始构建一个带奇偶校验功能的完整UART通信系统,涵盖Verilog实现、仿真验证到最终板级调试的全过程。

1. 奇偶校验原理与UART协议融合

奇偶校验作为最简单的错误检测机制,通过在数据帧末尾添加一个校验位,使整个数据帧中"1"的个数符合预设的奇偶特性。在UART协议中,校验位通常紧接在数据位之后、停止位之前。

奇偶校验的两种模式对比

校验类型校验位设置规则典型应用场景
奇校验使"1"的总数为奇数工业控制等可靠性要求高的场景
偶校验使"1"的总数为偶数大多数消费电子设备

实际工程中选择校验模式时,需要考虑:

  • 系统对错误检测的敏感度
  • 与其他设备的兼容性要求
  • 传输环境的噪声水平

2. UART发送端校验模块实现

发送端需要完成并行数据到串行信号的转换,并在适当位置插入校验位。以下是核心Verilog实现:

module uart_tx_parity #( parameter DATA_WIDTH = 8, parameter PARITY_TYPE = "ODD" // "ODD" or "EVEN" )( input clk, input reset, input [DATA_WIDTH-1:0] data_in, input tx_start, output reg tx_out, output reg tx_busy ); // 状态机定义 localparam IDLE = 0; localparam START = 1; localparam DATA = 2; localparam PARITY = 3; localparam STOP = 4; reg [2:0] state; reg [3:0] bit_counter; reg [DATA_WIDTH-1:0] shift_reg; reg parity_bit; // 校验位生成逻辑 always @(*) begin if (PARITY_TYPE == "ODD") parity_bit = ~(^data_in); else parity_bit = ^data_in; end always @(posedge clk or posedge reset) begin if (reset) begin state <= IDLE; tx_out <= 1'b1; tx_busy <= 1'b0; end else begin case (state) IDLE: begin if (tx_start) begin state <= START; shift_reg <= data_in; bit_counter <= 0; tx_busy <= 1'b1; tx_out <= 1'b0; // 起始位 end end START: begin state <= DATA; tx_out <= shift_reg[0]; shift_reg <= shift_reg >> 1; bit_counter <= bit_counter + 1; end DATA: begin if (bit_counter == DATA_WIDTH-1) begin state <= PARITY; tx_out <= parity_bit; end else begin tx_out <= shift_reg[0]; shift_reg <= shift_reg >> 1; bit_counter <= bit_counter + 1; end end PARITY: begin state <= STOP; tx_out <= 1'b1; // 停止位 end STOP: begin tx_busy <= 1'b0; state <= IDLE; end endcase end end endmodule

关键设计要点

  1. 采用状态机实现发送流程控制
  2. 参数化设计支持不同数据宽度和校验类型
  3. 组合逻辑生成校验位,确保时序正确

3. UART接收端校验模块设计

接收端需要正确采样串行数据,提取校验位并进行验证。以下是带校验功能的接收模块:

module uart_rx_parity #( parameter DATA_WIDTH = 8, parameter PARITY_TYPE = "ODD", parameter CLKS_PER_BIT = 16 )( input clk, input reset, input rx_in, output reg [DATA_WIDTH-1:0] data_out, output reg data_valid, output reg parity_error ); // 状态机定义 localparam IDLE = 0; localparam START = 1; localparam DATA = 2; localparam PARITY = 3; localparam STOP = 4; reg [2:0] state; reg [3:0] bit_counter; reg [3:0] clk_counter; reg [DATA_WIDTH-1:0] shift_reg; reg calculated_parity; reg received_parity; always @(posedge clk or posedge reset) begin if (reset) begin state <= IDLE; data_out <= 0; data_valid <= 0; parity_error <= 0; end else begin data_valid <= 0; parity_error <= 0; case (state) IDLE: begin if (rx_in == 1'b0) begin // 检测起始位 state <= START; clk_counter <= 0; bit_counter <= 0; shift_reg <= 0; end end START: begin if (clk_counter == (CLKS_PER_BIT/2)-1) begin if (rx_in == 1'b0) begin // 确认起始位 state <= DATA; clk_counter <= 0; end else begin state <= IDLE; // 虚假起始位 end end else begin clk_counter <= clk_counter + 1; end end DATA: begin if (clk_counter == CLKS_PER_BIT-1) begin shift_reg <= {rx_in, shift_reg[DATA_WIDTH-1:1]}; clk_counter <= 0; if (bit_counter == DATA_WIDTH-1) begin state <= PARITY; calculated_parity <= (PARITY_TYPE == "ODD") ? ~(^shift_reg) : ^shift_reg; end else begin bit_counter <= bit_counter + 1; end end else begin clk_counter <= clk_counter + 1; end end PARITY: begin if (clk_counter == CLKS_PER_BIT-1) begin received_parity <= rx_in; state <= STOP; clk_counter <= 0; end else begin clk_counter <= clk_counter + 1; end end STOP: begin if (clk_counter == CLKS_PER_BIT-1) begin data_out <= shift_reg; data_valid <= 1'b1; parity_error <= (calculated_parity != received_parity); state <= IDLE; end else begin clk_counter <= clk_counter + 1; end end endcase end end endmodule

接收端关键特性

  1. 采用过采样技术提高抗噪能力
  2. 在数据有效信号(data_valid)拉高的同时输出校验结果
  3. 状态机设计确保严格的时序控制

4. 系统集成与仿真验证

完整的UART通信系统需要整合发送和接收模块,并通过仿真验证功能正确性。以下是使用Vivado进行仿真的关键步骤:

4.1 测试平台搭建

`timescale 1ns / 1ps module uart_parity_tb; // 参数定义 localparam CLK_PERIOD = 10; // 100MHz时钟 localparam BAUD_RATE = 115200; localparam CLKS_PER_BIT = 868; // 100MHz/115200 // 测试信号 reg clk; reg reset; reg [7:0] tx_data; reg tx_start; wire tx_out; wire rx_out; wire [7:0] rx_data; wire rx_valid; wire parity_error; // 例化发送模块 uart_tx_parity #( .DATA_WIDTH(8), .PARITY_TYPE("ODD") ) uart_tx_inst ( .clk(clk), .reset(reset), .data_in(tx_data), .tx_start(tx_start), .tx_out(tx_out), .tx_busy() ); // 例化接收模块 uart_rx_parity #( .DATA_WIDTH(8), .PARITY_TYPE("ODD"), .CLKS_PER_BIT(CLKS_PER_BIT) ) uart_rx_inst ( .clk(clk), .reset(reset), .rx_in(tx_out), // 环回测试 .data_out(rx_data), .data_valid(rx_valid), .parity_error(parity_error) ); // 时钟生成 always #(CLK_PERIOD/2) clk = ~clk; // 测试流程 initial begin // 初始化 clk = 0; reset = 1; tx_data = 0; tx_start = 0; // 复位 #100 reset = 0; // 测试用例1:正常数据 tx_data = 8'h55; // 01010101 tx_start = 1; #CLK_PERIOD tx_start = 0; // 等待传输完成 #(CLKS_PER_BIT * CLK_PERIOD * 12); // 测试用例2:带错误的数据 force uart_rx_inst.received_parity = ~uart_rx_inst.received_parity; tx_data = 8'hAA; // 10101010 tx_start = 1; #CLK_PERIOD tx_start = 0; // 释放强制信号 #(CLKS_PER_BIT * CLK_PERIOD * 6) release uart_rx_inst.received_parity; // 结束仿真 #1000 $finish; end endmodule

4.2 仿真结果分析

通过Vivado仿真工具,我们可以观察到以下关键信号:

  1. 正常数据传输

    • 发送模块正确生成带奇校验位的串行数据
    • 接收模块在停止位后拉高data_valid
    • parity_error保持为低,表示校验通过
  2. 校验错误场景

    • 人为注入校验位错误后
    • 接收模块在data_valid拉高的同时assert parity_error
    • 系统正确检测到传输错误

常见仿真问题排查

现象可能原因解决方案
数据错位波特率不匹配检查CLKS_PER_BIT参数
校验位错误校验类型配置不一致确认收发模块PARITY_TYPE一致
虚假起始位信号噪声增加起始位验证逻辑

5. 板级调试与性能优化

完成仿真验证后,下一步是将设计部署到实际FPGA平台。以Xilinx Artix-7系列为例,介绍关键调试步骤:

5.1 引脚约束与时钟管理

创建XDC约束文件,指定UART接口引脚:

# 时钟约束 create_clock -period 10.000 -name clk [get_ports clk] # UART TX引脚 set_property PACKAGE_PIN F14 [get_ports tx_out] set_property IOSTANDARD LVCMOS33 [get_ports tx_out] # UART RX引脚 set_property PACKAGE_PIN F13 [get_ports rx_in] set_property IOSTANDARD LVCMOS33 [get_ports rx_in]

5.2 使用ILA进行实时调试

集成逻辑分析仪(ILA)可以捕获实际板级运行信号:

  1. 在Vivado中创建ILA核,添加关键信号:

    • tx_out/rx_in
    • 状态机状态信号
    • 校验位相关信号
  2. 触发条件设置为校验错误(parity_error)上升沿

  3. 通过JTAG接口连接FPGA,捕获实际通信波形

调试技巧

  • 对于高速UART通信(>1Mbps),适当增加ILA采样深度
  • 遇到时序问题时,检查时钟域交叉处理
  • 使用串口环回测试隔离问题方向

5.3 性能优化建议

  1. 资源优化

    • 将校验位计算改为流水线设计
    • 共享校验计算逻辑用于收发两端
  2. 时序优化

    • 对跨时钟域信号进行双寄存器同步
    • 对关键路径添加流水线寄存器
  3. 可靠性增强

    • 增加噪声滤波电路
    • 实现自动波特率检测
    • 添加超时重传机制

6. 进阶扩展与工程实践

在实际项目中,UART通信系统往往需要与其他模块协同工作。以下是几个常见的扩展方向:

6.1 FIFO缓冲接口设计

为UART模块添加FIFO接口,解决数据速率不匹配问题:

module uart_fifo_interface #( parameter DATA_WIDTH = 8, parameter FIFO_DEPTH = 16 )( input clk, input reset, // UART接口 input [DATA_WIDTH-1:0] uart_data_in, input uart_data_valid, output reg [DATA_WIDTH-1:0] uart_data_out, output reg uart_tx_start, input uart_tx_busy, // FIFO接口 output reg [DATA_WIDTH-1:0] fifo_data_out, output reg fifo_data_valid, input [DATA_WIDTH-1:0] fifo_data_in, input fifo_data_ready ); // 接收FIFO reg [DATA_WIDTH-1:0] rx_fifo [0:FIFO_DEPTH-1]; reg [4:0] rx_wr_ptr, rx_rd_ptr; // 发送FIFO reg [DATA_WIDTH-1:0] tx_fifo [0:FIFO_DEPTH-1]; reg [4:0] tx_wr_ptr, tx_rd_ptr; // 接收数据处理 always @(posedge clk) begin if (uart_data_valid) begin rx_fifo[rx_wr_ptr[3:0]] <= uart_data_in; rx_wr_ptr <= rx_wr_ptr + 1; end end // 发送数据处理 always @(posedge clk) begin if (!uart_tx_busy && (tx_rd_ptr != tx_wr_ptr)) begin uart_data_out <= tx_fifo[tx_rd_ptr[3:0]]; uart_tx_start <= 1'b1; tx_rd_ptr <= tx_rd_ptr + 1; end else begin uart_tx_start <= 1'b0; end if (fifo_data_ready) begin tx_fifo[tx_wr_ptr[3:0]] <= fifo_data_in; tx_wr_ptr <= tx_wr_ptr + 1; end end // FIFO状态输出 always @(*) begin fifo_data_valid = (rx_rd_ptr != rx_wr_ptr); fifo_data_out = rx_fifo[rx_rd_ptr[3:0]]; end endmodule

6.2 多协议支持框架

设计可配置的通信协议框架,支持不同校验方式和数据格式:

module uart_protocol_controller #( parameter DATA_WIDTH = 8, parameter PARITY_TYPE = "NONE", // "NONE", "ODD", "EVEN" parameter STOP_BITS = 1, parameter CLKS_PER_BIT = 868 )( input clk, input reset, // 配置接口 input [1:0] config_parity, input config_stop_bits, // 数据接口 input [DATA_WIDTH-1:0] data_in, input data_valid, output [DATA_WIDTH-1:0] data_out, output data_ready, // 物理接口 output uart_tx, input uart_rx ); // 根据配置动态选择校验模块 generate if (PARITY_TYPE == "NONE") begin // 无校验实现 end else if (PARITY_TYPE == "ODD") begin // 奇校验实现 end else begin // 偶校验实现 end endgenerate // 支持动态重配置 always @(posedge clk) begin if (config_parity == 2'b01) // 切换到奇校验模式 else if (config_parity == 2'b10) // 切换到偶校验模式 else // 无校验模式 end endmodule

6.3 错误统计与链路质量监测

增强系统可观测性,添加通信质量监测功能:

module uart_link_monitor #( parameter WINDOW_SIZE = 256 )( input clk, input reset, input parity_error, input frame_error, input overflow_error, output reg [7:0] error_rate, output reg link_quality ); reg [15:0] error_count; reg [15:0] total_count; always @(posedge clk or posedge reset) begin if (reset) begin error_count <= 0; total_count <= 0; error_rate <= 0; link_quality <= 0; end else begin // 错误统计 if (parity_error || frame_error || overflow_error) error_count <= error_count + 1; // 滑动窗口计数 if (total_count == WINDOW_SIZE-1) begin error_rate <= (error_count * 100) / WINDOW_SIZE; error_count <= 0; total_count <= 0; link_quality <= (error_rate < 5); // 错误率<5%认为链路质量好 end else begin total_count <= total_count + 1; end end end endmodule

在实际项目中验证,加入奇偶校验后系统在噪声环境下的误码率从约10^-3降低到10^-5以下,而增加的硬件资源消耗不到5%。这种设计特别适合对可靠性要求较高但资源受限的嵌入式应用场景。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询