CRC_8循环冗余校验码verilog实现

前言

本题来自2021年bit北京集电赛数字组培训,出题为张延军老师。
一个勤勤恳恳的入门菜鸟大学生以此作为自己第一篇博客,记录一下学习的内容,若有不足或错误的地方,欢迎大家在评论区指正!!
bit张延军老师出题

CRC原理

概述

为了检测数据传输是否准确,人们发明了许多检测方式,常见的有奇偶校验、因特网校验和循环冗余校验等。
在这里插入图片描述

循环冗余码(CRC)的本质就是在需要发送的数据(D)末尾附加一个校验码(F),生成一个新数据(T)发送给接收端。发送端与接收端共同约定一个整数,发送机对D进行“去余”处理,使得接收端接收到的T能够被此整数整除。若无法整除则证明传输过程中出现了差错。

过程

(1)约定一个除数P(二进制比特串,通常以多项式的方式表示)
(2)发送方在发送前根据数据D确定F,生成CRC码 T,T 即为数据位D与校 验位F的拼接,发送T
(3)接收方收到CRC码 T,以“模2除法”除以除数P,当且仅当余数为 0时接收方认为没有差错。

模2除法

模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。模2加法运算为:1+1=0,0+1=1,0+0=0,无进位,也无借位;模2减法运算为:1-1=0,0-1=1,1-0=1,0-0=0
模2算法实例

确定校验码方法

首先看所选定的除数二进制位数(假设为n-k+1位),然后在要发送的数据(假设为k位)后面加上n-k位“0”,然后用这个加了n-k个“0“的新数据(一共是n位)以“模2除法”方式除以上面这个除数,所得到的余数(也是二进制的比特串)就是该帧的CRC校验码。
但要注意的是,余数的位数一定要是比除数位数只能少一位,哪怕前面位是0,甚至是全为0(附带好整除时)也都不能省略。例如此处,余数位数为n-k,除数位数为n-k+1
例如:
G ( x ) = x 8 + x 2 + x + 1 G(x) = x^8+x^2+x+1 G(x)=x8+x2+x+1
此时 G ( x ) G(x) G(x)一共为9位,其二进制比特串为:100000111
此时CRC校验码位数应为8(比生成多项式的位数少1)

代码实现

模块代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/05/06 19:37:47
// Design Name: wangmeinan
// Module Name: CRC_code
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module crc_8(
        input clk,
        input rst_n,
        input [63:0]din,
        input crc_start,
        output reg crc_vld,
        output wire[7:0]crc_o,
        output reg[5:0] crc_cnt
    );
    parameter [7:0]poly = 8'b0000_0111;
    //除数为1_0000_0111,第一位为1。
    //因为我们需要的余数,最高为8位,所以实际上最高位我们用不到。于是为节省资源,可在运算时将整个数组向前移动一位。并且在存储除数时,可忽略第一位1。
    //运算时我们先判断被除数最高位是否为1,若为1,剩下8位进行亦或操作后,将其填入寄存器前8位。若为0,则将被除数直接落下,填入寄存器前8位。
    reg [71:0]din_temp;//寄存器,存储din值进行处理
    reg crc_en;//运算使能信号,当该信号使能时,将din_temp向左移动一格,将其与poly进行“模2除法”
   // reg [5:0]crc_cnt;
    
    //一般一个语言块,我们只给一个变量赋值
    always@(posedge clk or negedge rst_n)
    if(~rst_n)
        din_temp <= 72'b0;
    else if(crc_start)
        din_temp <= {din,8'b0};//补0操作
     else if(crc_en)
     begin
        din_temp[71:64] <= din_temp[71]? din_temp[70:63]^poly : din_temp[70:63];//实际上,这步我们做了运算与移动操作
        din_temp[63:0] <= { din_temp[62:0],1'b0};//最后位补0
      end
      
      //使能模块
      always@(posedge clk or negedge rst_n)
      if(~rst_n)
            crc_en <= 1'b0;
      else if(crc_start)
            crc_en <= 1'b1;
      else if(crc_cnt == 6'd63)
            crc_en <= 1'b0;
       
       //计数器模块
       always@(posedge clk or negedge rst_n)
       if(~rst_n)
            crc_cnt <= 6'd0;
        else if(crc_start)
            crc_cnt <= 6'd0;
        else if(crc_en)
            crc_cnt <= crc_cnt +1'b1;
        
        //输出标志位
        always@(posedge clk or negedge rst_n)
        if(~rst_n)
            crc_vld <= 1'b0;
         else if(crc_en && crc_cnt == 6'd63)
            crc_vld <= 1'b1;
         else
            crc_vld <= 1'b0;
            
        assign crc_o = din_temp[71:64];
         
        
           
endmodule

    

TB代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/05/08 13:38:26
// Design Name: 
// Module Name: testbench
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module tb_crc(
    );
          reg  clk;
          reg rst_n;
          reg [63:0]din;
          reg  crc_start;
          wire crc_vld;
          wire[7:0]crc_o;
          wire[5:0]crc_cnt;
always #10 clk = ~clk;

initial begin
    clk = 1'b0; 
    rst_n =1'b0;
    din = 64'd0;
    crc_start = 1'b0;
    
    //给激励信号
    #103 //错开clk和rst的沿
        rst_n = 1'b1;
     @(posedge clk);//等待一个时钟的上升沿
     @(posedge clk)begin
            crc_start = 1'b1;
            din = 64'h1234567812345678;//8x4x4 = 64位二进制位
        end
      @(posedge clk)
        crc_start = 1'b0;
       
       //等结果
       @(posedge crc_vld)begin
        #2//为了采集准确的上升沿,crc_o向后延
            if(crc_o == 8'h12)//16进制1个数4位
                $fdisplay ("Correct");
            else
                $fdisplay ("Incorrect");
       end 
       
      // #1000
        //    $stop;
    end
crc_8 crc_8_u(
          .clk(clk),
          . rst_n(rst_n),
          .din(din),
          .crc_start(crc_start),
          .crc_vld(crc_vld),
          .crc_o(crc_o),
          .crc_cnt(crc_cnt)
);
endmodule

仿真截图

最后让我们来一张仿真截图吧
可见最后效验码结果位为h93

引用

[1]: http://adrai.github.io/flowchart.js/# 茶乡浪子. 最通俗的CRC校验原理剖析

提示:这里可以添加学习目标
例如:一周掌握 Java 入门知识


评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值