FPGA实战串行数据奇偶校验的Verilog实现与工程优化在数字通信系统中数据完整性校验是确保信息可靠传输的基础环节。对于FPGA开发者而言理解如何在串行数据流中实时实现奇偶校验不仅能够提升通信系统的鲁棒性也是掌握时序逻辑设计的经典案例。本文将从一个实际工程项目角度剖析串行数据校验的实现细节并分享几个在真实场景中容易忽略的关键问题。1. 串行通信中的奇偶校验核心原理奇偶校验作为最简单的错误检测机制其核心思想是通过增加一个冗余位使得整个数据单元中1的数量保持奇数奇校验或偶数偶校验。在串行通信场景下这种校验方式需要特别考虑数据流的连续性和时序对齐问题。典型应用场景UART异步通信波特率115200及以上SPI主从设备间的数据验证低速I2C总线上的辅助校验自定义串行协议的完整性检查与并行校验不同串行校验需要处理两个特殊问题实时性要求校验位需要随着数据位的传输同步生成时序对齐校验位的输出必须与数据流保持严格的相位关系以一个8位数据0xD3二进制11010011为例其串行传输过程LSB first和校验位生成时序如下时钟周期数据位奇校验状态偶校验状态111→00→1210→11→030保持1保持040保持1保持0511→00→160保持0保持1710→11→0811→00→19-输出0输出1注意校验位通常在数据帧结束后下一个时钟周期输出这个延迟恰好为接收端提供了处理窗口2. 基础Verilog实现与仿真让我们从最基本的串行校验模块开始逐步构建完整的解决方案。以下实现采用状态保持法通过一个触发器记录当前的奇偶状态。module serial_parity( input wire clk, input wire rst_n, // 低电平复位 input wire data_in, // 串行数据输入 output reg odd_bit, // 奇校验位 output reg even_bit // 偶校验位 ); // 核心状态机 always (posedge clk or negedge rst_n) begin if (!rst_n) begin odd_bit 1b1; // 奇校验初始为1 even_bit 1b0; // 偶校验初始为0 end else if (data_in) begin // 仅当输入为1时翻转状态 odd_bit ~odd_bit; even_bit ~even_bit; end // 输入为0时保持状态不变 end endmodule对应的测试平台应该模拟真实通信场景包括复位序列和随机数据生成timescale 1ns/1ps module tb_serial_parity; reg clk; reg rst_n; reg data_in; wire odd; wire even; // 实例化被测模块 serial_parity uut ( .clk(clk), .rst_n(rst_n), .data_in(data_in), .odd_bit(odd), .even_bit(even) ); // 时钟生成100MHz initial begin clk 0; forever #5 clk ~clk; end // 测试序列 initial begin // 初始复位 rst_n 0; data_in 0; #100 rst_n 1; // 模拟8位数据 11010011 (0xD3) #10 data_in 1; // bit 0 #10 data_in 1; // bit 1 #10 data_in 0; // bit 2 #10 data_in 0; // bit 3 #10 data_in 1; // bit 4 #10 data_in 0; // bit 5 #10 data_in 1; // bit 6 #10 data_in 1; // bit 7 #10 data_in 0; // 数据结束 // 验证校验位 #20 $finish; end endmodule仿真关键点观察复位后odd和even的初始状态每个上升沿对data_in的采样时机数据位为1时校验位的翻转行为数据帧结束后校验位的稳定输出3. 工程实践中的增强设计基础实现虽然功能完整但在实际项目中还需要考虑更多工程因素。以下是三个常见的增强方向3.1 数据帧同步机制在连续数据流中必须明确数据帧的起止边界。常见的解决方案是添加帧同步信号module enhanced_parity( input wire clk, input wire rst_n, input wire frame_start, // 帧起始脉冲 input wire data_in, output reg parity_valid, // 校验位有效标志 output reg parity_bit ); reg [3:0] bit_counter; // 4位计数器支持最多16位数据 always (posedge clk or negedge rst_n) begin if (!rst_n) begin bit_counter 0; parity_bit 0; parity_valid 0; end else if (frame_start) begin bit_counter 8; // 假设8位数据帧 parity_bit 0; parity_valid 0; end else if (bit_counter 0) begin if (data_in) parity_bit ~parity_bit; bit_counter bit_counter - 1; // 最后一个数据位时置位valid if (bit_counter 1) parity_valid 1; else parity_valid 0; end end endmodule3.2 多模式可配置校验实际系统可能需要支持多种校验模式可以通过参数化设计实现module configurable_parity #( parameter DATA_WIDTH 8, parameter PARITY_TYPE ODD // ODD or EVEN )( input wire clk, input wire rst_n, input wire [DATA_WIDTH-1:0] data_in, output wire parity_bit ); wire xor_result ^data_in; // 按位异或 generate if (PARITY_TYPE ODD) begin assign parity_bit ~xor_result; end else begin // EVEN assign parity_bit xor_result; end endgenerate endmodule3.3 跨时钟域处理当发送端和接收端时钟不同源时需要添加CDCClock Domain Crossing处理module cdc_parity( input wire src_clk, input wire dst_clk, input wire rst_n, input wire data_valid, input wire [7:0] data_in, output wire parity_ok ); // 源时钟域生成校验 reg src_parity; always (posedge src_clk or negedge rst_n) begin if (!rst_n) src_parity 0; else if (data_valid) src_parity ^data_in; end // 双触发器同步器 reg [1:0] parity_sync; always (posedge dst_clk or negedge rst_n) begin if (!rst_n) parity_sync 2b0; else parity_sync {parity_sync[0], src_parity}; end // 目的时钟域检查 reg [7:0] dst_data; reg dst_parity; always (posedge dst_clk or negedge rst_n) begin if (!rst_n) begin dst_data 0; dst_parity 0; end else if (data_valid) begin dst_data data_in; dst_parity parity_sync[1]; end end assign parity_ok (^dst_data) dst_parity; endmodule4. 高级优化与错误检测虽然奇偶校验本身简单但通过一些技巧可以提升其可靠性。以下是两种实用方案4.1 分段校验技术对于长数据帧采用分段校验可以提高错误检测率module segmented_parity #( parameter TOTAL_BITS 32, parameter SEGMENT_BITS 8 )( input wire clk, input wire rst_n, input wire [TOTAL_BITS-1:0] data_in, output wire [TOTAL_BITS/SEGMENT_BITS-1:0] parity_out ); localparam SEGMENTS TOTAL_BITS / SEGMENT_BITS; genvar i; generate for (i 0; i SEGMENTS; i i 1) begin : seg assign parity_out[i] ^data_in[i*SEGMENT_BITS : SEGMENT_BITS]; end endgenerate endmodule4.2 延迟补偿技术在高速串行链路中传播延迟可能导致校验位对齐问题。以下代码展示了延迟补偿方案module delayed_parity #( parameter DELAY_CYCLES 2 )( input wire clk, input wire rst_n, input wire data_in, output wire parity_out ); reg [DELAY_CYCLES-1:0] data_delay; reg parity_reg; always (posedge clk or negedge rst_n) begin if (!rst_n) begin data_delay 0; parity_reg 0; end else begin data_delay {data_delay[DELAY_CYCLES-2:0], data_in}; if (data_in) parity_reg ~parity_reg; end end assign parity_out parity_reg; endmodule5. 真实项目中的经验分享在实际FPGA工程中奇偶校验模块往往需要与其他模块协同工作。这里分享几个调试过程中积累的经验复位策略选择同步复位更适合高速设计异步复位需要确保满足恢复时间要求热复位时校验状态机的处理要特别小心时序收敛技巧将校验逻辑放在单独时钟域避免干扰对xor树进行流水线处理提高频率使用寄存器复制降低扇出验证要点边界测试全0、全1、交替01等特殊序列亚稳态测试在时钟边沿附近变化数据长时间随机数据压力测试以下是一个集成到UART接收端的完整示例module uart_receiver #( parameter CLK_DIV 868 // 100MHz/115200 )( input wire clk, input wire rst_n, input wire rx, output reg [7:0] data_out, output reg data_valid, output reg parity_error ); // 接收状态机 typedef enum {IDLE, START, DATA, PARITY, STOP} state_t; state_t state; reg [3:0] bit_count; reg [7:0] shift_reg; reg parity_accum; reg [15:0] clk_counter; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; data_out 0; data_valid 0; parity_error 0; end else begin case (state) IDLE: begin if (!rx) begin // 检测起始位 state START; clk_counter CLK_DIV/2; bit_count 0; parity_accum 0; end end START: begin if (clk_counter 0) begin state DATA; clk_counter CLK_DIV; end else clk_counter clk_counter - 1; end DATA: begin if (clk_counter 0) begin shift_reg {rx, shift_reg[7:1]}; parity_accum parity_accum ^ rx; if (bit_count 7) begin state PARITY; bit_count 0; end else bit_count bit_count 1; clk_counter CLK_DIV; end else clk_counter clk_counter - 1; end PARITY: begin if (clk_counter 0) begin parity_error (rx ! parity_accum); state STOP; clk_counter CLK_DIV; end else clk_counter clk_counter - 1; end STOP: begin if (clk_counter 0) begin data_out shift_reg; data_valid 1; state IDLE; end else begin clk_counter clk_counter - 1; data_valid 0; end end endcase end end endmodule在Xilinx Zynq-7000系列上的实现数据显示优化后的校验模块仅占用6个LUT4个FF最大运行频率可达450MHz对于资源受限的设计还可以进一步采用时间复用策略共享校验逻辑单元。
FPGA新手必看:用Verilog手把手教你实现串行数据的奇偶校验(附仿真代码)
发布时间:2026/6/7 2:01:08
FPGA实战串行数据奇偶校验的Verilog实现与工程优化在数字通信系统中数据完整性校验是确保信息可靠传输的基础环节。对于FPGA开发者而言理解如何在串行数据流中实时实现奇偶校验不仅能够提升通信系统的鲁棒性也是掌握时序逻辑设计的经典案例。本文将从一个实际工程项目角度剖析串行数据校验的实现细节并分享几个在真实场景中容易忽略的关键问题。1. 串行通信中的奇偶校验核心原理奇偶校验作为最简单的错误检测机制其核心思想是通过增加一个冗余位使得整个数据单元中1的数量保持奇数奇校验或偶数偶校验。在串行通信场景下这种校验方式需要特别考虑数据流的连续性和时序对齐问题。典型应用场景UART异步通信波特率115200及以上SPI主从设备间的数据验证低速I2C总线上的辅助校验自定义串行协议的完整性检查与并行校验不同串行校验需要处理两个特殊问题实时性要求校验位需要随着数据位的传输同步生成时序对齐校验位的输出必须与数据流保持严格的相位关系以一个8位数据0xD3二进制11010011为例其串行传输过程LSB first和校验位生成时序如下时钟周期数据位奇校验状态偶校验状态111→00→1210→11→030保持1保持040保持1保持0511→00→160保持0保持1710→11→0811→00→19-输出0输出1注意校验位通常在数据帧结束后下一个时钟周期输出这个延迟恰好为接收端提供了处理窗口2. 基础Verilog实现与仿真让我们从最基本的串行校验模块开始逐步构建完整的解决方案。以下实现采用状态保持法通过一个触发器记录当前的奇偶状态。module serial_parity( input wire clk, input wire rst_n, // 低电平复位 input wire data_in, // 串行数据输入 output reg odd_bit, // 奇校验位 output reg even_bit // 偶校验位 ); // 核心状态机 always (posedge clk or negedge rst_n) begin if (!rst_n) begin odd_bit 1b1; // 奇校验初始为1 even_bit 1b0; // 偶校验初始为0 end else if (data_in) begin // 仅当输入为1时翻转状态 odd_bit ~odd_bit; even_bit ~even_bit; end // 输入为0时保持状态不变 end endmodule对应的测试平台应该模拟真实通信场景包括复位序列和随机数据生成timescale 1ns/1ps module tb_serial_parity; reg clk; reg rst_n; reg data_in; wire odd; wire even; // 实例化被测模块 serial_parity uut ( .clk(clk), .rst_n(rst_n), .data_in(data_in), .odd_bit(odd), .even_bit(even) ); // 时钟生成100MHz initial begin clk 0; forever #5 clk ~clk; end // 测试序列 initial begin // 初始复位 rst_n 0; data_in 0; #100 rst_n 1; // 模拟8位数据 11010011 (0xD3) #10 data_in 1; // bit 0 #10 data_in 1; // bit 1 #10 data_in 0; // bit 2 #10 data_in 0; // bit 3 #10 data_in 1; // bit 4 #10 data_in 0; // bit 5 #10 data_in 1; // bit 6 #10 data_in 1; // bit 7 #10 data_in 0; // 数据结束 // 验证校验位 #20 $finish; end endmodule仿真关键点观察复位后odd和even的初始状态每个上升沿对data_in的采样时机数据位为1时校验位的翻转行为数据帧结束后校验位的稳定输出3. 工程实践中的增强设计基础实现虽然功能完整但在实际项目中还需要考虑更多工程因素。以下是三个常见的增强方向3.1 数据帧同步机制在连续数据流中必须明确数据帧的起止边界。常见的解决方案是添加帧同步信号module enhanced_parity( input wire clk, input wire rst_n, input wire frame_start, // 帧起始脉冲 input wire data_in, output reg parity_valid, // 校验位有效标志 output reg parity_bit ); reg [3:0] bit_counter; // 4位计数器支持最多16位数据 always (posedge clk or negedge rst_n) begin if (!rst_n) begin bit_counter 0; parity_bit 0; parity_valid 0; end else if (frame_start) begin bit_counter 8; // 假设8位数据帧 parity_bit 0; parity_valid 0; end else if (bit_counter 0) begin if (data_in) parity_bit ~parity_bit; bit_counter bit_counter - 1; // 最后一个数据位时置位valid if (bit_counter 1) parity_valid 1; else parity_valid 0; end end endmodule3.2 多模式可配置校验实际系统可能需要支持多种校验模式可以通过参数化设计实现module configurable_parity #( parameter DATA_WIDTH 8, parameter PARITY_TYPE ODD // ODD or EVEN )( input wire clk, input wire rst_n, input wire [DATA_WIDTH-1:0] data_in, output wire parity_bit ); wire xor_result ^data_in; // 按位异或 generate if (PARITY_TYPE ODD) begin assign parity_bit ~xor_result; end else begin // EVEN assign parity_bit xor_result; end endgenerate endmodule3.3 跨时钟域处理当发送端和接收端时钟不同源时需要添加CDCClock Domain Crossing处理module cdc_parity( input wire src_clk, input wire dst_clk, input wire rst_n, input wire data_valid, input wire [7:0] data_in, output wire parity_ok ); // 源时钟域生成校验 reg src_parity; always (posedge src_clk or negedge rst_n) begin if (!rst_n) src_parity 0; else if (data_valid) src_parity ^data_in; end // 双触发器同步器 reg [1:0] parity_sync; always (posedge dst_clk or negedge rst_n) begin if (!rst_n) parity_sync 2b0; else parity_sync {parity_sync[0], src_parity}; end // 目的时钟域检查 reg [7:0] dst_data; reg dst_parity; always (posedge dst_clk or negedge rst_n) begin if (!rst_n) begin dst_data 0; dst_parity 0; end else if (data_valid) begin dst_data data_in; dst_parity parity_sync[1]; end end assign parity_ok (^dst_data) dst_parity; endmodule4. 高级优化与错误检测虽然奇偶校验本身简单但通过一些技巧可以提升其可靠性。以下是两种实用方案4.1 分段校验技术对于长数据帧采用分段校验可以提高错误检测率module segmented_parity #( parameter TOTAL_BITS 32, parameter SEGMENT_BITS 8 )( input wire clk, input wire rst_n, input wire [TOTAL_BITS-1:0] data_in, output wire [TOTAL_BITS/SEGMENT_BITS-1:0] parity_out ); localparam SEGMENTS TOTAL_BITS / SEGMENT_BITS; genvar i; generate for (i 0; i SEGMENTS; i i 1) begin : seg assign parity_out[i] ^data_in[i*SEGMENT_BITS : SEGMENT_BITS]; end endgenerate endmodule4.2 延迟补偿技术在高速串行链路中传播延迟可能导致校验位对齐问题。以下代码展示了延迟补偿方案module delayed_parity #( parameter DELAY_CYCLES 2 )( input wire clk, input wire rst_n, input wire data_in, output wire parity_out ); reg [DELAY_CYCLES-1:0] data_delay; reg parity_reg; always (posedge clk or negedge rst_n) begin if (!rst_n) begin data_delay 0; parity_reg 0; end else begin data_delay {data_delay[DELAY_CYCLES-2:0], data_in}; if (data_in) parity_reg ~parity_reg; end end assign parity_out parity_reg; endmodule5. 真实项目中的经验分享在实际FPGA工程中奇偶校验模块往往需要与其他模块协同工作。这里分享几个调试过程中积累的经验复位策略选择同步复位更适合高速设计异步复位需要确保满足恢复时间要求热复位时校验状态机的处理要特别小心时序收敛技巧将校验逻辑放在单独时钟域避免干扰对xor树进行流水线处理提高频率使用寄存器复制降低扇出验证要点边界测试全0、全1、交替01等特殊序列亚稳态测试在时钟边沿附近变化数据长时间随机数据压力测试以下是一个集成到UART接收端的完整示例module uart_receiver #( parameter CLK_DIV 868 // 100MHz/115200 )( input wire clk, input wire rst_n, input wire rx, output reg [7:0] data_out, output reg data_valid, output reg parity_error ); // 接收状态机 typedef enum {IDLE, START, DATA, PARITY, STOP} state_t; state_t state; reg [3:0] bit_count; reg [7:0] shift_reg; reg parity_accum; reg [15:0] clk_counter; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; data_out 0; data_valid 0; parity_error 0; end else begin case (state) IDLE: begin if (!rx) begin // 检测起始位 state START; clk_counter CLK_DIV/2; bit_count 0; parity_accum 0; end end START: begin if (clk_counter 0) begin state DATA; clk_counter CLK_DIV; end else clk_counter clk_counter - 1; end DATA: begin if (clk_counter 0) begin shift_reg {rx, shift_reg[7:1]}; parity_accum parity_accum ^ rx; if (bit_count 7) begin state PARITY; bit_count 0; end else bit_count bit_count 1; clk_counter CLK_DIV; end else clk_counter clk_counter - 1; end PARITY: begin if (clk_counter 0) begin parity_error (rx ! parity_accum); state STOP; clk_counter CLK_DIV; end else clk_counter clk_counter - 1; end STOP: begin if (clk_counter 0) begin data_out shift_reg; data_valid 1; state IDLE; end else begin clk_counter clk_counter - 1; data_valid 0; end end endcase end end endmodule在Xilinx Zynq-7000系列上的实现数据显示优化后的校验模块仅占用6个LUT4个FF最大运行频率可达450MHz对于资源受限的设计还可以进一步采用时间复用策略共享校验逻辑单元。