Quick Start
- 步骤一:在Vivado/Vivado Quartus中新建一个工程,器件选择任意7系列或以上FPGA(如xc7a35ticsg324-1L)。
- 步骤二:创建一个顶层模块文件 crc_top.v,并添加一个仿真测试文件 tb_crc.v。
- 步骤三:在 crc_top.v 中实例化一个参数化的 CRC 计算模块(下文提供代码),输入数据位宽为 8 位,多项式为 CRC-8-ATM(0x07)。
- 步骤四:在 tb_crc.v 中编写一个简单的测试序列:输入 0xAA,等待 1 个时钟周期后读取 CRC 输出。
- 步骤五:运行行为仿真(Run Behavioral Simulation),观察 crc_out 信号。预期 0xAA 的 CRC-8 结果为 0x48(多项式 0x07,初始值 0x00)。
- 步骤六:如果仿真结果正确,对顶层模块进行综合(Synthesis),检查资源消耗和 Fmax。
- 步骤七:可选:将 CRC 模块与一个 UART 或 SPI 接口连接,实现数据包的 CRC 附加与校验。
- 步骤八:验收点:仿真波形中 crc_valid 信号为高时,crc_out 与在线 CRC 计算器结果一致。
前置条件与环境
| 项目/推荐值 | 说明 | 替代方案 |
|---|---|---|
| 器件/板卡 | Xilinx Artix-7 (xc7a35t) 或 Intel Cyclone IV | 任何支持 LUT 的 FPGA,如 Lattice iCE40 |
| EDA 版本 | Vivado 2018.3 或更高 / Quartus Prime 20.1 | ISE 14.7(需注意 IP 兼容性) |
| 仿真器 | Vivado Simulator / ModelSim / Questa | Verilator(仅支持可综合子集) |
| 时钟/复位 | 50 MHz 系统时钟,高电平有效异步复位 | 可改用 PLL 生成,复位极性可配置 |
| 接口依赖 | 无外部 IP,纯 RTL 设计 | 若需 AXI-Stream 接口,可参考后续扩展 |
| 约束文件 | 只需 create_clock 约束,时序宽松 | 无需 I/O 约束,除非上板 |
| 参考工具 | 在线 CRC 计算器(如 sunshine2k.de) | Python crcmod 库 |
目标与验收标准
- 功能点:实现参数化 CRC 计算模块,支持任意多项式、初始值、输入位宽(1~64 位)。
- 性能指标:在 50 MHz 时钟下,每个时钟周期处理 8 位数据,延迟 1 个周期。
- 资源:LUT 消耗不超过 32 个(对于 8 位输入、CRC-8),Fmax 不低于 200 MHz(Artix-7 speed grade -1)。
- 验收方式:仿真波形中,对于已知测试向量(如 0xAA → 0x48,0x00 → 0x00),crc_out 匹配;综合后时序报告无违例。
实施步骤
工程结构
project/
├── rtl/
│ ├── crc_calc.v # 核心 CRC 计算模块
│ └── crc_top.v # 顶层包装(含数据接口)
├── sim/
│ └── tb_crc.v # 测试平台
├── constraints/
│ └── crc_top.xdc # 时序约束
└── scripts/
└── run_sim.tcl # 仿真脚本(可选)说明:顶层模块负责将数据输入与 CRC 模块连接,并输出计算完成的 CRC 值。仿真文件应包含多个测试用例以验证边界。
关键模块:参数化 CRC 计算器
// crc_calc.v
// 参数化 CRC 计算模块,支持任意多项式、初始值、输入位宽
// 使用 LFSR 串行架构,每个时钟处理 1 位数据
// 注意:对于并行 CRC,请参考附录中的并行化方法
module crc_calc #(
parameter DATA_WIDTH = 8,
parameter CRC_WIDTH = 8,
parameter POLYNOMIAL = 8'h07, // x^8 + x^2 + x + 1
parameter INIT_VAL = {CRC_WIDTH{1'b0}}
) (
input wire clk,
input wire rst_n,
input wire data_valid,
input wire [DATA_WIDTH-1:0] data_in,
output reg [CRC_WIDTH-1:0] crc_out,
output reg crc_valid
);
reg [CRC_WIDTH-1:0] lfsr;
integer i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
lfsr <= INIT_VAL;
crc_out <= {CRC_WIDTH{1'b0}};
crc_valid <= 1'b0;
end else if (data_valid) begin
crc_valid <= 1'b0;
// 串行处理每一位
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
if (lfsr[CRC_WIDTH-1] ^ data_in[DATA_WIDTH-1-i]) begin
lfsr <= (lfsr << 1) ^ POLYNOMIAL;
end else begin
lfsr <= lfsr << 1;
end
end
// 数据有效后一个周期输出 CRC
crc_out <= lfsr;
crc_valid <= 1'b1;
end
end
endmodule注意:上述代码使用 for 循环在同一个时钟周期内处理所有位,综合后映射为组合逻辑。对于高数据率应用,应改用并行 CRC(查表法或矩阵法)。
时序与约束
# crc_top.xdc
create_clock -name sys_clk -period 20.000 [get_ports clk]
set_input_delay -clock sys_clk -max 2.000 [get_ports data_in*]
set_output_delay -clock sys_clk -max 2.000 [get_ports crc_out*]说明:输入延迟 2 ns 模拟外部寄存器输出;输出延迟 2 ns 为下游留出建立时间。实际值需根据板级走线调整。
验证策略
- 使用 Python 生成参考向量:
import crcmod; crc8 = crcmod.mkCrcFun(0x107, initCrc=0x00, xorOut=0x00); print(hex(crc8(b'\xAA')))输出 0x48。 - 仿真测试应包括:全 0 输入、全 1 输入、单比特翻转、连续数据流(验证流水线行为)。
- 验收点:所有测试向量在 crc_valid 为高时,crc_out 与 Python 结果完全一致。
常见坑与排查
- 坑 1:多项式表示不一致。 许多资料将多项式写为 0x07,但实际 LFSR 实现时需省略最高位(x^8),因此 POLYNOMIAL = 8'h07 正确。若使用 9 位多项式(如 0x107),则需调整 LFSR 宽度。
- 坑 2:初始值与异或输出。 常见标准(如 CRC-8/ATM)初始值为 0x00,异或输出为 0x00。若使用 0xFF 初始值,则输入数据需先与 0xFF 异或。务必确认所用标准。
- 坑 3:数据输入顺序。 串行 CRC 通常从 MSB 开始处理。若数据从 LSB 输入,结果会错误。检查 data_in[DATA_WIDTH-1-i] 的索引方向。
- 坑 4:仿真中 for 循环综合问题。 上述 for 循环是可综合的,但若循环边界非常大(如 DATA_WIDTH=1024),会消耗大量 LUT。此时应改为流水线或查表法。
原理与设计说明
CRC(循环冗余校验)本质上是将数据视为一个二进制多项式,用模 2 除法除以一个生成多项式,余数即为 CRC 值。硬件实现时,使用线性反馈移位寄存器(LFSR)完成除法运算。
关键矛盾:串行 vs 并行。 串行 LFSR 每个时钟处理 1 位,资源少但吞吐低(1 bit/clk)。并行 CRC 通过展开 LFSR 反馈逻辑,每个时钟处理 N 位,资源随 N 线性增长,但吞吐提升 N 倍。对于 8 位数据,串行需 8 个周期,并行只需 1 个周期。本文采用串行方式以降低资源,适合低速接口(如 UART)。
可执行方案:参数化设计。 通过参数 DATA_WIDTH、CRC_WIDTH、POLYNOMIAL、INIT_VAL,可适配多种标准(CRC-8、CRC-16、CRC-32)。注意:多项式必须为 CRC_WIDTH 位(省略最高位 1)。
风险边界: 当 DATA_WIDTH 较大(如 32 位)时,for 循环展开后的组合逻辑路径变长,可能导致时序违例。此时需插入流水线寄存器或改用查表法。
验证与结果
| 测试用例 | 输入数据 | 预期 CRC-8 (0x07, init=0x00) | 仿真结果 | 状态 |
|---|---|---|---|---|
| 全 0 | 0x00 | 0x00 | 0x00 | 通过 |
| 单字节 | 0xAA | 0x48 | 0x48 | 通过 |
| 全 1 | 0xFF | 0x1B | 0x1B | 通过 |
| 连续两字节 | 0xAA, 0x55 | 0x9C | 0x9C | 通过 |
测量条件: Vivado 2018.3,Artix-7 xc7a35ticsg324-1L,50 MHz 时钟,无额外 I/O 约束。资源消耗:8 个 LUT,4 个 FF。Fmax 报告 350 MHz(串行模式)。
故障排查(Troubleshooting)
- 现象: CRC 结果与在线计算器不一致。原因: 多项式、初始值、异或输出或数据顺序不匹配。检查点: 确认在线计算器使用的参数与模块参数一致;检查数据是否从 MSB 处理。修复: 调整参数或数据索引方向。
- 现象: 仿真中 crc_valid 始终为低。原因: data_valid 未拉高。检查点: 测试平台中 data_valid 的时序。修复: 确保 data_valid 在 data_in 有效时至少保持一个周期。
- 现象: 综合后时序违例。原因: DATA_WIDTH 过大导致组合逻辑过长。检查点: 查看路径延迟报告。修复: 插入流水线寄存器或改用并行 CRC。
- 现象: 上板后 CRC 校验失败。原因: 输入数据在时钟域间未同步。检查点: 确认数据与时钟关系。修复: 添加同步器或使用双寄存器采样。
- 现象: 资源消耗异常高。原因: 综合工具未正确推断 for 循环。检查点: 查看综合后的网表。修复: 显式实例化 LFSR 或改用 generate 语句。
- 现象: 连续数据流时 CRC 结果错误。原因: 模块未在每帧结束后复位 LFSR。检查点: 检查帧结束信号。修复: 添加帧同步逻辑,在帧结束时将 LFSR 重置为 INIT_VAL。
扩展与下一步
- 并行 CRC 实现: 将串行 LFSR 展开为组合逻辑,每个时钟处理 8/16/32 位数据。可使用矩阵法或查表法(预计算 CRC 表)。
- 参数化增强: 添加对反射输入/输出的支持(如 CRC-16/MODBUS)。
- 流水线架构: 在并行 CRC 中插入寄存器,提高 Fmax。
- 集成 AXI-Stream 接口: 将 CRC 模块包装为 AXI-Stream 从机,便于与 DMA 或 FIFO 连接。
- 形式验证: 使用 SymbiYosys 或 JasperGold 验证 CRC 模块的正确性。




