欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

CRC校验算法的Verilog实现

发布时间:2023/12/31 编程问答 51 豆豆
生活随笔 收集整理的这篇文章主要介绍了 CRC校验算法的Verilog实现 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

一、 文章简述
CRC算法在通讯和数据传输领域中有着广泛的应用,关于CRC的原理本文档不做阐述,本文档将将重点放在Verilog CRC代码生成工具的使用和如何修改代码使其满足我们的要求两个方面来CRC算法Verilog实现的进行讲解。本教程包含以下方面的内容:
1.CRC类型简介
2.CRC参数简介
3.crc-gen代码生产工具的使用
4.CRC 校验代码的移植
5.CRC校验代码的测试
二、 CRC算法中参数的简介
2.1 CRC类型
简单来讲,CRC分为CRC-4、CRC-5、CRC-6、CRC-7、CRC-8、CRC-16、CRC-32;但是又可以进行更细节的分类,如图1所示,同样是CRC-8却有四种不同的计算方法,如CRC-8、CRC-8/ITU、CRC-8/ROHC、CRC-8/MAXIM。因此在进行CRC检验时,要根据实际情况进行选择,并且在双方进行通讯时,校验方式务必一致。

图1 CRC类型图
2.2 CRC参数:
从图1中可以看出,每一种CRC都有多个参数,这个参数用来确定检验算法具体如何实现,如图2所示。

图2 CRC参数图
CRC Name:CRC算法的名称,也是在CRC校验使用过程中很重要的参数,使用时通讯双方使用同一种检验方式进行检验、对比才有意义
CRC Width:CRC输出结果的位宽,与CRC名称中的-X相对应。
CRC Poly:该参数是CRC校验的核心参数,它与CRC校验公式有关。例如:CRC-5/ITC的公式为x5+x4+x2+1,该公式等效为x5×1+x4×1+x3×0+x2×1+x1×0+x0×1,二进制表示其阶数关系:10101(必须去掉最高阶数),二进制的10101等于十六进行的0x15。同理其他检验的Poly也可以以同样的方式进行确定。
CRC Init:该参数为CRC校验的结果的初始值。
CRC RefIn:该参数确定输入的数据是否进行字节的反转,如果是true则需要反转,若为false则不需要反转。
CRC RefOut:该参数确定CRC初步校验结果是否进行字节的反转,如果是true则需要反转,若为false则不需要反转。
CRC XorOut:该参数确定CRC校验结果输出时的异或值。
2.3 CRC检验的过程的理解:
关于CRC校验,我的理解是,首先有一个最基本的CRC的算法,它是CRC的主干,它需要的参数是Width、Poly、Init;还有一些外围的算法,例如输入输出是否反转,输出结果需要异或的值,它的参数包括:RefIn、RefOut、XorOut。
三、CRC算法Verilog代码生产工具的使用
3.1 crc-gen工具简介
crc-gen是一款基于命令行操作的crc校验代码生成工具,可以生成VHDL和Verilo HDL语言的CRC算法的基础代码。生成基础代码之后,我们只需要根据自己所使用的CRC校验算法的参数要求在进行参数上的修改,就可以写出符合要求的CRC校验的VHDL或Verilog HDL的代码。本人学习的是Verilog HDL语言,本文将以该语言进行讲解。
Crc-gen工具可以在网上进行下载,也可以使用下面的链接进行下载。
crc-gen下载链接:https://pan.baidu.com/s/1BuH7LmCB7x2gd1tJAuPCYA
提取码:rt2z
3.2 crc-gen 的使用
在进行使用前,请先看一下crc-gen文件夹中的说明文档。以免下面的操作有不好理解的地方。本例采用CRC-8/MAXIM校验方式,CRC多项式为:x8+x5+x4+1。
首先打开电脑cmd命令行,并进入crc-gen文件夹,如图3所示:

图3 crc-gen初始界面
crc-gen的命令格式如下:
crc-gen language data_width poly_width poly_string
language: verilog or vhdl
data_width : 数据数据位宽,1-1024。
poly_width : CRC多项式宽度,1-1024。
poly_string : CRC多项式描述,即CRC Poly参数,16进制表示。
CRC-8/MAXIM的多项式为x8+x5+x4+1,通过多项式可以得到Poly值为0x31。我们要生成的代码,编程语言采用Verlog,数据输入为8位,输入以下命令:
crc-gen Verilog 8 8 31,如图4所示:

