关于 CRC 校验原理及 C 语言实现,尚为网曾写过相应的文章——循环冗余检验 (CRC) 算法原理及 C 语言实现,为了加快 CRC 的计算速度,也写过CRC 算法查表法推导及 C 语言实现。这里再写一篇 FPGA 并行实现 CRC 校验。
其实,对于 C 语言实现 CRC 校验,只需要弄懂 CRC 的原理和处理逻辑就可以了,而 FPGA 实现 CRC 则是通过其内部的数字器件,如触发器来并行处理 CRC 校验的。例如,LFSR(Linear Feedback Shift Register,线性反馈移位寄存器)计算 CRC,可以用多项式 G(x)表示,G(x) = X16+X12+X5+1 模型可如下图所示。
根据上图可以看出 FPGA 实现 CRC 校验,其实就是靠 FPGA 内部的触发器和门器件实现的,具体原理就不再详述,这里重点看一下 Verilog HDL 的实现。
FPGA 并行实现 CRC 的流程
(1)FPGA 并行计算 crc 用 verilog 语言描述是复杂繁琐的,所以可以使用在线工具生成 verilog 或者 VHDL 模板(在线工具见文末),模板生成的代码稍加修改即可使用。
(2)本次校验模型为 G(x) = X16+X12+X5+1。在线工具中操作相关选项生成模板。
模板.v 文件如下:
//////////////////////////////////////////////////////////////////////////////// // Copyright (C) 1999-2008 Easics NV. // This source file may be used and distributed without restriction // provided that this copyright statement is not removed from the file // and that any derivative work contains the original copyright notice // and the associated disclaimer. // // THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED // WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // Purpose : synthesizable CRC function // * polynomial: x^16 + x^12 + x^5 + 1 // * data width: 16 // // Info : tools@easics.be // http://www.easics.com //////////////////////////////////////////////////////////////////////////////// module CRC16_D16; // polynomial: x^16 + x^12 + x^5 + 1 // data width: 16 // convention: the first serial bit is D[15] function [15:0] nextCRC16_D16; input [15:0] Data; input [15:0] crc; reg [15:0] d; reg [15:0] c; reg [15:0] newcrc; begin d = Data; c = crc; newcrc[0] = d[12] ^ d[11] ^ d[8] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[8] ^ c[11] ^ c[12]; newcrc[1] = d[13] ^ d[12] ^ d[9] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[9] ^ c[12] ^ c[13]; newcrc[2] = d[14] ^ d[13] ^ d[10] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[10] ^ c[13] ^ c[14]; newcrc[3] = d[15] ^ d[14] ^ d[11] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[11] ^ c[14] ^ c[15]; newcrc[4] = d[15] ^ d[12] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[12] ^ c[15]; newcrc[5] = d[13] ^ d[12] ^ d[11] ^ d[9] ^ d[8] ^ d[5] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[5] ^ c[8] ^ c[9] ^ c[11] ^ c[12] ^ c[13]; newcrc[6] = d[14] ^ d[13] ^ d[12] ^ d[10] ^ d[9] ^ d[6] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[6] ^ c[9] ^ c[10] ^ c[12] ^ c[13] ^ c[14]; newcrc[7] = d[15] ^ d[14] ^ d[13] ^ d[11] ^ d[10] ^ d[7] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[7] ^ c[10] ^ c[11] ^ c[13] ^ c[14] ^ c[15]; newcrc[8] = d[15] ^ d[14] ^ d[12] ^ d[11] ^ d[8] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[8] ^ c[11] ^ c[12] ^ c[14] ^ c[15]; newcrc[9] = d[15] ^ d[13] ^ d[12] ^ d[9] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[9] ^ c[12] ^ c[13] ^ c[15]; newcrc[10] = d[14] ^ d[13] ^ d[10] ^ d[9] ^ d[5] ^ c[5] ^ c[9] ^ c[10] ^ c[13] ^ c[14]; newcrc[11] = d[15] ^ d[14] ^ d[11] ^ d[10] ^ d[6] ^ c[6] ^ c[10] ^ c[11] ^ c[14] ^ c[15]; newcrc[12] = d[15] ^ d[8] ^ d[7] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[7] ^ c[8] ^ c[15]; newcrc[13] = d[9] ^ d[8] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[8] ^ c[9]; newcrc[14] = d[10] ^ d[9] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[9] ^ c[10]; newcrc[15] = d[11] ^ d[10] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[10] ^ c[11]; nextCRC16_D16 = newcrc; end endfunction endmodule
(3)修改模板最终如下:
`timescale 1ns/1ps module crc16_test ( input wire i_clk , //时钟; input wire i_rst_n , //同步复位; input wire i_din_valid , //输入数据有效; input wire [15:0] i_din , //输入数据; output wire o_dout_valid , //输出 CRC 值有效; output wire [15:0] o_dout //输出 CRC; ); reg [15:0] r_dout; wire [15:0] d; wire [15:0] c; assign d = i_din; assign c = r_dout; always @(posedge i_clk) begin if (~i_rst_n) r_dout <= 16'hffff; //初始值为 ffff; else if (i_din_valid) begin //计算逻辑; r_dout[0] = d[12] ^ d[11] ^ d[8] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[8] ^ c[11] ^ c[12]; r_dout[1] = d[13] ^ d[12] ^ d[9] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[9] ^ c[12] ^ c[13]; r_dout[2] = d[14] ^ d[13] ^ d[10] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[10] ^ c[13] ^ c[14]; r_dout[3] = d[15] ^ d[14] ^ d[11] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[11] ^ c[14] ^ c[15]; r_dout[4] = d[15] ^ d[12] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[12] ^ c[15]; r_dout[5] = d[13] ^ d[12] ^ d[11] ^ d[9] ^ d[8] ^ d[5] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[5] ^ c[8] ^ c[9] ^ c[11] ^ c[12] ^ c[13]; r_dout[6] = d[14] ^ d[13] ^ d[12] ^ d[10] ^ d[9] ^ d[6] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[6] ^ c[9] ^ c[10] ^ c[12] ^ c[13] ^ c[14]; r_dout[7] = d[15] ^ d[14] ^ d[13] ^ d[11] ^ d[10] ^ d[7] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[7] ^ c[10] ^ c[11] ^ c[13] ^ c[14] ^ c[15]; r_dout[8] = d[15] ^ d[14] ^ d[12] ^ d[11] ^ d[8] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[8] ^ c[11] ^ c[12] ^ c[14] ^ c[15]; r_dout[9] = d[15] ^ d[13] ^ d[12] ^ d[9] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[9] ^ c[12] ^ c[13] ^ c[15]; r_dout[10] = d[14] ^ d[13] ^ d[10] ^ d[9] ^ d[5] ^ c[5] ^ c[9] ^ c[10] ^ c[13] ^ c[14]; r_dout[11] = d[15] ^ d[14] ^ d[11] ^ d[10] ^ d[6] ^ c[6] ^ c[10] ^ c[11] ^ c[14] ^ c[15]; r_dout[12] = d[15] ^ d[8] ^ d[7] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[7] ^ c[8] ^ c[15]; r_dout[13] = d[9] ^ d[8] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[8] ^ c[9]; r_dout[14] = d[10] ^ d[9] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[9] ^ c[10]; r_dout[15] = d[11] ^ d[10] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[10] ^ c[11]; end end reg r_dout_valid = 0; always @(posedge i_clk) //输入数据在一个时钟内完成 CRC 计算,下一个时钟输出; begin r_dout_valid <= i_din_valid; end assign o_dout_valid = r_dout_valid; assign o_dout = r_dout ; endmodule // end the crc16_test model;
FPGA 并行实现 CRC 仿真分析
仿真结果和在线工具计算结果进行比较,在线工具地址:http://www.ip33.com/crc.html。
(1)编写 tb 文件,对代码进行测试,测试结果如下图所示:
(2)在线校验。输入需要校验的数据,选择参数模型,输入初始值(此次 crc 结果的前一个 crc 值,代码中初始化为 ffff)。可以对比发现计算无误。
第一次计算 0011(初始值为 ffff),结果为 1f1f。
第二次计算 0013(初始值为 1f1f),结果为 d2c1。
FPGA 并行实现 CRC 占用资源及时序分析
本次综合参考芯片为:xc7k325tffg676-2
(1)资源使用量如下图所示。
(2)模块时钟约束为 100M,裕量如下图所示,满足时序要求。
Verilog HDL/VHDL 并行实现 CRC 在线生成工具
CRC 校验的原理、算法、电路结构和 verilog 代码实现
扫码关注尚为网微信公众号
原创文章,作者:sunev,如若转载,请注明出处:https://www.sunev.cn/embedded/986.html