FPGA实现CAN通信CRC校验
检验本质上是CRC检验生成多项式
理解CRC校验,首先需要了解生成多项式的概念,生成多项式也就是发送方和接受方约定的一个除数而已,发送方和接收方都使用这一个相同的除数进行模2运算,计算结果相同则说明传输数据没有问题,而如果计算结果不同可能传输的数据就出现了问题,目的就是为了保证数据传输的可靠性。
上面提到的模二计算本质上就是异或运算,相同的位为0,不同的位为1,也就是不考虑进位、错位的二进制加减法运算,例如:10011011 + 11001010 = 01010001.
常见的生成多项式:
CRC8 = X8 + X5 + X4 +1
CRC12 = X12 + X11 + X3 + X2 + 1
CRC16 = X16 + X15 + X5 +1
CRC15 =X15 + X14 + X10 + X8 + X7 + X4 + X3 + X0.
CRC32 = X32 + X26 + X23 + X22 + X16 + X12 + X11 + X10 + X8 + X7 + X5 + X4 + X2 + X1 + 1
每一个生成多项式都是与一个代码相对应的,比如CRC8对应的代码就是100110001
比如在实现网络通信时C语言校验实现如下
uint cal_crc(uchar *ptr, uchar len) {
uint crc;
uchar i;
crc=0;
while (len--!=0) {
for (i=0x80; i!=0; i/=2) {
if ((crc&0x8000)!=0){
crc*=2; crc^=0x1021;
} else crc*=2;
if ((*ptr&i)!=0)
crc^=0x1021;
}
ptr++;
}
return(crc);
}
用FPGA实现32位网络通信CRC检验码并行计算如下
`timescale 1ns / 1ps
module crc (Clk, Reset, Data, Enable, Crc,CrcNext);
parameter Tp = 1;
input Clk;
input Reset;
input [0:3] Data;
input Enable;
output [31:0] Crc;
reg [31:0] Crc;
output [31:0] CrcNext;
assign CrcNext[0] = Enable & (Data[0] ^ Crc[28]);
assign CrcNext[1] = Enable & (Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29]);
assign CrcNext[2] = Enable & (Data[2] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[30]);
assign CrcNext[3] = Enable & (Data[3] ^ Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30] ^ Crc[31]);
assign CrcNext[4] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[0];
assign CrcNext[5] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[1];
assign CrcNext[6] = (Enable & (Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30])) ^ Crc[ 2];
assign CrcNext[7] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[3];
assign CrcNext[8] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[4];
assign CrcNext[9] = (Enable & (Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30])) ^ Crc[5];
assign CrcNext[10] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[6];
assign CrcNext[11] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[7];
assign CrcNext[12] = (Enable & (Data[2] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[30])) ^ Crc[8];
assign CrcNext[13] = (Enable & (Data[3] ^ Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30] ^ Crc[31])) ^ Crc[9];
assign CrcNext[14] = (Enable & (Data[3] ^ Data[2] ^ Crc[30] ^ Crc[31])) ^ Crc[10];
assign CrcNext[15] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[11];
assign CrcNext[16] = (Enable & (Data[0] ^ Crc[28])) ^ Crc[12];
assign CrcNext[17] = (Enable & (Data[1] ^ Crc[29])) ^ Crc[13];
assign CrcNext[18] = (Enable & (Data[2] ^ Crc[30])) ^ Crc[14];
assign CrcNext[19] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[15];
assign CrcNext[20] = Crc[16];
assign CrcNext[21] = Crc[17];
assign CrcNext[22] = (Enable & (Data[0] ^ Crc[28])) ^ Crc[18];
assign CrcNext[23] = (Enable & (Data[1] ^ Data[0] ^ Crc[29] ^ Crc[28])) ^ Crc[19];
assign CrcNext[24] = (Enable & (Data[2] ^ Data[1] ^ Crc[30] ^ Crc[29])) ^ Crc[20];
assign CrcNext[25] = (Enable & (Data[3] ^ Data[2] ^ Crc[31] ^ Crc[30])) ^ Crc[21];
assign CrcNext[26] = (Enable & (Data[3] ^ Data[0] ^ Crc[31] ^ Crc[28])) ^ Crc[22];
assign CrcNext[27] = (Enable & (Data[1] ^ Crc[29])) ^ Crc[23];
assign CrcNext[28] = (Enable & (Data[2] ^ Crc[30])) ^ Crc[24];
assign CrcNext[29] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[25];
assign CrcNext[30] = Crc[26];
assign CrcNext[31] = Crc[27];
always @ (posedge Clk or posedge Reset)
begin
if (Reset)
Crc <= #1 32'hffffffff;
else if(Enable)
Crc <= CrcNext;
end
endmodule
在实现CAN通信时是采用15位校验码。由于CAN在数据的收发中存在插入一个翻转电平,这个翻转电平实际上也是参与CRC计算的,因此采用并行的计算方法存在一定的问题,根据CAN2.0协议提供的串行实现方案来用FPGA实现。
CAN协议C语言实现方案如下
U16 Can_FD_Analyzer::MakeCRC15(std::vector<BitState> &bits, U32 num_bits)
{
//X15 + X14 + X10 + X8 + X7 + X4 + X3 + X0.
U16 CRC[15] = { 0 };
for (U32 i = 0; i < num_bits; i++) {
U32 DoInvert = (bits[i] == mSettings->Recessive()) ^ CRC[14]; //XOR required?
CRC[14] = (CRC[13] ^ DoInvert); //14
CRC[13] = CRC[12];
CRC[12] = CRC[11];
CRC[11] = CRC[10];
CRC[10] = (CRC[9] ^ DoInvert); //10
CRC[9] = CRC[8];
CRC[8] = (CRC[7] ^ DoInvert); //8
CRC[7] = (CRC[6] ^ DoInvert); //7
CRC[6] = CRC[5];
CRC[5] = CRC[4];
CRC[4] = (CRC[3] ^ DoInvert); //4
CRC[3] = (CRC[2] ^ DoInvert); //3
CRC[2] = CRC[1];
CRC[1] = CRC[0];
CRC[0] = DoInvert;
}
U16 result = 0; // CRC Result
for (U32 i = 0; i < 15; i++) {
result = result | (CRC[i] << i);
}
return (U16)result;
}
FPGA串行方案实现如下:
这个是采用标准帧格式,8个字节数据的发送。
//
CIC
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
can_data_crc <= 'b0 ;
end else if (can_data_in_en==1'b1) begin
can_data_crc <= {1'b0,in_can_id[11:1],1'b0,2'b0,in_can_dlc,in_can_data};
end
end
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
can_crc_en_flag <= 'b0 ;
end else if (can_data_in_en==1'b1) begin
can_crc_en_flag <= 1'b1 ;
end
else if (can_crc_cont>=can_crc_contdata) begin
can_crc_en_flag <= 1'b0 ;
end
end
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
can_crc_en_flag_t <= 'b0 ;
end else begin
can_crc_en_flag_t <= can_crc_en_flag ;
end
end
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
can_crc_contdata <= 18 ;
end else if (can_data_in_en==1'b1) begin
can_crc_contdata <= 18 + {in_can_dlc,3'b0};
end
else if (can_crc_cont>=can_crc_contdata) begin
can_crc_contdata <= 18 ;
end
end
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
can_crc_cont <= 1'b0 ;
end else if (can_crc_en_flag==1'b1) begin
can_crc_cont <= can_crc_cont + 1'b1 ;
end
else begin
can_crc_cont <= 1'b0 ;
end
end
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
can_data_crc_t <= 1'b0 ;
end else if (can_data_in_en==1'b1) begin
can_data_crc_t <= {1'b0,in_can_id[11:1],1'b0,2'b0,in_can_dlc,in_can_data} ;
end
else if (can_crc_en_flag==1'b1) begin
can_data_crc_t <= {can_data_crc_t[81:0],1'b1} ;
end
else begin
can_data_crc_t <= 1'b0 ;
end
end
reg [14:0] crc_now_data ;
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
crc_now_data <= 1'b0 ;
end else if (can_crc_en_flag==1'b1) begin
crc_now_data <= crc_now_next ;
end
else begin
crc_now_data <= 1'b0 ;
end
end
always @(posedge clk_100m or negedge rst_n )begin
if(rst_n==1'b0) begin
crc_now <= 1'b0 ;
end else if ((can_crc_en_flag==1'b0)&&(can_crc_en_flag_t==1'b1)) begin
crc_now <= crc_now_data ;
end
else if (tx_eof_flag==1'b1) begin
crc_now <= 1'b0 ;
end
end
assign crc_first =can_data_crc_t[82] ^ crc_now_data[14] ;
assign crc_now_next[14] = ( crc_now_data[13] ^ crc_first) ;
assign crc_now_next[13] = crc_now_data[12] ;
assign crc_now_next[12] = crc_now_data[11] ;
assign crc_now_next[11] = crc_now_data[10] ;
assign crc_now_next[10] = ( crc_now_data[9] ^ crc_first) ;
assign crc_now_next[9] = crc_now_data[8] ;
assign crc_now_next[8] = ( crc_now_data[7] ^ crc_first) ;
assign crc_now_next[7] = ( crc_now_data[6] ^ crc_first) ;
assign crc_now_next[6] = crc_now_data[5] ;
assign crc_now_next[5] = crc_now_data[4] ;
assign crc_now_next[4] = ( crc_now_data[3] ^ crc_first) ;
assign crc_now_next[3] = ( crc_now_data[2] ^ crc_first) ;
assign crc_now_next[2] = crc_now_data[1] ;
assign crc_now_next[1] = crc_now_data[0] ;
assign crc_now_next[0] = crc_first ;
上述FPGA实现CAN串行方案和网络32位CRC检验已经过长时间验证,可以使用