当前位置: 首页 > article >正文

详解CRC校验原理以及FPGA实现

文章目录

  • 一、什么是CRC校验?
  • 二、实现CRC校验原理以及步骤
    • 2.1 用多项式表示二元码数据
    • 2.2 选择一个生成多项式作为校验
    • 2.3 计算CRC校验码
  • 三、CRC判断数据是否错误的原理以及步骤
    • 3.1 将收到的数据与生成多项式求余
    • 3.2 数据发生错误再进行CRC校验判断
  • 四、FPGA实现CRC
    • 4.1 线性移位反馈寄存器
    • 4.2 LFSR工作原理
    • 4.3 仿真验证


一、什么是CRC校验?

  CRC(Cyclic Redundancy Check,循环冗余校验)是一种广泛使用的错误检测技术,主要用于检测数据在传输或存储过程中是否发生了错误。它通过对数据进行特定的数学运算,生成一个固定长度的校验码(CRC 校验码),并将其附加到数据后面。接收方在收到数据时,可以通过相同的运算来验证数据的完整性。

  CRC 校验的基本思路是将数据视为一个多项式,并通过模 2 除法(即不考虑进位的二进制除法)来计算校验值:

  1. 选择生成多项式:CRC 校验使用一个预定义的生成多项式(通常是一个二进制数),这个多项式的选择会影响 CRC 校验的性能和错误检测能力。
  2. 数据多项式:将待校验的数据视为一个多项式,使用二进制表示。
  3. 模 2 除法:用数据多项式除以生成多项式,得到的余数就是 CRC 校验码。
  4. 附加校验码:将 CRC 校验码附加到原始数据后面,形成完整的数据包。

二、实现CRC校验原理以及步骤

2.1 用多项式表示二元码数据

   将输入的数据看成一个多项式,其多项式的公式如下:

G ( X ) = g n x n + g n − 1 x n − 1 + g n − 2 x n − 2 + g 2 x 2 + g 1 x 1 + g 0 x 0 = ∑ i = 0 i = n g i x i G(X)=g_nx^n + g_{n-1}x^{n-1} + g_{n-2}x^{n-2} + g_2x^2 + g_1x^1 + g_0x^0 = \sum_{i=0}^{i=n}g_ix^i G(X)=gnxn+gn1xn1+gn2xn2+g2x2+g1x1+g0x0=i=0i=ngixi

  • g i g_i gi表示二元码中第i位的数
  • x i x^i xi表示二元码中第i位

   例如:一个多项式为 G ( X ) = x 4 + x + 1 = 1 ∗ x 4 + 0 ∗ x 3 + 0 ∗ x 2 + 1 ∗ x 1 + 1 ∗ x 0 G(X)=x^4 + x + 1=1*x^4 + 0*x^3 + 0*x^2 + 1*x^1 + 1*x^0 G(X)=x4+x+1=1x4+0x3+0x2+1x1+1x0
则这个多项式表示数据为{1,0,0,1,1},反之亦然,如果知道一个数据,也就知道这个数据对应的多项式。

2.2 选择一个生成多项式作为校验

   生成多项式满足:仅仅能够被1和自身整除,常见的生成多项式有以下:

在这里插入图片描述
  这里面多项式的宽度以及多项式的值是去掉了最高位1的,比如我们上面举例 G ( X ) = x 4 + x + 1 G(X)=x^4 + x + 1 G(X)=x4+x+1表示为{1,0,0,1,1},一共五位,换成16进制为13。截图里去掉了最高位1,就变成了{0,0,1,1}宽度为4,初始值03。

2.3 计算CRC校验码

  在上面我们知道了二元序列的多项式表示,以及一些常见的生成多项式,我们就要计算出我们需要的CRC校验码,具体计算就是除法取余数。例如:我们需要传输的数据为:{1,0,0,1,0,1,0,1},我们选择CRC4的生成多项式 G ( X ) = x 4 + x + 1 G(X)=x^4 + x + 1 G(X)=x4+x+1来计算CRC校验码。

  1. 将需要传输的数据最低位补n个0,这里的n是指生成多项式中x的最高次方,这里CRC4的最高次方是4,因此需要在数据最低为补4个0,变成为{1,0,0,1,0,1,0,1,0000}

  2. 用补0后的数据整除生成多项式取得余数,二进制取余数用模2除法,步骤如下:

