四、实现方式
实现方式主要有逐比特计算法、并行计算法和查表法,逐比特计算法有2种方法,第一种是按照标准除法流程进行计算,第二种是按照标准除法等效的移位寄存器进行计算;并行计算法是一次进行多位bit的CRC计算,通过多位bit串联的CRC计算公式得到整个发送序列的CRC;查表法把整个发送序列的CRC值存在一张表中,通过查询即可得到CRC,发送序列太大时需要巨大的存储空间,所以这种方法只适用于发送序列较短的情况,或者可以作为并行计算算法的一部分,在进行多位bit的CRC计算时使用查表法,查表法不再叙述。
a)逐比特计算法
按照标准除法流程进行计算时的算法如下:
(1)在待传输的k比特数据后面加入(n-k)个0,即x^(n-k)M(x)
(2)取x^(n-k)M(x)的前(n-k+1)个数作余式r(x)的初始值
(3)While (数据未处理完),次数为k次
Begin
If (r(x)首位是1)
r(x)= r(x)XOR g(x)
else
r(x)= r(x)
end
r(x)寄存器左移一位,按顺序读入一个新的x^(n-k)M(x)数据于r(x)寄存器的0 bit的位置。
End
(4) r(x)寄存器就是我们所要求的余数。
MATLAB脚本程序如下:
function crc_out = CRC_direct(g,din,InitialState,FinalXOR) %%% g:生成多项式,din:数据输入
lg = length(g); %%%%% g(x)的长度,为n-k+1
ld = length(din); %%%%%%M(x)的长度,为k
din_addzeros = [din;zeros(lg,1)]; %%%%%在待传输的k比特数据后面加入(n-k+1)
%%个0,多加一个是为了为后一步时,r(x)的长度保持为n-k+1
r = din_addzeros(1:lg); %%%%%取x^(n-k)M(x)的前(n-k+1)个数作余式r(x)的初始值
r(1:lg-1) = mod(din_addzeros(1:lg-1) +InitialState.',2) ;%%% r(x)的初始值和输入初始值异或
for ii=1:ld %%%%次数为k次
if r(1) ==0 %%%If (r(x)首位是0)
r(1:lg-1) = r(2:lg); %%% r(x)寄存器左移一位
r(lg)=din_addzeros(ii+lg); %%%%读入一个新的x^(n-k)M(x)数据
else %%%If (r(x)首位是1)
r(1:lg-1) = mod(r(2:lg) + g(2:lg).',2); %%% r(x)= r(x)XOR g(x),首位为0,移除
r(lg)=din_addzeros(ii+lg); %%%%读入一个新的x^(n-k)M(x)数据
end
end
r(1:lg-1) =mod(r(1:lg-1) + FinalXOR.',2); %%%与“结果异或值”进行异或
crc_out = [din;r(1:lg-1)]; %%%组成信息序列和CRC值
按照标准除法等效的移位寄存器进行计算的算法结构如下图所示:
算法流程如下:
(1) 设置CRC寄存器,如果有初值,并给其赋值为“余数初始值”;
(2) 将M(x)的第一个bit 与CRC寄存器最高位进行异或,该结果与g(x)的最低位到次高位进行相乘(g(x)的阶数比CRC高一阶,所以长度与CRC相同),记为rd,CRC寄存器最低位添0,向高位移位,在和rd异或后成为新的CRC寄存器;
(3) M(x)下一个bit进行(2)操作;
(4) M(x)最后一个bit进行(2)操作后,CRC寄存器即为CRC值;
(5) 最终CRC寄存器的内容与“结果异或值”进行或非操作后即为最终的CRC值。
原理阅了Peterson和Brown写的《Cyclic Codes for Error Detection》,除法如下图:
可以把输入数据拆开进行等效计算,输入数据为[1 1 1 0 1 0],如下所示,
[1 1 1 0 1 0] =[1 0 0 0 0 0 ] + [0 1 0 0 0 0] + [0 0 1 0 0 0] + [0 0 0 0 0 0] +
[0 0 0 0 1 0] + [0 0 0 0 0 0]
第一个先求[1 0 0 0 0 0 ]的余式,初始余式为全0,场景回到上一个余式,即此时的余式为全0,记为rx0=[0 0],rx0和[1 0 0 0 0 0 ]的高2位[1 0]异或后即为下一个余式的高2位[1 0],场景回到当前,[1 0]添0为[1 0 0],再处以[1 0 1],此时商1,[1 0 0]异或[1 0 1],余式为[0 0 1],高位舍弃,余式为[ 0 1],即[1 0 0 0 0 0 ]的最终余式为[0 1];
第二个求[0 1 0 0 0 0 ]的余式,上一个余式为[0 1],场景回到上一个余式,记为rx1=[0 1],rx1和[0 1 0 0 0 0 ]的2位[1 0]异或后即为下一个余式的高2位[1 1],场景回到当前,[1 1]添0为[1 1 0],再处以[1 0 1],此时商1,[1 1 0]异或[1 0 1],余式为[0 1 1],高位舍弃,余式为[ 1 1],即[0 1 0 0 0 0 ]的余式为[11];
第三个求[0 0 1 0 0 0 ]的余式,上一个余式为[1 1],场景回到上一个余式,记为rx2=[1 1],rx2和[0 0 1 0 0 0 ]的2位[1 0]异或后即为下一个余式的高2位[0 1],场景回到当前,[0 1]添0为[0 1 0],再处以[1 0 1],此时商0,[0 1 0]异或[0 0 0],余式为[0 1 0],高位舍弃,余式为[1 0],即[0 0 1 0 0 0 ]的余式为[10];
以此类推,所以当前的输入与上一个的余式最高位的异或决定了当前的商值,异或为1,商为1,异或为0,商为0。
在https://blog.csdn.net/u011639609/article/details/51464773中有过解释,参考如下图,g(x)=x3+x2+1。
信息序列还是1001,初始状态时,D1~D3三个寄存器的值都为0,输入的第一个信息比特是1,与D3的异或是1,这里为什么输入序列与D3做异或呢?其实这里隐含了一个选择操作,D1~D3代表当前的余式,D3表示余式的最高位。
场景回到上一个余式,此时,D3为上一个余式的次高位,上一个余式计算时,次高位与当前的下一个输入的异或值,即为下一个余式的初始的最高位,若最高位为1,下一个商为1,若最高位为0,下一个商为0。
场景回到当前,所以如果当前的输入与余式最高位D3相同的话,则当前输入与余式的模2加为0,即当前输入更新使得余式最高位为0,则此时商上面上0,不用对生成多项式做模2加运算,余式只要右移一位完成余式的更新操作(一个比特与0异或等于自身,所以这种情况下,电路中异或不起作用);另一种情况是,余式最高位D3与当前输入序列异或为1,表示当前输入更新后余式包含最高位,需要对生成多项式求余,求余操作通过电路中的异或进行,完成D1~D3的更新。具体步骤如下:
(1) 初始状态:输入ui D1 D2 D3 输出uo
0 0 0
(2) 1 1 0 1 1
(3) 0 1 1 1 0
(4) 0 1 1 0 0
(5) 1 1 1 0 1
D1~D3剩下的011即为监督比特,剩下3拍,逐比特输出:
(6) 0 0 1 1 0
(7) 0 0 0 1 1
(8) 0 0 0 0 1
MATLAB脚本程序如下:
function crc_out = CRC_direct_LFSR(g,din,InitialState,FinalXOR)
lg = length(g);
ld = length(din);
r = InitialState.'; %%%初始化
for ii=1:ld
rd = mod(r(1) + din(ii),2); %%%当前的输入与上一个的余式最高位的异或
r(1:lg-2) = mod(r(2:lg-1) + rd*g(2:lg-1).',2); %%%按g(x)的多项式更新
r(lg-1)=rd; %%%%最低位为rd
end
r(1:lg-1) =mod(r(1:lg-1) + FinalXOR.',2);
crc_out = [din;r(1:lg-1)];
按照标准除法等效的移位寄存器进行计算的算法还有一种结构,如下图所示:
b)并行计算法
并行计算法是一次进行多位bit的CRC计算,通过多位bit串联的CRC计算公式得到整个发送序列的CRC,多位bit可以为4位、8位或者16位。
以16位CRC为例介绍,如果要计算16位CRC,如下2种方式:
- 一次可以进行8位CRC计算,再通过8位bit串联的CRC计算公式得到整个发送序列的CRC;
- 一次可以进行16位CRC计算,计算方法即为正常CRC的方法:将本组CRC的值和下一组16位数据值异或后,重新计算得到下一组的CRC值,顺序执行即可得到整个发送序列的CRC;
8位bit的CRC计算值可以查表得到,用逐比特计算法计算得到表值 ;
8位bit串联的CRC计算公式如下,参见《通信系统中的CRC算法的研究和工程实现_廖海红》:
MATLAB脚本程序如下:
function crc_out = CRC_Paraller16_8(g,din,InitialState,FinalXOR,direct)
lg = length(g);
ld = length(din);
din(1:lg-1) = mod(din(1:lg-1) +InitialState.',2) ;
M = 8;
Num = ld/M;
din_1 = din(1:M); %%%第一组8bit
if direct == 1
r_M(:,1) = CRC_direct_LFSR(g,din_1,zeros(1,lg-1),FinalXOR);%%%直接法
else
r_M(:,1) = CRC_table(din_1); %%%查表法
end
r = r_M(end-lg+2:end,1);
for ii=2:Num %%%第二组到最后一组8bit
rL = [r(9:16);zeros(8,1)]; %%低8位左移
din_ii= mod(din(ii*M-M+1:ii*M) +r(1:8),2) ; %%取高8位
if direct == 1
r_M(:,ii) = CRC_direct_LFSR(g,din_ii,zeros(1,lg-1),FinalXOR); %%%直接法
else
r_M(:,1) = CRC_table(din_1) ; %%%查表法
end
r = mod(rL + r_M(end-lg+2:end,ii),2); %%%相加
end
r(1:lg-1) =mod(r(1:lg-1) + FinalXOR.',2);
crc_out = [din;r(1:lg-1)];
独立16位bit的CRC计算公式可以根据生成多项式进行推导或者查表得到。
根据生成多项式进行推导16位bit的CRC计算公式的结构如下图所示:
对应的MATLAB脚本程序(在Zhao Baozhu的基础上修改的)如下:
para =16; %%%%并行位数
gen = [1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1]; %%%生成多项式,x^16+x^15+x^2+1
crc_value = [diag(ones(1,length(gen)-1)),zeros(length(gen)-1,para)];%%%初始值
%%%%%% crc_value含义:第1行为CRC新值0的表达式,其中前16bit为CRC旧值,从低到高依次为CRC旧值0,1,…15,为1表示需要异或,为0表示不需要异或,17-32bit为输入bit,分别为d0,d1,…d15,d15表示第1个输入,d15表示第16个输入,依次类推,第2行为CRC新值1的表达式,…,第16行为CRC新值15的表达式。所以前16*16矩阵为diag,后16*16为全0,表示初始值。
%%%%%%
%%%%%%
for i=1:para
dataXORcrc_value_end = crc_value(length(gen)-1,:); %%%先取CRC旧值最高位
dataXORcrc_value_end(length(gen)+para-i) = 1; %%%%输入bit置为1,相当于异或
for j=length(gen)-1:-1:2 %%%%%%必须逆序,先把高位置为新值
if gen(j)==1
crc_value(j,:) = crc_value(j-1,:)+dataXORcrc_value_end;
else
crc_value(j,:) = crc_value(j-1,:);
end
end
crc_value(1,:) = dataXORcrc_value_end;
end
crc_value = mod(crc_value,2);
for i = 1:length(gen)-1
s=sprintf('crc_new(%d) =',i-1);
for j = 1:length(gen)+para-1
if crc_value(i,j) == 1
if j <length(gen)
str=sprintf(' crc_old(%d)',j-1);
else
str=sprintf(' data_new(%d)',j-length(gen));
end
s=strcat(s,' +',str);
end
end
simplepoly{i,1}=s;
end
运行后得到x^16+x^15+x^2+1的CRC16bit并行计算公式如下所示:
其中,crc_new(0) = + crc_old(0) + crc_old(4) + crc_old(8) + crc_old(11) + crc_old(12) + data_new(0) + data_new(4) + data_new(8) + data_new(11) + data_new(12),CRC旧值和data新值的序号一模一样,所以CRC旧值和data新值可以先进行异或成为新的data_new,再通过公式算出CRC,公式只取data_new相关的部分,例如:
crc_new(0) = data_new(0) + data_new(4) + data_new(8) + data_new(11) + data_new(12)。
以16位bit为单位连续bit的CRC计算的MATLAB脚本程序如下:
function crc_out = CRC_Paraller16_16(g,din,InitialState,FinalXOR,direct)
lg = length(g);
ld = length(din);
din(1:lg-1) = mod(din(1:lg-1) +InitialState.',2) ;
M = 16;
Num = ld/M;
din_1 = din(1:M); %%%第一组16bit
if direct == 1
r_M(:,1) = CRC_direct_LFSR(g,din_1,zeros(1,lg-1),FinalXOR); %%%直接法,也可以用根据生成多项式进行推导的公式或者查表法
end
r = r_M(end-lg+2:end,1);
for ii=2:Num
din_ii= mod(din(ii*M-M+1:ii*M) +r,2) ; %%%和上一组的余式进行相加
if direct == 1
r_M(:,ii) = CRC_direct_LFSR(g,din_ii,zeros(1,lg-1),FinalXOR); %%%直接法,也可以用根据生成多项式进行推导的公式或者查表法
end
r = r_M(end-lg+2:end,ii);
end
r(1:lg-1) =mod(r(1:lg-1) + FinalXOR.',2);
crc_out = [din;r(1:lg-1)];