图4 crc-gen 命令输入及代码生成界面
从图中可以看出CRC的代码已经生成,并且公式与我们所要求的公式是一致的。
将代码赋值到文件中,复制的代码如下:

// CRC module for // data[7:0] // crc[7:0]=1+x^4+x^5+x^8; // module crc(input [7:0] data_in,input crc_en,output [7:0] crc_out,input rst,input clk);reg [7:0] lfsr_q,lfsr_c;assign crc_out = lfsr_q;always @(*) beginlfsr_c[0] = lfsr_q[0] ^ lfsr_q[3] ^ lfsr_q[4] ^ lfsr_q[6] ^ data_in[0] ^ data_in[3] ^ data_in[4] ^ data_in[6];lfsr_c[1] = lfsr_q[1] ^ lfsr_q[4] ^ lfsr_q[5] ^ lfsr_q[7] ^ data_in[1] ^ data_in[4] ^ data_in[5] ^ data_in[7];lfsr_c[2] = lfsr_q[2] ^ lfsr_q[5] ^ lfsr_q[6] ^ data_in[2] ^ data_in[5] ^ data_in[6];lfsr_c[3] = lfsr_q[3] ^ lfsr_q[6] ^ lfsr_q[7] ^ data_in[3] ^ data_in[6] ^ data_in[7];lfsr_c[4] = lfsr_q[0] ^ lfsr_q[3] ^ lfsr_q[6] ^ lfsr_q[7] ^ data_in[0] ^ data_in[3] ^ data_in[6] ^ data_in[7];lfsr_c[5] = lfsr_q[0] ^ lfsr_q[1] ^ lfsr_q[3] ^ lfsr_q[6] ^ lfsr_q[7] ^ data_in[0] ^ data_in[1] ^ data_in[3] ^ data_in[6] ^ data_in[7];lfsr_c[6] = lfsr_q[1] ^ lfsr_q[2] ^ lfsr_q[4] ^ lfsr_q[7] ^ data_in[1] ^ data_in[2] ^ data_in[4] ^ data_in[7];lfsr_c[7] = lfsr_q[2] ^ lfsr_q[3] ^ lfsr_q[5] ^ data_in[2] ^ data_in[3] ^ data_in[5];end // alwaysalways @(posedge clk, posedge rst) beginif(rst) beginlfsr_q <= {8{1'b1}};endelse beginlfsr_q <= crc_en ? lfsr_c : lfsr_q;endend // always endmodule // crc

四、Verilog代码移植与修改
4.1参数要求
CRC-8/MAXIM的参数如图5右侧所示,其中Poly为0x31,初始值为0x00,输入输出需要数据位翻转,输出值要异或0x00(相当于不进行异或)。在生成的代码中我们要进行初始值的设置,数据输入输出的翻转。