在这里插入图片描述

  1. 将余数{0,1,0,0}就是CRC校验码,填充到传输数据的末尾即可得到整个CRC编码{1,0,0,1,0,1,0,1,0100}

  我们用CRC校验生成网站,计算一下本次结果和我们手算的是否一致,{1,0,0,1,0,1,0,1}换成16进制为95,打开网站:

在这里插入图片描述
  可以看到我们用网站计算的结果和手算的结果一致。

三、CRC判断数据是否错误的原理以及步骤

  接收方收到整个数据包后,将整个数据对约定好的生成多项式进行模2取余操作,最后判断余数是否为0,如果余数为0表示数据传输无误,如果不为0表示数据传输有错,还是用上面的例子来操作一遍:

3.1 将收到的数据与生成多项式求余

在这里插入图片描述

  如果余数为0,就表示这次收到的数据没有发生错误;如果数据包发生了错误,无论1位还是多位,余数一定不为0;

3.2 数据发生错误再进行CRC校验判断

  我们随机将收到的数据两位取反,表示收到的数据收到噪声干扰出错了,我们再与生成多项式取余操作:

在这里插入图片描述
  由此可见,如果传输过程中,数据发生了错误最终CRC校验码不等于0。

四、FPGA实现CRC

  实现CRC的关键就是取余数,在FPGA中可以使用线性移位反馈寄存器来实现取余。

4.1 线性移位反馈寄存器

  LFSR(线性反馈移位寄存器,Linear Feedback Shift Register)是一种用于生成伪随机序列的电路或算法。它在数字电路、通信系统和密码学中有广泛的应用。LFSR 通过使用线性反馈机制来生成新的位,通常用于加密、错误检测和伪随机数生成等领域。

LFSR的基本组成:

  1. 寄存器:LFSR 由一系列的位存储单元(通常是触发器)组成,这些位存储在寄存器中。寄存器的长度通常用n表示。
  2. 反馈多项式:LFSR 的反馈是通过一个反馈多项式来实现的,该多项式定义了哪些位会参与反馈计算。反馈多项式通常表示为: G ( X ) = g n x n + g n − 1 x n − 1 + g n − 2 x n − 2 + g 2 x 2 + g 1 x 1 + g 0 x 0 G(X)=g_nx^n + g_{n-1}x^{n-1} + g_{n-2}x^{n-2} + g_2x^2 + g_1x^1 + g_0x^0 G(X)=gnxn+gn1xn1+gn2xn2+g2x2+g1x1+g0x0
  3. 移位操作:在每个时钟周期,LFSR 会将寄存器中的所有位向右移位,并将新的反馈位放入寄存器的最高位
    在这里插入图片描述

4.2 LFSR工作原理

  • 首先将移位寄存器里面的寄存器赋非0初值,也叫种子。刚开始 R 0 , R 1 , R 2 , . . . . . . , R n − 1 {R_0,R_1,R_2,......,R_n-1} R0R1R2......Rn1表示种子。
  • 然后根据生成多项式,来确定反馈位(通常是某些位的异或结果);即: g 0 , g 1 , g 2 , g 3 , . . . . . . . , g n − 2 , g n − 1 , g n {g_0,g_1,g_2,g_3,.......,g_n-2,g_n-1,g_n} g0g1g2g3.......gn2gn1gn表示抽头系数。
  • 最后移位,将输入的数据低n位从高到低移动进来,移动结束后, R 0 , R 1 , R 2 , . . . . . . , R n − 1 {R_0,R_1,R_2,......,R_n-1} R0R1R2......Rn1表示余数。

4.3 仿真验证

  根据前面的CRC计算原理以及LFSR原理,我们搭建电路如上所示,我们还是用最开始的例子:传输的数据为{1,0,0,1,0,1,0,1},选择生成多项式 G ( X ) = x 4 + x + 1 G(X)=x^4 + x + 1 G(X)=x4+x+1,仿真代码如下:

`timescale 1ns / 1ps

module tb_crc4_lfsr();

    reg                                                 sys_clk ;
    reg                                                 sys_rst ;
    reg                                                 din     ;

//发送信息为1001_0101
//后面补4个0就是10010101_0000
initial begin
    sys_clk = 0;
    sys_rst = 1;
    din = 0;
    #200
    @(posedge sys_clk)
    sys_rst = 0;
    #500
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
     #200 $stop;
end

always #5 sys_clk = ~sys_clk;

crc4_lfsr u_crc4_lfsr(
    .sys_clk         ( sys_clk        	),
    .sys_rst         ( sys_rst         	),
    .din             ( din             	),
    .crc_code_valid  (   				),
    .crc_code        (         			)
);
endmodule

在这里插入图片描述

  可以看到我们计算出来的CRC校验码为{0,1,0,0}和我们前面计算的一致。我们将计算好的CRC校验码加到发送信息后四位,再经过LFSR看一下结果,仿真代码如下:

`timescale 1ns / 1ps

