赞
踩
一、 文章简述
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 @(*) begin lfsr_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 // always always @(posedge clk, posedge rst) begin if(rst) begin lfsr_q <= {8{1'b1}}; end else begin lfsr_q <= crc_en ? lfsr_c : lfsr_q; end end // always endmodule // crc
四、Verilog代码移植与修改
4.1参数要求
CRC-8/MAXIM的参数如图5右侧所示,其中Poly为0x31,初始值为0x00,输入输出需要数据位翻转,输出值要异或0x00(相当于不进行异或)。在生成的代码中我们要进行初始值的设置,数据输入输出的翻转。
图5 CRC-8/MAXIM的参数
4.2 代码修改
从参数身上可以看出,我们需要进行三处的修改(输出结果异或0x00相当于不异或):
// 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位的data 2.将data_in 翻转赋值给data 3.将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 @(*) begin lfsr_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) begin if(rst) begin lfsr_q <= {8{1'b0}}; end else begin lfsr_q <= crc_en ? lfsr_c: lfsr_q; end end // 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) begin if(cnt == 2'd3) begin CLK_DIV <= ~CLK_DIV; cnt <= 2'd0; end else begin cnt <= cnt + 1'd1; end end //进行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) begin case(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; //关闭crc crc_en <= 1'd0; //复位crc crc_rst <= 1'd1; //状态计数器加1 crc_cnt <= crc_cnt + 1'd1; end 8'd1: begin //使能crc crc_en <= 1'd1; //停止复位crc crc_rst <= 1'd0; //输入数据初值 crc_data_in <= crc_data_test[0]; //状态计数器加1 crc_cnt <= crc_cnt + 1'd1; end 8'd2: begin //输入数据初值 crc_data_in <= crc_data_test[1]; //状态计数器加1 crc_cnt <= crc_cnt + 1'd1; end 8'd3: begin //输入数据初值 crc_data_in <= crc_data_test[2]; //状态计数器加1 crc_cnt <= crc_cnt + 1'd1; end 8'd4: begin //输入数据初值 crc_data_in <= crc_data_test[3]; //状态计数器清零 crc_cnt <= 8'd0; end default: begin crc_cnt <= 8'd0; end endcase end endmodule
5.2测试结果
CRC计算机计算结果如图6所示。
图6 CRC计算机计算结果
FPGA 测试结果如图7所示。
图7 FPGA 测试结果
从图中可以看到,输入数据相同的情况下,在数据输入结束后的,CRC校验输出值与CRC计算器计算的结果是一样。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。