> 技术文档 > CRC16校验原理与Verilog实现详解_crc16校验码的原理

CRC16校验原理与Verilog实现详解_crc16校验码的原理


一、CRC校验基础概念

CRC(Cyclic Redundancy Check,循环冗余校验)是一种常用的数据校验方法,广泛应用于通信协议、存储系统等地方。CRC16特指生成16位校验码的CRC算法。

1.1 CRC核心特点

  • 检错能力强:可检测所有单比特和双比特错误

  • 计算效率高:适合硬件实现

  • 灵活性好:可通过不同多项式适应各种场景

1.2 常见CRC16标准

标准名称 多项式(十六进制) 初始值 输入反转 输出反转 CRC-16-IBM 0x8005 0x0000 Yes Yes CRC-16-CCITT 0x1021 0xFFFF No No CRC-16-MODBUS 0x8005 0xFFFF Yes Yes CRC-16-DNP 0x3D65 0x0000 Yes Yes

二、CRC16算法原理

2.1 基本计算过程

  1. 初始化寄存器赋初值(通常全0或全1)

  2. 数据输入:逐位或逐字节处理

  3. 多项式除法:通过异或运算实现

  4. 结果输出:最终寄存器值即为CRC

2.2 关键运算图解

数据位:D[7:0]
CRC寄存器:C[15:0]

计算步骤:
1.D与C低8位异或 → T[7:0] = D ^ C[7:0]

2.将T移入CRC寄存器高8位,同时将原高8位移至低8位(等效8次位移)

3.对CRC寄存器进行8次位移和条件异或(完成剩余8次位移)

三、Verilog RTL实现(带详细注释)

3.1 参数化设计核心模块

verilog

/**
 * CRC16校验模块(参数化设计)
 * @param POLY   生成多项式(默认0x8005)
 * @param INIT   初始值(默认0x0000)
 * @param REFIN  输入数据是否位反转(默认1)
 * @param REFOUT 输出结果是否位反转(默认1)
 */
module crc16 #(
    parameter POLY = 16\'h8005,
    parameter INIT = 16\'h0000,
    parameter REFIN = 1,
    parameter REFOUT = 1
)(
    input clk,          // 系统时钟
    input rst_n,        // 低有效复位
    input [7:0] data_in,// 输入数据(8bit/cycle)
    input data_valid,   // 数据有效信号
    output reg [15:0] crc_out,  // CRC16校验结果
    output reg crc_ready        // 校验完成标志
);

    // 当前CRC计算值寄存器
    reg [15:0] crc_reg;
    
    // 输入数据位反转处理
    wire [7:0] data = REFIN ? {data_in[0], data_in[1], data_in[2], data_in[3], 
                             data_in[4], data_in[5], data_in[6], data_in[7]} 
                           : data_in;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            // 复位初始化
            crc_reg <= INIT;
            crc_ready <= 1\'b0;
        end 
        else if (data_valid) begin
            // 数据有效时进行计算
            crc_ready <= 1\'b0;
            
            // 步骤1:输入数据与CRC高字节异或
            crc_reg[15:8] <= crc_reg[7:0] ^ data;
            crc_reg[7:0] <= crc_reg[15:8];
            
            // 步骤2:8次位移和条件异或
            for (int i=0; i<8; i=i+1) begin
                if (crc_reg[15]) begin  // 最高位为1时执行多项式异或
                    crc_reg <= {crc_reg[14:0], 1\'b0} ^ POLY;
                end else begin          // 否则仅位移
                    crc_reg <= {crc_reg[14:0], 1\'b0};
                end
            end
        end
        else begin
            // 数据无效时标志计算完成
            crc_ready <= 1\'b1;
        end
    end

    // 输出结果位反转处理
    assign crc_out = REFOUT ? {crc_reg[0], crc_reg[1], crc_reg[2], crc_reg[3],
                             crc_reg[4], crc_reg[5], crc_reg[6], crc_reg[7],
                             crc_reg[8], crc_reg[9], crc_reg[10], crc_reg[11],
                             crc_reg[12], crc_reg[13], crc_reg[14], crc_reg[15]} 
                           : crc_reg;

endmodule

3.2 单周期并行计算优化

verilog

/**
 * 单周期CRC16并行计算模块
 * 适用于高速数据处理场景
 */
module crc16_parallel #(
    parameter POLY = 16\'h8005
)(
    input [15:0] crc_in,  // 当前CRC值
    input [7:0] data,     // 输入数据
    output [15:0] crc_out // 新CRC值
);
    // 中间计算结果
    wire [15:0] d = {8\'h00, data} ^ crc_in;
    
    // 并行计算逻辑展开
    assign crc_out = {
        d[14:8],                    // bit[15:9]
        d[7]^d[15],                 // bit8
        d[6],                       // bit7
        d[5],                       // bit6  
        d[4],                       // bit5
        d[3],                       // bit4
        d[2],                       // bit3
        d[1],                       // bit2
        d[0]^d[15],                 // bit1
        d[15]^d[14],               // bit0
        d[15]^d[13],               // bit15
        d[15]^d[12],               // bit14
        d[15]^d[11],               // bit13
        d[15]^d[10],               // bit12
        d[15]^d[9],                // bit11
        d[15]^d[8]                 // bit10
    };
endmodule

四、应用场景与实例

4.1 工业通信协议(Modbus RTU)

verilog

// Modbus RTU CRC校验实例
crc16 #(
    .POLY(16\'h8005),
    .INIT(16\'hFFFF),
    .REFIN(1),
    .REFOUT(1)
) modbus_crc (
    .clk(uart_clk),
    .rst_n(sys_rst_n),
    .data_in(rx_data),
    .data_valid(rx_valid),
    .crc_out(modbus_crc_result)
);

// 帧校验逻辑
always @(posedge clk) begin
    if (frame_end) begin
        if (modbus_crc_result == 16\'h0000)
            crc_ok <= 1\'b1;
        else
            crc_error <= 1\'b1;
    end
end

4.2 存储系统数据校验

verilog

// NAND Flash数据校验
crc16 #(
    .POLY(16\'h1021),
    .INIT(16\'hFFFF),
    .REFIN(0),
    .REFOUT(0)
) flash_crc (
    .clk(nand_clk),
    .rst_n(!nand_reset),
    .data_in(flash_data),
    .data_valid(data_rdy),
    .crc_out(flash_crc_result)
);

// 写入时生成CRC
always @(posedge nand_clk) begin
    if (write_en) begin
        flash_data_out <= {user_data, flash_crc_result};
    end
end