module tb_crc4_lfsr();

    reg                                                 sys_clk ;
    reg                                                 sys_rst ;
    reg                                                 din     ;

//发送信息为1001_0101
//后面补4个0就是10010101_0000
initial begin
    sys_clk = 0;
    sys_rst = 1;
    din = 0;
    #200
    @(posedge sys_clk)
    sys_rst = 0;
    #500
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    #200 //加入前面计算好的余数0100,总共信息位就是10010101_0100
     @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
     #200 $stop;
end

always #5 sys_clk = ~sys_clk;

crc4_lfsr u_crc4_lfsr(
    .sys_clk         ( sys_clk        	),
    .sys_rst         ( sys_rst         	),
    .din             ( din             	),
    .crc_code_valid  (   				),
    .crc_code        (         			)
);
endmodule

在这里插入图片描述
   可以看到,将计算好的余数添加到信息位后面,再经过LFSR后得到余数为0,表示数据无误。我们随机将打反一位数据,仿真代码如下:

`timescale 1ns / 1ps

module tb_crc4_lfsr();

    reg                                                 sys_clk ;
    reg                                                 sys_rst ;
    reg                                                 din     ;

//发送信息为1001_0101
//后面补4个0就是10010101_0000
initial begin
    sys_clk = 0;
    sys_rst = 1;
    din = 0;
    #200
    @(posedge sys_clk)
    sys_rst = 0;
    #500
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
    #200 //加入前面计算好的余数0100,总共信息位就是10010101_0100,我们随机打反一位数据,信息变成 11010101_0100
     @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 1;
    @(posedge sys_clk)
    din = 0;
    @(posedge sys_clk)
    din = 0;
     #200 $stop;
end

always #5 sys_clk = ~sys_clk;

crc4_lfsr u_crc4_lfsr(
    .sys_clk         ( sys_clk        	),
    .sys_rst         ( sys_rst         	),
    .din             ( din             	),
    .crc_code_valid  (   				),
    .crc_code        (         			)
);
endmodule

在这里插入图片描述
   可以看到,如果传输过程中,数据位发生了错误,那么经过解码后的余数不为0;


http://www.kler.cn/a/375429.html

相关文章:

  • 如何解决 ‘adb‘ 不是内部或外部命令,也不是可运行的程序或批处理文件的问题
  • 《计算机组成及汇编语言原理》阅读笔记:p48-p81
  • 深度学习每周学习总结J9(Inception V3 算法实战与解析 - 天气识别)
  • 【085】基于51单片机PID直流电机控制系统【Proteus仿真+Keil程序+报告+原理图】
  • 【游戏设计原理】22 - 石头剪刀布
  • PostgreSQL标识符长度限制不能超过63字节
  • nVisual标签打印模块的部署与使用
  • Android Studio中Gradle一直在加载怎么解决(首次安装过程中)
  • Spark_跑批__.checkpoint()为什么比.persist()快
  • TikTok视频上传失败:原因与解决方案
  • 【AI学习】Mamba学习(十四):S5模型的矩阵A对角化
  • 黑龙江某涝区泵闸站自动化、信息化改造项目案例
  • C语言函数嵌套调用
  • 【iOS】使用AFNetworking进行网络请求
  • Java第二阶段---16字符串---第一节 String
  • 探索Python文档自动化的奥秘:揭开docxtpl库的神秘面纱
  • 大数据-201 数据挖掘 机器学习理论 - 决策树 局部最优 剪枝 分裂 二叉分裂
  • Jenkins 构建时报错 No RSA host key is known for
  • shell脚本实例(4)while实现1+...+100,linux新增用户
  • notepad++ compare插件的离线下载和安装
  • 【大数据学习 | kafka】kafka的ack和一致性
  • Rust:Vec<u8> 与 [u8] 之间的转换
  • 第三方的mkvimball 使用
  • PySpark 本地开发环境搭建与实践
  • 一些MATLAB到Python的转换指南
  • 助力风力发电风机设备智能化巡检,基于YOLOv3全系列【tiny/l/spp】参数模型开发构建无人机巡检场景下风机叶片缺陷问题智能化检测预警模型