1 原理
參考文件:CRC校驗 (qq.com)
參考書籍:《計算機網路(第7版)-謝希仁》
1.1 原理簡介
CRC是一種檢錯方法。
在傳送端,先把資料劃分為組,假定每組k個位元。現假定待傳送的資料M = 101001(k = 6)。CRC運算就是在資料M的後面新增供差錯檢測用的n位冗餘碼,然後構成一個幀傳送出去,一共傳送(k + n)位。
資料:k位元
冗餘碼:n位元
這n位冗餘碼可用以下方法得出。在資料M後面新增n個0。得到的(k + n)位的數除以收發雙方事先商定的長度為(n + 1)位的除數p(多項式),得出商是Q而餘數是R(n 位,比P 少一位,校驗碼)。
這個餘數R就作為冗餘碼拼接在資料M的後面傳送出去。這種為了進行檢錯而新增的冗餘碼常稱為幀檢驗序列FCS (Frame Check Sequence) 。
迴圈冗餘檢驗CRC 和幀檢驗序列FCS 並不是同一個概念。CRC 是一種檢錯方法,而FCS 是新增在資料後面的冗餘碼。
模二運算:也就是異或運算,相同異或為0,不同異或為1。
在接收端把接收到的資料以幀為單位進行CRC檢驗:把收到的每一個幀都除以同樣的除數P(模2運算),然後檢查得到的餘數R 。如果在傳輸過程中無差錯,那麼經過CRC檢驗後得出的餘數R肯定是0。
1.2 計算步驟
(1)先選擇CRC多項式,得到多項式的位寬。
例如:選擇G(x) = X4 + X3 + 1,對應的二進位制編碼為:2b'11001,多項式位寬為4。
(2)選擇計算的資料。
(3)計算。
計算方式為:在要傳送的資料幀(假設為m位)後面加上k-1位“0”(k:多項式位寬),然後使用模二運算得到餘數,這個餘數就是CRC校驗碼。
例如:多項式為G(x) = X4 + X3 + 1,計算的資料幀為:2b'10110011,計算過程如下:
得到的CRC校驗碼為:2b'0100。
2 程式碼實現
參考連結:資料幀CRC32校驗演算法實現 - 沒落騎士 - 部落格園 (cnblogs.com)
線上生成工具:CRC Generation Tool - easics
還是以多項式為G(x) = X4 + X3 + 1,計算的資料幀為:2b'10110011來進行說明。
這裡直接說實現,首先使用線上生成工具,得到一個VHDL或者Verilog的CRC校驗原始碼。
然後對下載的程式碼進行一些改動即可。這裡直接給出最後實現的C程式碼。
#include <stdio.h>
#define int32_t signed int
#define uint32_t unsigned int
#define uint8_t unsigned char
#define GET_BIT_N_VAL(data, n) (0x1 & (( *((data) + (n)/8) & (0x1 << ((n)%8)) ) >> ((n)%8)))
#define BITS_TO_BYTES_ARRAY(entry_key, entry_key_bits, bytes_array) \
do \
{ \
uint32_t i = 0; \
for(i = 0; i < (entry_key_bits); i++) \
{ \
bytes_array[i] = GET_BIT_N_VAL((entry_key), i); \
} \
} while (0)
#define HASH_KEY_WRITE_BITS 8
uint8_t crc_4_d8(const uint8_t *d) // only use the last one bit
{
/*
-- polynomial: x^4 + x^3 + 1
-- data width: 8
-- convention: the first serial bit is D[7]
*/
int32_t i;
uint8_t ret=0;
uint8_t lfsr_c[4] = {0};
uint8_t c[4] = {0};
lfsr_c[0] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ d[0] ^ c[1] ^ c[3];
lfsr_c[1] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[1] ^ c[0] ^ c[2];
lfsr_c[2] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ d[2] ^ c[0] ^ c[1] ^ c[3];
lfsr_c[3] = d[7] ^ d[6] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[0] ^ c[2] ^ c[3];
for(i = 0; i < 4; i++)
{
ret |= (lfsr_c[i]<<i);
}
return ret;
}
int main(void)
{
uint8_t d = 0xb3;
uint8_t ret = 0;
uint8_t bytes_array[8] = {0};
BITS_TO_BYTES_ARRAY(&d, HASH_KEY_WRITE_BITS, bytes_array);
ret = crc_4_d8(bytes_array);
printf("ret = 0x%x\n", ret);
return 0;
}
編譯,執行,結果和之前手動計算的一致。
[grace@localhost] ~/c/crc
$ gcc test_crc.c
[grace@localhost] ~/c/crc
$ ./a.out
ret = 0x4