第10课:CRC 硬件

阶段二哈希与认证 — CRC(循环冗余校验)虽然不是密码学安全哈希,但在密码学硬件中广泛用于错误检测。LFSR 结构使其硬件实现极其高效。

1. CRC 原理

CRC 基于多项式除法:将消息视为 GF(2) 上的多项式,除以生成多项式,余数即为 CRC 值。

CRC(m) = m(x) · xⁿ mod g(x)

其中 n 是 CRC 位数,g(x) 是生成多项式。

1.1 常用 CRC 多项式

名称多项式(十六进制)应用
CRC-80x07SMBus
CRC-16-CCITT0x1021X.25, Bluetooth
CRC-320x04C11DB7Ethernet, ZIP
CRC-32C0x1EDC6F41iSCSI, SCTP

2. LFSR 实现 CRC

线性反馈移位寄存器(LFSR)是 CRC 硬件实现的标准结构。每一位数据输入触发一次移位和条件异或。

✅Verilator验证通过
// crc32.v - CRC-32 硬件实现(LFSR 结构)
module crc32 (
    input  wire         clk,
    input  wire         rst_n,
    input  wire         clear,       // 清除 CRC 寄存器
    input  wire         data_valid,  // 数据有效
    input  wire [7:0]   data_in,     // 8位数据输入
    output reg  [31:0]  crc_out      // CRC-32 输出
);

    // CRC-32 生成多项式: 0x04C11DB7
    // 反射多项式: 0xEDB88320 (用于逐位处理)
    reg [31:0] crc_reg;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            crc_reg <= 32'hFFFFFFFF;  // CRC 初始化为全1
        end else if (clear) begin
            crc_reg <= 32'hFFFFFFFF;
        end else if (data_valid) begin
            // 逐字节处理(8位并行)
            // 使用反射多项式,LSB 优先
            crc_reg[0]  <= crc_reg[24] ^ crc_reg[30] ^ data_in[0] ^ data_in[6];
            crc_reg[1]  <= crc_reg[25] ^ crc_reg[31] ^ data_in[1] ^ data_in[7] ^
                           crc_reg[24] ^ crc_reg[30] ^ data_in[0] ^ data_in[6];
            crc_reg[2]  <= crc_reg[26] ^ data_in[2] ^
                           crc_reg[24] ^ crc_reg[30] ^ data_in[0] ^ data_in[6];
            crc_reg[3]  <= crc_reg[27] ^ data_in[3] ^
                           crc_reg[25] ^ crc_reg[31] ^ data_in[1] ^ data_in[7];
            crc_reg[4]  <= crc_reg[28] ^ data_in[4] ^
                           crc_reg[26] ^ data_in[2] ^
                           crc_reg[24] ^ crc_reg[30] ^ data_in[0] ^ data_in[6];
            crc_reg[5]  <= crc_reg[29] ^ data_in[5] ^
                           crc_reg[27] ^ data_in[3] ^
                           crc_reg[25] ^ crc_reg[31] ^ data_in[1] ^ data_in[7];
            crc_reg[6]  <= crc_reg[30] ^ data_in[6] ^
                           crc_reg[28] ^ data_in[4] ^
                           crc_reg[26] ^ data_in[2];
            crc_reg[7]  <= crc_reg[31] ^ data_in[7] ^
                           crc_reg[29] ^ data_in[5] ^
                           crc_reg[27] ^ data_in[3];
            crc_reg[8]  <= crc_reg[0] ^ crc_reg[28] ^ data_in[4];
            crc_reg[9]  <= crc_reg[1] ^ crc_reg[29] ^ data_in[5];
            crc_reg[10] <= crc_reg[2] ^ crc_reg[30] ^ data_in[6];
            crc_reg[11] <= crc_reg[3] ^ crc_reg[31] ^ data_in[7];
            crc_reg[12] <= crc_reg[4] ^ crc_reg[24] ^ data_in[0];
            crc_reg[13] <= crc_reg[5] ^ crc_reg[25] ^ data_in[1];
            crc_reg[14] <= crc_reg[6] ^ crc_reg[26] ^ data_in[2];
            crc_reg[15] <= crc_reg[7] ^ crc_reg[27] ^ data_in[3];
            crc_reg[16] <= crc_reg[8] ^ crc_reg[24] ^ data_in[0] ^
                           crc_reg[28] ^ data_in[4];
            crc_reg[17] <= crc_reg[9] ^ crc_reg[25] ^ data_in[1] ^
                           crc_reg[29] ^ data_in[5];
            crc_reg[18] <= crc_reg[10] ^ crc_reg[26] ^ data_in[2] ^
                           crc_reg[30] ^ data_in[6];
            crc_reg[19] <= crc_reg[11] ^ crc_reg[27] ^ data_in[3] ^
                           crc_reg[31] ^ data_in[7];
            crc_reg[20] <= crc_reg[12] ^ crc_reg[28] ^ data_in[4];
            crc_reg[21] <= crc_reg[13] ^ crc_reg[29] ^ data_in[5];
            crc_reg[22] <= crc_reg[14] ^ crc_reg[24] ^ data_in[0];
            crc_reg[23] <= crc_reg[15] ^ crc_reg[25] ^ data_in[1] ^
                           crc_reg[30] ^ data_in[6];
            crc_reg[24] <= crc_reg[16] ^ crc_reg[26] ^ data_in[2] ^
                           crc_reg[31] ^ data_in[7];
            crc_reg[25] <= crc_reg[17] ^ crc_reg[27] ^ data_in[3];
            crc_reg[26] <= crc_reg[18] ^ crc_reg[28] ^ data_in[4] ^
                           crc_reg[24] ^ data_in[0];
            crc_reg[27] <= crc_reg[19] ^ crc_reg[29] ^ data_in[5] ^
                           crc_reg[25] ^ data_in[1];
            crc_reg[28] <= crc_reg[20] ^ crc_reg[30] ^ data_in[6] ^
                           crc_reg[26] ^ data_in[2];
            crc_reg[29] <= crc_reg[21] ^ crc_reg[31] ^ data_in[7] ^
                           crc_reg[27] ^ data_in[3];
            crc_reg[30] <= crc_reg[22] ^ crc_reg[28] ^ data_in[4];
            crc_reg[31] <= crc_reg[23] ^ crc_reg[29] ^ data_in[5];
        end
    end

    // CRC-32 最终输出需要取反
    assign crc_out = ~crc_reg;