图5 CRC-8/MAXIM的参数
4.2 代码修改
从参数身上可以看出,我们需要进行三处的修改(输出结果异或0x00相当于不异或):

  • CRC结果初始值,即每次CRC计算的起始值,需设置为0x00。
  • 对输入数据的进行位数的翻转。
  • 对输出的CRC校验值进行翻转。
    修改后的代码如下:
  • // CRC module for // data[7:0] // crc[7:0]=1+x^4+x^5+x^8; // module crc(input [7:0] data_in,input crc_en,output [7:0] crc_out,input rst,input clk);reg [7:0] lfsr_q,lfsr_c;//输入数据的翻转/*1.定义一个8位的data2.将data_in 翻转赋值给data3.将always @(*)语句块中的data_in替换为data*/wire [7:0]data;assign data = {data_in[0],data_in[1],data_in[2],data_in[3],data_in[4],data_in[5],data_in[6],data_in[7]};//输出数据的翻转assign crc_out = {lfsr_q[0],lfsr_q[1],lfsr_q[2],lfsr_q[3],lfsr_q[4],lfsr_q[5],lfsr_q[6],lfsr_q[7]};always @(*) beginlfsr_c[0] = lfsr_q[0] ^ lfsr_q[3] ^ lfsr_q[4] ^ lfsr_q[6] ^ data[0] ^ data[3] ^ data[4] ^ data[6];lfsr_c[1] = lfsr_q[1] ^ lfsr_q[4] ^ lfsr_q[5] ^ lfsr_q[7] ^ data[1] ^ data[4] ^ data[5] ^ data[7];lfsr_c[2] = lfsr_q[2] ^ lfsr_q[5] ^ lfsr_q[6] ^ data[2] ^ data[5] ^ data[6];lfsr_c[3] = lfsr_q[3] ^ lfsr_q[6] ^ lfsr_q[7] ^ data[3] ^ data[6] ^ data[7];lfsr_c[4] = lfsr_q[0] ^ lfsr_q[3] ^ lfsr_q[6] ^ lfsr_q[7] ^ data[0] ^ data[3] ^ data[6] ^ data[7];lfsr_c[5] = lfsr_q[0] ^ lfsr_q[1] ^ lfsr_q[3] ^ lfsr_q[6] ^ lfsr_q[7] ^ data[0] ^ data[1] ^ data[3] ^ data[6] ^ data[7];lfsr_c[6] = lfsr_q[1] ^ lfsr_q[2] ^ lfsr_q[4] ^ lfsr_q[7] ^ data[1] ^ data[2] ^ data[4] ^ data[7];lfsr_c[7] = lfsr_q[2] ^ lfsr_q[3] ^ lfsr_q[5] ^ data[2] ^ data[3] ^ data[5];end // always//校验初始化/*将always @(posedge clk, posedge rst) 中的 lfsr_q <= {8{1'b1}}改为 lfsr_q <= {8{1'b0}}.*/always @(posedge clk, posedge rst) beginif(rst) beginlfsr_q <= {8{1'b0}};endelse beginlfsr_q <= crc_en ? lfsr_c: lfsr_q;endend // always endmodule // crc

    五、测试及分析
    5.1测试代码
    测试输入数据位0x01、0x02、0x03、0x04,测试代码如下:

    module CRC( input CLK );//对时钟进行分频,用于运算reg [1:0]cnt = 2'd0;reg CLK_DIV = 1'd0;always@(posedge CLK)beginif(cnt == 2'd3)beginCLK_DIV <= ~CLK_DIV;cnt <= 2'd0;endelsebegincnt <= cnt + 1'd1;endend//进行CRC模块的测试reg [7:0]crc_data_test[3:0];reg [7:0]crc_cnt = 8'd0;reg [7:0]crc_data_in;wire [7:0]crc_data_out;reg crc_en;reg crc_rst;//crc-8实例化crc(.data_in(crc_data_in),.crc_en(crc_en),.crc_out(crc_data_out),.rst(crc_rst),.clk(~CLK_DIV));//测试状态机always@(posedge CLK_DIV)begincase(crc_cnt)8'd0:begin//测试数据赋初值crc_data_test[0] <= 8'h01;crc_data_test[1] <= 8'h02;crc_data_test[2] <= 8'h03;crc_data_test[3] <= 8'h04;//关闭crccrc_en <= 1'd0;//复位crccrc_rst <= 1'd1;//状态计数器加1crc_cnt <= crc_cnt + 1'd1;end8'd1:begin//使能crccrc_en <= 1'd1;//停止复位crccrc_rst <= 1'd0;//输入数据初值crc_data_in <= crc_data_test[0];//状态计数器加1crc_cnt <= crc_cnt + 1'd1;end8'd2:begin//输入数据初值crc_data_in <= crc_data_test[1];//状态计数器加1crc_cnt <= crc_cnt + 1'd1;end8'd3:begin//输入数据初值crc_data_in <= crc_data_test[2];//状态计数器加1crc_cnt <= crc_cnt + 1'd1;end8'd4:begin//输入数据初值crc_data_in <= crc_data_test[3];//状态计数器清零crc_cnt <= 8'd0;enddefault:begincrc_cnt <= 8'd0;endendcaseend endmodule

    5.2测试结果
    CRC计算机计算结果如图6所示。

    图6 CRC计算机计算结果
    FPGA 测试结果如图7所示。

    图7 FPGA 测试结果
    从图中可以看到,输入数据相同的情况下,在数据输入结束后的,CRC校验输出值与CRC计算器计算的结果是一样。

    总结

    以上是生活随笔为你收集整理的CRC校验算法的Verilog实现的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。