当前位置:   article > 正文

CRC效验码Verilog_crc verilog

crc verilog

 


前言

在学习写SD卡2.0协议的一个SD卡模型(Verilog)的时候,遇到了CRC效验码的有关问题,记录一下。


 

一、CRC效验码是什么?

CRC效验码是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。

基本思想是将传输的数据补位一定长度。将这个补位后的数除以另一个数(约定好的一个多项式 ,如x^7 + x^3 + 1,或者二进制表示10001001)。得到的余数作为校验数据附加到原数据后面。

计算方法为采用无进位的二进制加法(等价于XOR操作),最后对另一个数(约定好的一个多项式 ,如x^7 + x^3 + 1)取模。

二、在SD2.0中的CRC

1.CRC 适用于保护 SD 总线上的 SD 卡命令,响应和数据传输免于出错的。命令线上,每个命令 都会产生 CRC,而每个响应也都会检查 CRC。对于数据块来说,每一个传输的块都会产生 CRC。

CRC 的产生和检查描述如下:

CRC7 的检测是用于所有的命令,所有的响应(除了 R3),以及 CSD 和 CID 寄存器。CRC7 是一个 7bit 的数,计算方法如下: G(x) = x^7 + x^3 + 1。

M(x) = (第 1bit)*x^n + (第 2bit)*x^(n-1) + … + (n+1bit)*x^0

CRC[6:0] = [(M(x)*x^7)/G(x)]的余数

第 1bit 是相对应 bit 串(命令,响应,CID,CSD)最左边的 bit。 n 是 CRC 保护的 bit 数-1。命令和响应的保护数是 40(n=39),CSD 和 CID 是 120(n=119)。 

CRC7 的生成器和检查器

 CRC7 实例

命令和响应的 CRC 断。

CMD0 (参数=0)     01 000000 00000000000000000000000000000000 "1001010" 1

CMD17(参数=0)    01 010001 00000000000000000000000000000000 "0101010" 1

CMD17 的响应      00 010001 00000000000000000000100100000000 "0110011" 1 

CMD17中 [7:1] 7位bit 即"0101010"    是对 [47:8] 前40个bit '01 010001 00000000000000000000000000000000'的效验结果

   这里是一个7位CRC校验码,不同的CRC参数模型有不同的G(x),常用的有

 

 

2.CRC代码生成

在写相应代码的时候,刚开始没有注意CRC的计算法方,就直接把公式在vivado里写了上去,当然结果不符合要求。

于是我在网上查找资料,发现了几个不错的CRC的Verilog(VHDL)代码生成网站,输入相应的参数就可以生成有效的Verilog代码了,非常好用,推荐给大家。

计算CRC的网站     http://www.ip33.com/crc.html

生成CRC代码的网站1     http://outputlogic.com/?page_id=321

蓝色箭头表示要保护的数据长度,如40

红色箭头表示要生成的CRC长度,如7

点击Apply,step2,勾选相应的Gx,Generate Verilog代码

生成CRC代码的网站2    https://www.easics.com/crctool/

选择相应的Gx,输入想要生成的CRC效验码长度,Download .v 文件,这个网站的代码要改改就可以用来学习了,40CRC7的模块代码如下所示:

