0. 前言
CRC(Cyclic Redundancy Check,循环冗余校验),是一种数字编码技术,广泛应用于数字通信的传输差错检测,或数字存储的数据完整性检测,最早由W. Wesley Peterson于1961年发表的论文《Cyclic Codes for Error Detection》中提出。
这个数字时代,CRC无处不在!每当你发一条微信、刷一个视频、开一个网页……,几乎都有着这个诞生于六十多年前的技术的默默守护!对于大多数嵌入式软件工程师、FPGA逻辑工程师,一个稍显夸张的说法:咱们不是在用,就是在学CRC,或者在路上。
众所周知,CRC校验码需要预先确定一个生成多项式G(x),其(差错检测)性能几乎全由这个G(x)决定。老话又说了,不是每个多项式都适合作CRC的G(x),简中网(博文)数十年的沉淀,给这个G(x)定下了四个条件(不要纠结是否和你曾经看到的一字不差):
- 最高位和最低位必须为1;
- 当CRC校验码任何一位发生错误时,被生成多项式模2除后余数应不为0;
- 不同位发生错误时,模2除运算后余数不同;
- 对不为0的余数继续进行模2除运算,应使余数循环。
网络博文,靠不靠谱哦?刚开始我也有这个疑问,后来发现还真有出处:2、3、4三个说法能在某本1994年出版的《计算机组成原理》中看到,2008年出版的另一本列为了三个条件,2021年出版的某本列为了四个条件。
既然这“四大条件”被写进了数本《计算机组成原理》教材,自当好好学习研究一下,于是有了此文。(备注:文中公式较多,当您读着不顺畅时,不妨刷新试试!)
1. CRC校验到底是干嘛用的?
CRC校验码的发明者Peterson教授将论文取名为“Cyclic Codes for Error Detection——用于差错检测的循环码” ,后来人们称为Cyclic Redundancy Check。看来,作为二进制分组码的CRC,当它的最小汉明距离 ≥3 时,虽然在理论上具备至少一个比特的纠错能力,但人们在数字通信或存储的工程实施时,更倾向于选择应用它强大的差错检测能力,即用来检错的。
2. 条件是拿来干嘛的?怎么满足?
CRC校验的差错检测性能几乎全由其生成多项式G(x)决定,既然是G(x)需要满足的条件,自然是用来在新规范设计时确保达到某个性能,或分析理解已有规范的G(x)能提供什么样的性能。
这些条件到底怎么满足?博文和相关教材统一套路:从天而降4个条件,然后一个或大或小的表格列一些国际规范的多项式。2008年出版的那本教材倒是说了一句:“达到上述要求的数学关系比较复杂,读者若有兴趣可查阅有关资料。”
那么问题来了,给出4个条件,不告诉读者(学生)怎么根据条件筛选或确定CRC的生成多项式;或者给出一个现成的多项式表格,又不告诉读者(学生)怎么鉴别这些多项式是否满足条件。嗯,表格摘录的是国际规范的多项式,都满足条件的,不用分析。一定满足吗?
不能用、不会用、不需要用,那你告诉我4个条件干什么,背下来考试吗?
3. 这“四大条件”到底说了个啥?
文章到这里,怎么感觉前面写的全是废话,这要不给这“四大条件”说出个子丑寅卯来,怕是要被读者的口水淹死!那我们就来逐个分析什么样的多项式满足这些条件,满足这些条件能获得什么样的差错检测性能。
3.1 最高位和最低位必须为1
我们常用的CRC校验码属于二进制分组码,处理的是二进制信息,最小的信息单元是一个二进制比特位,有且只有0或1两个状态。当采用\( k \)个二进制比特位按照规定的顺序排列,即可构成一个\( k \)位的二进制序列,或称为二进制码字\( C=(c_{k-1},c_{k-2}, \cdots ,c_1,c_0) \),可表示\( 2^k \)种状态。
在编码理论中,为了更好地采用代数工具研究二进制编码,人们普遍采用GF(2)有限域的多项式(以下简称多项式)来描述二进制码字,即将码字\( C=(c_{k-1},c_{k-2}, \cdots ,c_1,c_0) \)的比特位写作码多项式\( C(x)= c_{k-1} x^{k-1} + c_{k-2}x^{k-2} + \cdots + c_1x^1 + c_0x^0 \)的系数,\( C(x) \)中的自变量\( x \)通常用作占位元,一般没有具体含义。
多项式\( C(x) \)的所有非零系数中,最高幂次称为它的次数,记为deg(c)。在一个多项式的非零最高次项前面,无论我们再写多少幂次更高但系数为零的项,都不影响该多项式参与运算的结果,而这样写下去,有无穷多个运算性质相同的多项式,只会造成沟通不畅(或定义混淆),于是,人们就给多项式规定了一个次数,因此,多项式的次数是一个定义,是它的固有属性。
\( C(x) \)的常数项(或称为零次项)也是它的一个重要项,常用于表征某多项式是否是非零多项式:即\( C(0)=0 \),则\( C(x) \)的常数项系数\( c_0=0 \);或\( C(0) \neq 0 \),则\( C(x) \)的常数项系数\( c_0 \neq 0 \) 。
人们在描述或介绍某个规范的CRC校验码时,最顶层(宏观)的描述是:CRC-r,POL=0b1x⋯x1 ,其中,POL规定了选用的生成多项式,\( r \)则规定了校验位的长度和生成多项式的次数。CRC校验码生成多项式的最低条件是:最高位和最低位必须是1。于是,我们可据此写出POL对应的多项式G(x):
\[ G(x)=x^r + g_{r-1}x^{r-1} + \cdots + g_1x^1 + 1 \]
严格意义来说,CRC校验码生成多项式的最低条件应描述为:次数 ≥ 1,且常数项为1的多项式。这也是实际通讯规范选用的生成多项式都满足的基本条件。为什么要这么规定?(扩展阅读\( ^{[4]} \):详解CRC校验码生成多项式的最高位和最低位必须是1)。
满足这个条件,具备什么样的差错检测性能?答案:在任意码字长度范围内,检测所有的单比特错误。即,不管含校验码的码字有多长,只要该码字中有且只有1个比特的错误,也不管这个错误比特位于码字中的哪个位置,都能检测出此错误。对CRC校验码而言,意味着这个有且只有单比特错误的码字除以生成多项式的余式不为0!
这个结论怎么证明?\( ^{[1]} \)请见Peterson教授论文第3页定理1(友情提示:该定理限定的生成多项式条件都不需要常数项为1,仅需要生成多项式的非零项数目(即汉明重量)大于等于2,而且证明过程是显然成立。):
3.2 任何一位发生错误则余数不为0
本条件虽然是用于约束CRC校验码生成多项式的筛选原则,但并未直接描述多项式,而是描述生成码字集合的差错检测性能,要求选用的生成多项式应确保本性能指标。
教材或相关文章通常会用比较精炼的语言描述,本文反其道而行之,看看本条件具体规定了什么样的差错检测性能:
在任意码字长度范围内,检测所有的单比特错误。即,不管含校验码的码字有多长,只要该码字中有且只有1个比特的错误,也不管这个错误比特位于码字中的哪个位置,都能检测出此错误。对CRC校验码而言,意味着这个有且只有单比特错误的码字除以生成多项式的余式不为0!
等等,你莫不是在逗我,你咋把上一节满足最低位和最高位为1的多项式的检错性能给抄在了这里?我也没办法,事实就是这么个事实,满足条件1的多项式,天然满足条件2,条件2把条件1的结果当作条件又提了一次!
3.3 不同位发生错误的余数不同
本条件描述的是双比特错误的检错能力,或单比特错误的纠错能力。一个长度为\( n \)的码字中,发生在不同位置\( i,j(i \neq j,0 \leqslant i,j \leqslant n-1) \)的两个独立错误,其错误图样可分别用多项式\( E^{(i)} (x) = x^i \)和\( E^{(j)} (x) = x^j \)表示,它们对生成多项式G(x)求余的余式\( R^{(i)} (x) \)、\( R^{(j)} (x) \)分别为:
\[ \begin{aligned}
R^{(i)} (x) & \equiv x^i \: \mathrm{mod} \: G(x) \\
R^{(j)} (x) & \equiv x^j \: \mathrm{mod} \: G(x)
\end{aligned} \]
本条件要求\( R^{(i)} (x) \neq R^{(j)} (x) \)。由于CRC校验码的多项式运算定义于GF(2)有限域,其运算法则是模二算术,即模二加等于模二减,所以:
\[ R^{(i)} (x) - R^{(j)} (x) = R^{(i)} (x) + R^{(j)} (x) \neq 0 \]
又,两多项式之和对G(x)的余式等于它们分别对G(x)的余式之和,即:
\[ [(x^i+x^j) \: \mathrm{mod} \: G(x)] = R^{(i)} (x) + R^{(j)} (x) \neq 0 \]
上式表明,长为\( n \)的码字中,有且只有两个独立比特的差错时,CRC校验可检测出该错误。显然,该码字集合的最小汉明距离 ≥3,可用来检测两个独立比特的错误,或用来纠正一个单比特错误。这个性能看起来相当不错,那我们选用的生成多项式需要满足什么条件呢?
由于是发生在\( i,j(i \neq j,0 \leqslant i,j \leqslant n-1 ) \)的两个不同位置的独立差错,我们可任意假定\( j > i \),并设\( j - i = m \),则错误图样可用多项式\( E(x) = x^i+x^j = x^i(x^{j-i} + 1) = x^i(x^{m} + 1) \)表示,由\( i,j \)的约束关系可得:\( 0 < m \leqslant n-1 \) ,从而,我们得到CRC校验码检测双比特错误的条件:生成多项式不能整除\( x^{m}+1 \) 。
到了这里,不得不再次引入有限域多项式的另一个固有属性(或者说定义):多项式的周期。见文献[2]第121~122页:
读者(包括笔者本人)暂时不了解有限域多项式的周期也没关系,我们只需记住CRC校验码的生成多项式:
- 都有周期,是固有属性\( ^{[2][3]} \);
- 周期是可以求解(有工具软件)的;
- 选定非零多项式次数\( r \)后,周期\( e \)的大小不会超过\( 2^{r}-1 \),即\( e \leqslant 2^r - 1 \) 。
从而,CRC校验码生成多项式与双比特差错检测的关系就出来了:
- 当码字长度\( n \)小于等于生成多项式周期\( e \)时,一定能检测出该码字中任意的双比特错误,即整个码字集合的最小汉明距离 ≥3,理论上可纠正一个单比特错误,至于用不用于纠错,就看使用者怎么考虑了;
- 当码字长度\( n \)大于生成多项式周期\( e \)时,存在不能检测的双比特错误情况,即整个码字集合的最小汉明距离 ≥2。
因此,对于条件3而言,设计时的筛选或应用时的分析,都是解决生成多项式周期与码字长度间的权衡。这在校验位长度较短时,尤其应当引起足够的重视,例如,8位以下的CRC校验码,周期最大也不会超过255,两个经典的8位CRC生成多项式:CRC-8/ITU,\( G(x) = x^8 + x^2 + x + 1 \);CRC-8/MAXIM,\( G(x) = x^8 + x^5 + x^4 + 1 \),它们的周期都是127,意味着信息位长度一旦超过119,则码字长度将超过127,将会出现部分双比特错误漏检的情况。CRC-8/SAE J1850的\( G(x) = x^8 + x^4 + x^3 + x^2 + 1 \),周期是255。
单从周期来看,似乎CRC-8/SAE J1850的性能优于CRC-8/ITU和CRC-8/MAXIM?不是的,分析CRC校验码的检错性能时,不能孤立的比较多项式周期大小,一定要结合码字长度和多项式的其它特性综合考虑:
- CRC-8/SAE J1850的多项式是本原多项式,具有8次多项式可能最大的周期255,但它没有检测“所有奇数个错误比特”的能力;
- CRC-8/ITU和CRC-8/MAXIM的多项式周期是127,但它们都能检测“所有奇数个错误比特”,也就是说,在码字长度 ≤127时,CRC-8/ITU和CRC-8/MAXIM可检测所有的:单比特、双比特和三比特随机差错,码字集合的最小汉明距离是4,而一旦码字长度超过127时,最小汉明距离又会快速的退化为2;
- 由于SAE J1850数据链路层规定的消息帧格式中,由CRC保护的数据含校验位在内最大12字节(96-bits),小于127,所以,若SAE J1850选择CRC-8/ITU或CRC-8/MAXIM的生成多项式,可获得事实上更好的差错检测性能;
- MAXIM公司在其推出的温度传感器芯片DS18B20中首次选用CRC-8/MAXIM这个多项式,而由CRC保护的数据含校验位在内最大8字节(64-bits),小于127,所以DS18B20的CRC-8校验码设计可确保检测出传输中的所有:单比特、双比特和三比特随机差错,有着相当好的差错检测性能;
- SMB-BUS规范选用了CRC-8/ITU的多项式用作传输差错检测,但其数据链路层要求CRC校验保护的数据含校验位在内却可能高达260字节(2080-bits),这远远超过了8位CRC校验码的周期,意味着通讯传输中可能出现较大量的双比特随机差错漏检!若选用CRC-8/SAE J1850的多项式,能获得一定量的性能提升,但事实上应选择更高位数的CRC校验。
3.4 对不为0的余数继续作除会使余数循环
笔者第一次看到这个条件时,并没想明白它想表达个什么意思,直到学习了某几本教材和某个慕课视频后,终于明白:当CRC校验的接收码字中出现1个单比特错误,则该码字对生成多项式求余,自然会得到一个不为0的余式,将此余式左移1位并右侧补0,然后对生成多项式求余,继续……,如此循环,我们会惊喜的发现,经过一定循环次数后,会得到最初的那个余式。
这是为什么呢?这又涉及到有限域多项式求余的一个基本性质:
\[ \left [ x \cdot f(x) \right ] \: \mathrm{mod} \: G(x) = \left \{ x \left [ f(x) \: \mathrm{mod} \: G(x) \right ] \right \} \: \mathrm{mod} \: G(x) \]
本条件的“对不为0的余数左移补0循环求余”的数学描述是:假设原单比特错误\( x^i \)的余式是\( R^{(i)} (x) \) ,经\( j \)次左移补0循环求余后的余式是\( R^{(j)} (x) \) ,有:
\[\begin{aligned}
x^i \: \mathrm{mod} \: G(x) &= R^{(i)} (x) \\
x^j R^{(i)} (x) \: \mathrm{mod} \: G(x) &= R^{(j)} (x)
\end{aligned}\]
合并上式可得:
\[ x^{i+j} \: \mathrm{mod} \: G(x) = R^{(j)} (x) \]
本条件的要求是回到最初的余数,即\( R^{(j)} (x) = R^{(i)} (x) \),也就是要求\( (x^i + x^{i+j}) \)对\( G(x) \)求余的余式为0:
\[ \begin{aligned}
(x^i + x^{i+j}) \: \mathrm{mod} \: G(x) &= 0 \\
\left [ x^i(x^j + 1) \right ] \: \mathrm{mod} \: G(x) &= 0
\end{aligned} \]
什么条件下上式能成立?\( (x^j + 1) \: \mathrm{mod} \: G(x) = 0 \),或者说\( (x^j + 1) \)可被\( G(x) \)整除,眼不眼熟?这不就是多项式周期的定义嘛,也就是说,如果“对不为0的余数左移补0循环求余”,不但可以确定余式肯定会回到最初的那个余式(即循环),而且可以确定,需要也只需要生成多项式周期\(e\)次操作即可回到最初。
条件4不需要规定,它本身就是CRC校验码生成多项式固有周期的一个外在表现,或者说条件4是多项式周期的结果,只要你用CRC校验,条件4天然满足。
4. 总结
本文对简中网博文乃至某些教材上广泛提及的CRC校验码生成多项之“四大条件”进行了系统性分析,发现:
- 条件1,是必要的,同时也是众所周知的,所有现行通讯协议规范都遵照的。
- 条件2,是毫无意义的,是条件1的结果,满足条件1自然满足条件2。
- 条件3,是码字长度与生成多项式周期的权衡结果。较短(比如8位)的CRC校验位时,现行通讯协议规范不一定都满足。
- 条件4,都不能称其为条件,是CRC校验码生成多项式固有周期的外在表现,用CRC就天然满足。
既然简中网这“四大条件”不好用,那我们怎么评估CRC校验码生成多项式与其差错检测性能间的关系?笔者建议:
- 定性分析。仔细阅读掌握Peterson教授1961年的论文\( ^{[1]} \),文中包含8条定理及其证明,可帮助我们评估生成多项式的:单比特、双比特、三比特、奇数比特等随机错误检测能力,不同长度的突发错误检测能力。为致敬Peterson教授,笔者在节选性翻译的基础上,辅以适当的二次创作,有了一篇博文\( ^{[5]} \):CRC校验码的检错性能(一)—— 检出(或漏检)比例。
- 定量分析。所谓定量,即采用计算机穷举法枚举目标CRC校验码的全部码字集合,得到它的重量分布数据,从而可得到码字集合的最小汉明距离,或直接计算在给定信道误比特率下的差错漏检概率。建议参考Carnegie Mellon University,Philip Koopman教授的论文,尤其是他在个人网站上已经公布的,大量的多项式评估数据\( ^{[6]} \)。
5. 参考
- W. W. Peterson and D. T. Brown, "Cyclic Codes for Error Detection," in Proceedings of the IRE, vol. 49, no. 1, pp. 228-235, Jan. 1961
- 冯克勤. 纠错码的代数理论[M]. 北京: 清华大学出版社, 2005.
- 鲖阳路人. 有限域上的多项式(1): 多项式的阶与本原多项式[EB/OL]. (2022-03-23).
- innovationcjs. 详解CRC校验码生成多项式的最高位和最低位为什么必须是1[EB/OL]. (2025-04-23).
- innovationcjs. CRC校验码的检错性能(一)—— 检出(或漏检)比例[EB/OL]. (2024-04-04).
- Philip Koopman. Best CRC Polynomials[EB/OL]. Carnegie Mellon University.