基于 FPGA 的 CRC 校验算法实现
在数字通信和数据存储领域,CRC(循环冗余校验)是一种常用的错误检测方法。它通过对数据进行特定的运算生成校验码,接收端再用同样的算法对数据和校验码进行验证,以此判断数据在传输或存储过程中是否发生错误。本文将介绍基于 FPGA 实现 CRC 校验算法,支持 CRC16、CRC32 以及 CRC8 等多种模式。
CRC 算法原理简介
CRC 算法本质上是一种模 2 运算下的除法操作。发送端将待传输的数据视为一个二进制多项式,再选取一个特定的生成多项式(比如 CRC16 常用的生成多项式为 $x^{16} + x^{15} + x^{2} + 1$,CRC32 常用的生成多项式为 $x^{32} + x^{26} + x^{23} + x^{22} + x^{16} + x^{12} + x^{11} + x^{10} + x^{8} + x^{7} + x^{5} + x^{4} + x^{2} + x + 1$)。用数据多项式除以生成多项式,得到的余数就是 CRC 校验码。接收端用接收到的数据(包含校验码)再次除以相同的生成多项式,如果余数为 0,则认为数据传输正确。
FPGA 实现思路
在 FPGA 中实现 CRC 校验算法,我们可以采用移位寄存器的方式。以 CRC8 为例,其生成多项式为 $x^{8} + x^{5} + x^{4} + 1$,对应的二进制表示为 100110001。我们可以用 8 位的移位寄存器来存储当前的 CRC 值。
CRC8 代码示例(Verilog)
module crc8 ( input wire clk, input wire rst, input wire [7:0] data_in, input wire data_valid, output reg [7:0] crc_out );
reg [7:0] crc_reg;
always @(posedge clk or posedge rst) begin
if (rst) begin
crc_reg <= 8'h00;
end else if (data_valid) begin
crc_reg = crc_reg ^ data_in;
for (int i = 0; i < 8; i = i + 1) begin
if (crc_reg[7]) begin
crc_reg = {crc_reg[6:0], 1'b0} ^ 8'b100110001;
end else begin
crc_reg = {crc_reg[6:0], 1'b0};
end
end
end
end
always @(*) begin
crc_out = crc_reg;
end
endmodule
CRC8 代码分析
- 模块定义:
module crc8定义了一个名为crc8的模块,它有clk(时钟信号)、rst(复位信号)、data_in(8 位输入数据)、data_valid(数据有效信号)以及crc_out(8 位 CRC 校验结果输出)这些端口。 - 寄存器定义:
reg [7:0] crc_reg定义了一个 8 位的寄存器crc_reg来存储当前的 CRC 值。 - always 块:第一个
always块在时钟上升沿或者复位信号有效时触发。复位时,crc_reg清零。当data_valid有效时,先将输入数据与当前crc_reg的值进行异或操作,然后通过 8 次循环,根据crc_reg最高位是否为 1,决定是否与生成多项式对应的二进制数进行异或操作,并移位。 - 输出赋值:第二个
always块将crc_reg的值赋给crc_out。
CRC16 和 CRC32 的实现
CRC16 和 CRC32 的实现思路与 CRC8 类似,只是移位寄存器的宽度和生成多项式不同。
CRC16 代码示例(Verilog)
module crc16 ( input wire clk, input wire rst, input wire [7:0] data_in, input wire data_valid, output reg [15:0] crc_out );
reg [15:0] crc_reg;
always @(posedge clk or posedge rst) begin
if (rst) begin
crc_reg <= 16'h0000;
end else if (data_valid) begin
crc_reg = crc_reg ^ {8'h00, data_in};
for (int i = 0; i < 8; i = i + 1) begin
if (crc_reg[15]) begin
crc_reg = {crc_reg[14:0], 1'b0} ^ 16'b11000000000000101;
end else begin
crc_reg = {crc_reg[14:0], 1'b0};
end
end
end
end
always @(*) begin
crc_out = crc_reg;
end
endmodule