module CRC7_D40(

 

  // polynomial: x^7 + x^3 + 1

  // data width: 40

  // convention: the first serial bit is D[39]

    output [6:0] nextCRC7_D40,

 

    input [39:0] Data,

    input [6:0] crc

    );

    reg [39:0] d;

    reg [6:0] c;

    reg [6:0] newcrc;

    always @(*)

    begin

    d = Data;

    //c = crc;

    c = 'b0000000;

 

    newcrc[0] <= d[39] ^ d[37] ^ d[35] ^ d[34] ^ d[31] ^ d[30] ^ d[24] ^ d[23] ^ d[21] ^ d[20] ^ d[18] ^ d[16] ^ d[15] ^ d[14] ^ d[12] ^ d[8] ^ d[7] ^ d[4] ^ d[0] ^ c[1] ^ c[2] ^ c[4] ^ c[6];

    newcrc[1] <= d[38] ^ d[36] ^ d[35] ^ d[32] ^ d[31] ^ d[25] ^ d[24] ^ d[22] ^ d[21] ^ d[19] ^ d[17] ^ d[16] ^ d[15] ^ d[13] ^ d[9] ^ d[8] ^ d[5] ^ d[1] ^ c[2] ^ c[3] ^ c[5];

    newcrc[2] <= d[39] ^ d[37] ^ d[36] ^ d[33] ^ d[32] ^ d[26] ^ d[25] ^ d[23] ^ d[22] ^ d[20] ^ d[18] ^ d[17] ^ d[16] ^ d[14] ^ d[10] ^ d[9] ^ d[6] ^ d[2] ^ c[0] ^ c[3] ^ c[4] ^ c[6];

    newcrc[3] <= d[39] ^ d[38] ^ d[35] ^ d[33] ^ d[31] ^ d[30] ^ d[27] ^ d[26] ^ d[20] ^ d[19] ^ d[17] ^ d[16] ^ d[14] ^ d[12] ^ d[11] ^ d[10] ^ d[8] ^ d[4] ^ d[3] ^ d[0] ^ c[0] ^ c[2] ^ c[5] ^ c[6];

    newcrc[4] <= d[39] ^ d[36] ^ d[34] ^ d[32] ^ d[31] ^ d[28] ^ d[27] ^ d[21] ^ d[20] ^ d[18] ^ d[17] ^ d[15] ^ d[13] ^ d[12] ^ d[11] ^ d[9] ^ d[5] ^ d[4] ^ d[1] ^ c[1] ^ c[3] ^ c[6];

    newcrc[5] <= d[37] ^ d[35] ^ d[33] ^ d[32] ^ d[29] ^ d[28] ^ d[22] ^ d[21] ^ d[19] ^ d[18] ^ d[16] ^ d[14] ^ d[13] ^ d[12] ^ d[10] ^ d[6] ^ d[5] ^ d[2] ^ c[0] ^ c[2] ^ c[4];

    newcrc[6] <= d[38] ^ d[36] ^ d[34] ^ d[33] ^ d[30] ^ d[29] ^ d[23] ^ d[22] ^ d[20] ^ d[19] ^ d[17] ^ d[15] ^ d[14] ^ d[13] ^ d[11] ^ d[7] ^ d[6] ^ d[3] ^ c[0] ^ c[1] ^ c[3] ^ c[5];

 

    end

assign    nextCRC7_D40 = newcrc;

endmodule

 

仿真结果是确实有效的:


总结

  生成的代码抽象度比较高,直接展开来算,资源换速度,直接异或出结果,一个时钟周期就好了,比较快,我觉得最优的方法可能是接收的同时直接异或,按照SD卡2.0里的计算方法,一个时钟处理一位,这样应该是资源和时间利用率比较高的方法。
以上就是今天内容,本文仅仅简单介绍了CRC效验码的使用。

  咨询了一下前辈,了解到CRC硬件电路类似于LFSR(线性反馈移位寄存器) ,也就是数电中的移位寄存器加反馈,在verilog中可以非常简便的描述出来,CRC里是模2除,硬件电路里就是左移,CRC7,就是7个左移寄存器加上Gx(x^7+x^3+1)里的反馈(异或连接),其中利用LFSR实现CRC的方法比较节约资源,在接收数据的同时接入LFSR,结果接受完就出来了,实现方法也非常简单,方便拓展。

LFSR做CRC代码:

module CRC_model(

        input clk,

        input rst,

        input din,//输入

        input cmd_en,

        output [6:0] crc_out//7位的CRC效验


 

);

reg temp = 'd0;

//reg en = 'd0;

reg [6:0] crc = 'd0;

assign crc_out = crc;

 

always @(posedge clk or negedge rst) begin

    if (!rst) begin

        temp <= 'd0;

        crc  <= 'd0;

    end

    else if(cmd_en) begin//7位CRC效验的多项式为 x^7 + x^3 + 1(x^0)

    temp     = crc[6] ^ din;    //x^7 din是输入 temp是反馈

    crc[6]   = crc[5];

    crc[5]   = crc[4];

    crc[4]   = crc[3];

    crc[3]   = crc[2] ^ temp;   //x^3

    crc[2]   = crc[1];

    crc[1]   = crc[0];

    crc[0]   = temp ;           //x^0

    end

    else crc <= 'd0;

end

endmodule

两中方法的比较:

这里我是把48位数据接收完再放到并行CRC模块里进行计算的,其实可以39位的时候就可以计算,时间上感觉差不多,资源上肯定是LFSR实现的比较少。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/879917
推荐阅读
相关标签
  

闽ICP备14008679号