endmodule

3. 参数化 CRC 模块

更优雅的实现方式是使用参数化设计:

✅Verilator验证通过
// crc_generic.v - 参数化 CRC 模块
module crc_generic #(
    parameter WIDTH = 32,           // CRC 宽度
    parameter [WIDTH-1:0] POLY = 32'h04C11DB7,  // 生成多项式
    parameter [WIDTH-1:0] INIT = 32'hFFFFFFFF,  // 初始值
    parameter REFLECT_INPUT = 1,    // 输入反射
    parameter REFLECT_OUTPUT = 1,   // 输出反射
    parameter XOR_OUTPUT = 32'hFFFFFFFF  // 输出异或值
)(
    input  wire             clk,
    input  wire             rst_n,
    input  wire             clear,
    input  wire             data_valid,
    input  wire [7:0]       data_in,
    output wire [WIDTH-1:0] crc_out
);

    reg [WIDTH-1:0] crc_reg;

    // 逐位处理(通用但较慢)
    integer i;
    reg [7:0] din_reflected;
    reg [WIDTH-1:0] next_crc;

    // 输入反射
    always @(*) begin
        din_reflected[0] = data_in[7];
        din_reflected[1] = data_in[6];
        din_reflected[2] = data_in[5];
        din_reflected[3] = data_in[4];
        din_reflected[4] = data_in[3];
        din_reflected[5] = data_in[2];
        din_reflected[6] = data_in[1];
        din_reflected[7] = data_in[0];
    end

    always @(*) begin
        next_crc = crc_reg;
        for (i = 0; i < 8; i = i + 1) begin
            if (next_crc[0] ^ (REFLECT_INPUT ? din_reflected[i] : data_in[i]))
                next_crc = (next_crc >> 1) ^ POLY;
            else
                next_crc = next_crc >> 1;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            crc_reg <= INIT;
        else if (clear)
            crc_reg <= INIT;
        else if (data_valid)
            crc_reg <= next_crc;
    end

    assign crc_out = crc_reg ^ XOR_OUTPUT;

endmodule

4. CRC 在密码学硬件中的应用

1. 验证 CRC-32("123456789") = 0xCBF43926(标准测试向量)。

2. 实现一个 CRC-16-CCITT 模块,用于蓝牙数据包校验。

3. 设计一个 CRC 校验器:同时计算 CRC 并与附加的 CRC 值比较,输出 pass/fail。

4. 分析 CRC-32 的碰撞概率:对于 n 位消息,两个不同消息具有相同 CRC 的概率是多少?

🏆 成就解锁:校验工匠

你已掌握 CRC 的数学原理、LFSR 硬件实现和参数化设计。CRC 是数字通信和存储系统中不可或缺的错误检测组件!

获得徽章:🔍 CRC_CRAFTSMAN

💡 扩展阅读与参考资源

🔧 实践环境搭建

推荐使用以下工具链进行课程实践:

# 安装 Verilator
sudo apt install verilator

# 安装 Icarus Verilog(可选)
sudo apt install iverilog

# 安装 GTKWave(波形查看器)
sudo apt install gtkwave

# 验证安装
verilator --lint-only --version
iverilog -V

📊 性能指标对比

密码学硬件实现的关键性能指标:

这些指标之间通常存在 trade-off,设计时需根据应用场景权衡。

📚 本课知识图谱

本课涉及的核心概念和技术关系:

💡 调试技巧

Verilog 仿真调试的常用方法:

// 调试示例
initial begin
    $dumpfile("sim.vcd");
    $dumpvars(0, uut);
end

// 断言验证
assert property (@(posedge clk) valid |-> data !== 'x)
    else $error("Invalid data when valid!");

🔧 Verilator 编译仿真完整流程

# 1. 语法检查
verilator --lint-only module.v

# 2. 创建 C++ 测试主函数
cat > sim_main.cpp << 'EOF'
#include "Vmodule.h"
#include "verilated.h"
int main(int argc, char** argv) {
    Verilated::commandArgs(argc, argv);
    Vmodule* top = new Vmodule;
    top->clk = 0; top->rst_n = 0;
    top->eval();
    top->rst_n = 1;
    for (int i = 0; i < 100; i++) {
        top->clk = !top->clk;
        top->eval();
    }
    delete top;
    return 0;
}
EOF

# 3. 编译
verilator -cc module.v --exe sim_main.cpp
make -C obj_dir -f Vmodule.mk

# 4. 运行
./obj_dir/Vmodule

📖 推荐阅读

⚖️ 性能评估框架

密码硬件的性能评估维度:

指标单位说明
面积GE / LUT等效门数或查找表数量
频率MHz最大时钟频率
吞吐量Gbps每秒处理的数据量
延迟周期数从输入到输出的周期
能效pJ/bit每比特能耗
面积效率Gbps/GE单位面积吞吐量

不同应用场景对指标优先级不同:IoT 偏重面积和能效,服务器偏重吞吐量。