RTL设计(7)- CRC校验码产生器

差错检测

在数据传输过程中,无论传输系统的设计再怎么完美,差错总会存在,这种差错可能会导致在链路上传输的一个或者多个帧被破坏(出现比特差错,0变为1,或者1变为0),从而接受方接收到错误的数据。为尽量提高接受方收到数据的正确率,在接收方接收数据之前需要对数据进行差错检测,当且仅当检测的结果为正确时接收方才真正收下数据。

在一个 [p位二进制数据序列] 之后附加一个 [r位二进制校检码] ,构成一个总长为 [p+r的二进制序列] 。附加在数据序列之后的这个校检码与p位二进制序列之间存在一个特定的关系,如果因干扰等原因使得数据序列中的一些位发生错误,这种特性的关系就会破坏。因此,可以通过检查该特定关系,实现对接收到的数据正确性的检验。

检测的方式有多种,常见的有奇偶校验、累加和校检和循环冗余校验等。
(1)奇偶校验,检测错误概率大约为50%,简单但传输效率低。奇偶校验多用于低速度数据通讯,如RS232。
(2)累加和校验,检测错误概率大概为1/256,实现简单,也被广泛的采用。
(3)CRC校检,只要选择的除数多项式位数足够多,检测错误的概率几乎不存在。

循环冗余校验

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

CRC校验码产生器

不同的生成多项式有不同的检错能力,选用CRC-16生成多项式:
G(X) = x16+x15 + x2 + 1

CRC校验码产生器分为两种:串行CRC校验码产生器和并行CRC校验码产生器。

通常,CRC校验码的值可以通过线性移位寄存器和异或门求得,线性移位寄存器一次移一位,完成除法功能,异或门完成不带进位的减法功能。如果商数为1,则从被除数的高阶位异或除数,同时移位寄存器右移一位,准备为被除数的较低位进行运算;如果商数为0,则移位寄存器直接右移一位。

电路结构图如下所示:
在这里插入图片描述

串行CRC校验码产生器每个时钟移1位,并行CRC校验码产生器每个时钟周期移16位(用组合逻辑实现)。

扫描二维码关注公众号,回复: 12271726 查看本文章

在如下网站可以在线计算CRC:
http://www.ip33.com/crc.html
在这里插入图片描述
00AA -> 03FC
AA89 -> 7F3F
2853 -> F1EA

并行CRC-16检验码产生器

parallelCRC16.v

`timescale 1ns / 1ps

// Company: 
// Engineer: 
// 
// Create Date: 2020/12/14
// Author Name: Sniper
// Module Name: parallelCRC16
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 


module parallelCRC16
#(
    parameter DATA_WIDTH = 16
)
(
    input clk,
    input rst_n,
	input [DATA_WIDTH-1:0] data,
	output reg [15:0] crc_out
);

reg [15:0] crc_temp;
reg temp;

integer i;


//CRC out
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		crc_out <= 0;
	else
		crc_out <= crc_temp;
end


//CRC temp
//G(X) = x16 + x15 + x2 + 1
always@(*)
begin
	if(!rst_n)
    begin
		crc_temp = 0;
		temp = 0;
    end
	else
    begin
        crc_temp = 0;//init
        for(i=DATA_WIDTH-1; i>=0; i=i-1)
        begin
            temp = data[i] ^ crc_temp[15];

            crc_temp[15] = temp ^ crc_temp[14];
            crc_temp[14:3] = crc_temp[13:2];
            crc_temp[2] = temp ^ crc_temp[1];
            crc_temp[1] = crc_temp[0];
            crc_temp[0] = temp;
        end
    end
end


endmodule

tb_parallelCRC16.v

`timescale 1ns / 1ps

// Company:
// Engineer:
//
// Create Date: 2020/12/14
// Author Name: Sniper
// Module Name: tb_parallelCRC16
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//


module tb_parallelCRC16;

//parameter
parameter DATA_WIDTH = 16;


//input
reg clk;
reg rst_n;
reg [DATA_WIDTH-1:0] data;


//output
wire [15:0] crc_out;



initial
begin
    clk = 0;
    rst_n = 0;
    data[DATA_WIDTH-1:0] = 0;

	#100;
    rst_n = 1;

    @(posedge clk);
    data <= 16'h00AA;
    @(posedge clk);
    data <= 16'hAA89;
    @(posedge clk);
    data <= 16'h2853;




end

//clock
always #5 clk = ~clk;



//DUT
parallelCRC16 
#(
    .DATA_WIDTH(DATA_WIDTH)
)
DUT
(
    .clk(clk),
    .rst_n(rst_n),
    .data(data),
    .crc_out(crc_out)
);

initial
begin
    $dumpfile("tb_parallelCRC16.vcd");
    $dumpvars(0,tb_parallelCRC16);
end

initial #1000 $finish;

endmodule

运行结果

vcs -R parallelCRC16.v tb_parallelCRC16.v

CRC结果在数据输入的下一个时钟周期产生。
在这里插入图片描述

串行CRC-16检验码产生器

serialCRC16.v

`timescale 1ns / 1ps

// Company: 
// Engineer: 
// 
// Create Date: 2020/12/14
// Author Name: Sniper
// Module Name: serialCRC16
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 


module serialCRC16
#(
    parameter DATA_WIDTH = 16
)
(
    input clk,
    input rst_n,
    input clear,
	input data,
	output reg [15:0] crc_out
);

wire temp;
assign temp = data ^ crc_out[15];

//CRC out
//G(X) = x16 + x15 + x2 + 1
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		crc_out <= 0;
	else if(clear)
        crc_out <= 0;
    else
    begin
        crc_out[15] <= temp ^ crc_out[14];
        crc_out[14:3] <= crc_out[13:2];
        crc_out[2] <= temp ^ crc_out[1];
        crc_out[1] <= crc_out[0];
        crc_out[0] <= temp;
    end
end



endmodule

tb_serialCRC16.v

`timescale 1ns / 1ps

// Company:
// Engineer:
//
// Create Date: 2020/12/15
// Author Name: Sniper
// Module Name: tb_serialCRC16
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//


module tb_serialCRC16;

//parameter
parameter DATA_WIDTH = 16;


//input
reg clk;
reg rst_n;
reg clear;
reg data;


//output
wire [15:0] crc_out;


task write_data(input [DATA_WIDTH-1:0] data_all);
begin
    @(posedge clk);
    clear <= 1;

    for(int i=DATA_WIDTH-1;i>=0;i=i-1)
    begin
        @(posedge clk);
        data <= data_all[i];
        clear <= 0;
    end
    repeat(3) @(posedge clk) clear <= 1;
end
endtask


initial
begin
    clk = 0;
    rst_n = 0;
    clear = 0;
    data = 0;

	#100;
    rst_n = 1;

    repeat(3) @(posedge clk);

    write_data(16'h00AA);
    write_data(16'hAA89);
    write_data(16'h2853);



end

//clock
always #5 clk = ~clk;



//DUT
serialCRC16 
#(
    .DATA_WIDTH(DATA_WIDTH)
)
DUT
(
    .clk(clk),
    .rst_n(rst_n),
    .clear(clear),
    .data(data),
    .crc_out(crc_out)
);

initial
begin
    $dumpfile("tb_serialCRC16.vcd");
    $dumpvars(0,tb_serialCRC16);
end

initial #1000 $finish;

endmodule

运行结果

vcs -R -sverilog serialCRC16.v tb_serialCRC16.v

CRC结果在数据最后一个bit输入的下一个时钟周期产生。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/meng1506789/article/details/111191638