一、推导过程以及电路生成
crc校验码举例请看CRC校验码举例
规律:生成多项式除了最高项,其余哪个项的系数为1,相应的寄存器前面插一个异或逻辑。
二、上例对应代码
上例输入数据为1bit,CRC输出5bit
module crc(
input data_in,//即上图中的ai
input crc_en,
output [4:0] crc_out,//输出5位crc校验码
input rst,
input clk);
reg [4:0] lfsr_q;//寄存器输出端Q
reg [4:0] lfsr_c;//寄存器输入端D,在always语句块里被赋值定义成reg
assign crc_out = lfsr_q;
always @(*) begin
lfsr_c[0] = lfsr_q[4] ^ data_in;//C0的输入端等于C4的输出端与输出的下一bit数据的异或
lfsr_c[1] = lfsr_q[0] ;
lfsr_c[2] = lfsr_q[1] ^ lfsr_q[4]^ data_in;
lfsr_c[3] = lfsr_q[2];
lfsr_c[4] = lfsr_q[3] ^ lfsr_q[4]^ data_in; //C4的输入端等于lfsr_q[4] ^ data_in的结果与C3异或
end
always @(posedge clk, posedge rst) begin
if(rst) begin
lfsr_q <= {5{1'b0}};//初值设为0
end
else begin
lfsr_q <= crc_en ? lfsr_c: lfsr_q;//crc使能有效,寄存器更新值,否则保持。
end
end
endmodule
testbench:
module tb_crc5();
reg clk;
reg rst;
reg data_in;
reg crc_en;
wire [4:0] crc_out;
always #10 clk = ~clk;
crc u_crc(
.data_in(data_in),
.crc_en(crc_en),
.crc_out(crc_out),
.rst(rst),
.clk(clk)
);
initial begin
clk = 0;
rst = 1;
data_in = 1;
crc_en = 0;
#40
rst = 0;
crc_en = 1;
#400
$finish;
end
endmodule
与使用CRC校验码举例文中的计算方法的计算结果一致。可自行计算。该方法仅适用于初值为0的情况。
输入第一bit1(data_in初值),即计算1,crc校验码:15
在线crc计算验证一下:
输入第二bit1,即计算11,crc校验码:0a
以此类推。可知仿真结果是正确。
该例子的输入为1bit数据,若不是串行输入,则需要进行化简,可通过https://www.easics.com/crctool/设置生成多项式、输入数据位宽从而选择verilog代码并下载。
更多例子,如乐鑫笔试题,请参考此处。
三、选择实验所需的crc多项式
由该系列前两篇文章可知,在串口通信中,接收模块接收8bit串行数据,转化为并行数据输出,发送模块把接收到的并行数据转化为串行数据输出。因此需将输入数据位宽设置为8。为了校验更准确,将校验码位数设置多一些,此处选择16位crc码。
// 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: 8
//
// Info : tools@easics.be
// http://www.easics.com
module CRC16_D8(
input [7:0] Data,
input [15:0] CRC_in,
output [15:0] CRC_out
);
// polynomial: x^16 + x^12 + x^5 + 1
// data width: 8
// convention: the first serial bit is D[7]
function [15:0] nextCRC16_D8;
input [7:0] Data;
input [15:0] crc;
reg [7:0] d;
reg [15:0] c;
reg [15:0] newcrc;
begin
d = Data;
c = crc;
newcrc[0] = d[4] ^ d[0] ^ c[8] ^ c[12];
newcrc[1] = d[5] ^ d[1] ^ c[9] ^ c[13];
newcrc[2] = d[6] ^ d[2] ^ c[10] ^ c[14];
newcrc[3] = d[7] ^ d[3] ^ c[11] ^ c[15];
newcrc[4] = d[4] ^ c[12];
newcrc[5] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[12] ^ c[13];
newcrc[6] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[13] ^ c[14];
newcrc[7] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[14] ^ c[15];
newcrc[8] = d[7] ^ d[3] ^ c[0] ^ c[11] ^ c[15];
newcrc[9] = d[4] ^ c[1] ^ c[12];
newcrc[10] = d[5] ^ c[2] ^ c[13];
newcrc[11] = d[6] ^ c[3] ^ c[14];
newcrc[12] = d[7] ^ d[4] ^ d[0] ^ c[4] ^ c[8] ^ c[12] ^ c[15];
newcrc[13] = d[5] ^ d[1] ^ c[5] ^ c[9] ^ c[13];
newcrc[14] = d[6] ^ d[2] ^ c[6] ^ c[10] ^ c[14];
newcrc[15] = d[7] ^ d[3] ^ c[7] ^ c[11] ^ c[15];
nextCRC16_D8 = newcrc;
end
endfunction
assign CRC_out = nextCRC16_D8(Data,CRC_in);
endmodule
该模块的验证放在数据链路层。