用 1 个 bit 检测传输错误——奇偶校验的原理与 Verilog 实现
💡 你通过 UART 从传感器读数据,大部分时候数据正常,但偶尔会收到一个离谱的值——明明温度只有 25°C,却读到了 153°C。问题出在哪里?可能是传输过程中某一位数据被噪声翻转了。
怎么知道收到的数据有没有出错?最简单的方法就是奇偶校验(Parity Check)——只需要多传 1 个 bit,就能检测出任意单 bit 的错误。
这篇文章带你搞清楚:奇偶校验是怎么工作的、怎么用 Verilog 实现、以及什么时候该用更强的校验方案。
1. 核心原理:数 1 的个数
奇偶校验的原理简单到只需一句话:发送端和接收端约定好”1 的个数应该是奇数还是偶数”,如果不对就说明数据出错了。
1.1 偶校验(Even Parity)
约定:数据位 + 校验位中,1 的总个数为偶数。
数据:1 0 1 1 0 0 1(7位,其中有 4 个 1,已经是偶数)
偶校验位 = 0
传输:1 0 1 1 0 0 1 [0] ← 总共 4 个 1,偶数 ✓
数据:1 0 1 1 0 1 1(7位,其中有 5 个 1,是奇数)
偶校验位 = 1
传输:1 0 1 1 0 1 1 [1] ← 总共 6 个 1,偶数 ✓
1.2 奇校验(Odd Parity)
约定:数据位 + 校验位中,1 的总个数为奇数。计算方式就是偶校验取反。
💬 你可能会问:奇校验和偶校验该用哪个?
两者的检错能力完全相同,选哪个主要看通信协议的规定。UART 默认常用偶校验,但实际中两者都有使用。有一个小优势:奇校验保证至少有 1 个 1,所以可以检测”全 0 数据”的传输故障(比如线断了)。
2. Verilog 实现:一行代码搞定
奇偶校验的 Verilog 实现非常优雅——异或运算天然就是在”数 1 的个数”。
2.1 偶校验生成器
module even_parity_gen (
input wire [7:0] data_in,
output wire parity_out
);
// 所有位异或:1 的个数为奇数时结果为 1,为偶数时结果为 0
assign parity_out = ^data_in;
endmodule
为什么异或能实现奇偶校验? 因为异或运算的本质就是”模 2 加法”——多个 bit 异或的结果,就是这些 bit 中 1 的个数的奇偶性。1 的个数为奇数 → 结果为 1;1 的个数为偶数 → 结果为 0。
2.2 奇校验生成器
module odd_parity_gen (
input wire [7:0] data_in,
output wire parity_out
);
assign parity_out = ~(^data_in); // 偶校验取反
endmodule
2.3 校验检测器
module parity_checker (
input wire [7:0] data_in,
input wire parity_in,
output wire error // 1 = 检测到错误
);
// 偶校验检测:数据+校验位全部异或,结果为 1 说明 1 的个数变成了奇数 → 出错
assign error = ^{data_in, parity_in};
endmodule
3. 实战:在 UART 发送模块中集成奇偶校验
光看生成器和检测器还不够——下面是一个在 UART 发送中集成偶校验的实际示例:
// UART 发送模块(含偶校验位)
// 帧结构:[起始位(0)] [8位数据] [校验位] [停止位(1)]
module uart_tx_with_parity (
input wire clk,
input wire rst,
input wire tx_start, // 发送触发
input wire [7:0] tx_data, // 待发送数据
output reg tx_out, // UART 输出
output reg tx_busy // 发送忙标志
);
// 计算偶校验位
wire parity_bit = ^tx_data;
// 构造完整的发送帧:起始位 + 8位数据 + 校验位 + 停止位
reg [10:0] tx_frame;
reg [3:0] bit_cnt;
always @(posedge clk) begin
if (rst) begin
tx_out <= 1'b1; // 空闲状态为高电平
tx_busy <= 1'b0;
bit_cnt <= 4'd0;
end else if (tx_start && !tx_busy) begin
// 装载发送帧
tx_frame <= {1'b1, parity_bit, tx_data, 1'b0}; // 停止位 + 校验 + 数据 + 起始位
tx_busy <= 1'b1;
bit_cnt <= 4'd0;
end else if (tx_busy) begin
tx_out <= tx_frame[0]; // LSB first
tx_frame <= {1'b1, tx_frame[10:1]}; // 右移
bit_cnt <= bit_cnt + 1'b1;
if (bit_cnt == 4'd10) // 11位全部发完
tx_busy <= 1'b0;
end
end
endmodule
💡 工程师手记:我在一个工业控制项目中用 UART 和温度传感器通信,一开始没有开启奇偶校验。大部分时候数据完全正常,但工厂现场电磁干扰很强,偶尔会收到明显错误的温度值。后来开启了 UART 的偶校验功能,在接收端检测到校验错误时丢弃该帧并请求重传,问题就解决了。虽然奇偶校验只能检测单 bit 错误,但在大多数低速串口场景中,同时出现 2 bit 错误的概率极低,够用了。
(建议替换为你自己的真实经历,读者会更有共鸣)
4. ★ 奇偶校验够用吗?——FPGA 常用校验方案对比
奇偶校验虽然简单,但它的局限性也很明显。下表帮你决定在不同场景下该用哪种校验方案:
| 方案 | 能检测的错误 | 能纠错吗 | 开销 | 适用场景 |
|---|---|---|---|---|
| 奇偶校验 | 1 bit 错误 | 不能 | 极低(1 bit) | UART、SPI、低速总线 |
| CRC | 多 bit 错误、突发错误 | 不能 | 低(8-32 bit) | 以太网、USB、SD卡、PCIe |
| 汉明码 | 2 bit 错误(检测),1 bit(纠正) | 能纠 1 bit | 中等 | ECC 内存、NAND Flash |
| RS 码 | 多符号错误 | 能纠多符号 | 高 | 光通信、卫星通信 |
选择原则:
- 低速、低误码率场景 → 奇偶校验足够
- 高速、高可靠性场景 → CRC 是最佳平衡
- 需要纠错能力 → 汉明码 / ECC
- 噪声极端恶劣(航天、深空通信)→ RS 码或 LDPC
💬 你可能会问:奇偶校验这么简单,为什么不直接用 CRC?
因为 CRC 的硬件实现需要移位寄存器和多项式除法,虽然也不复杂,但比一行异或运算还是大了不少。在一些资源极其紧张或速度要求极高的场景中(比如 FPGA 内部模块间的快速数据传递),奇偶校验的”几乎零开销”就是它的优势。
5. 总结
| 核心认知 | 内容 |
|---|---|
| 原理 | 通过约定 1 的个数奇偶性,用 1 bit 校验位检测单 bit 错误 |
| Verilog 实现 | 偶校验 = ^data,奇校验 = ~(^data),一行搞定 |
| 局限性 | 只能检测奇数 bit 错误,无法纠错,无法检测偶数 bit 同时出错 |
| 适用场景 | 低速串口通信(UART、SPI)、FPGA 内部低开销校验 |
| 更强方案 | CRC(检多 bit 错)→ 汉明码(能纠错)→ RS 码(纠多符号错) |
系列回顾:
到这里,你已经学完了 FPGA 数字设计的 6 个基础专题:
- 组合逻辑 vs 时序逻辑
- 竞争与冒险
- 同步 vs 异步设计
- 亚稳态与跨时钟域
- 阻塞赋值 vs 非阻塞赋值
- 奇偶校验
这些是 FPGA 设计中最基础、最重要的概念。掌握了它们,你就有了扎实的理论基础,可以开始挑战更复杂的设计了——比如状态机设计、FIFO 架构、信号处理流水线等。
常见问题
💬 如果同时有 2 位出错,奇偶校验能检测到吗?
不能。2 位同时翻转后,1 的个数奇偶性不变,校验位仍然”正确”——这就是奇偶校验最大的盲区。好消息是,在大多数低误码率的通信场景中,同时出现 2 bit 错误的概率极低(概率大约是单 bit 错误概率的平方)。
💬 UART 的奇偶校验位是自动处理的还是需要手动实现?
取决于你的实现方式。如果你使用 FPGA 厂商提供的 UART IP 核,通常可以通过参数配置自动生成和检测校验位。如果你自己写 UART 模块,就需要手动在发送端生成校验位、在接收端检测校验位。
💬 奇偶校验在 FPGA 内部模块间通信中有用吗?
FPGA 内部的数据传输通常不会出现 bit 翻转(没有外部噪声),所以一般不需要校验。但在一些高可靠性场景中(如航天 FPGA),会对 BRAM 的存储内容加 ECC 保护,防止单粒子翻转(SEU)导致的数据错误。
参考资料
- Xilinx/AMD, UG953: Vivado Design Suite 7 Series FPGA and Zynq-7000 SoC Libraries Guide(XPM_MEMORY ECC 配置)
- IEEE 802.3: Ethernet Standard(CRC-32 校验)
- Richard Hamming, Error Detecting and Error Correcting Codes, Bell System Technical Journal, 1950
系列导航:本文是「FPGA 入门系列」第 17 篇。
- 上一篇:= vs <=:一个字符的差别,决定你的电路对不对
- 下一篇:敬请期待
如果这篇文章对你有帮助,欢迎点赞、收藏,也欢迎在评论区聊聊你在项目中用过的错误检测方案——奇偶校验、CRC 还是 ECC?