【A GUIDE TO CRC ERROR DETECTION ALGORITHM】 (译文3-Todo)

11. "Reflected" Table-Driven Implementations “反射”表驱动实现

Despite the fact that the above code is probably optimized about as much as it could be, this did not stop some enterprising individuals from making things even more complicated. To understand how this happened, we have to enter the world of hardware.

尽管上述代码可能已经尽可能地优化了,但这并没有阻止一些有进取心的人把事情变得更加复杂。要了解这是如何发生的,我们必须进入硬件世界。

DEFINITION: A value/register is reflected if it's bits are swapped around its centre.

定义:如果一个值/寄存器的位围绕其中心交换,则反映该值/寄存器。

For example: 0101 is the 4-bit reflection of 1010.
0011 is the reflection of 1100.
0111-0101-1010-1111-0010-0101-1011-1100 is the reflection of 0011-1101-1010-0100-1111-0101-1010-1110.

Turns out that UARTs (those handy little chips that perform serial IO) are in the habit of transmitting each byte with the least significant bit (bit 0) first and the most significant bit (bit 7) last (i.e. reflected). An effect of this convention is that hardware engineers constructing hardware CRC calculators that operate at the bit level took to calculating CRCs of bytes streams with each of the bytes reflected within itself. The bytes are processed in the same order, but the bits in each byte are swapped; bit 0 is now bit 7, bit 1 is now bit 6, and so on. Now this wouldn't matter much if this convention was restricted to hardware land. However it seems that at some stage some of these CRC values were presented at the software level and someone had to write some code that would interoperate with the hardware CRC calculation.

事实证明,UART(那些执行串行IO的方便的小芯片)习惯于首先传输最低有效位(位0),最后传输最高有效位(位数7)的每个字节(即反射)。这种约定的一个效果是,构建在比特级运行的硬件CRC计算器的硬件工程师需要计算字节流的CRC,每个字节都反映在其内部。字节以相同的顺序处理,但每个字节中的位被交换;位0现在是位7,位1现在是位6,以此类推。如果这个约定仅限于硬件领域,这就没什么关系了。然而,在某些阶段,这些CRC值中的一些似乎是在软件级别呈现的,必须有人编写一些与硬件CRC计算互操作的代码。

In this situation, a normal sane software engineer would simply reflect each byte before processing it. However, it would seem that normal sane software engineers were thin on the ground when this early ground was being broken, because instead of reflecting the bytes, whoever was responsible held down the byte and reflected the world, leading to the following "reflected" algorithm which is identical to the previous one except that everything is reflected except the input bytes.

在这种情况下,一个正常理智的软件工程师会在处理每个字节之前简单地反映出来。然而,当这个早期的基础被打破时,正常理智的工程师似乎很薄弱,因为负责的人没有反映字节,而是压低了字节,反映了世界,导致了以下“反映”算法,该算法与前一个算法相同,除了除了输入字节之外,其他所有内容都被反映出来。

         Message (non augmented) >-----+
                                       |
       Bytes   0    1    2    3        v
            +----+----+----+----+      |
            |    |    |    |    |>----XOR
            +----+----+----+----+      |
                      ^                |
                      |                |
                     XOR               |
                      |                |
            +----+----+----+----+0     |
            +----+----+----+----+      v
            +----+----+----+----+      |
            +----+----+----+----+      |
            +----+----+----+----+      |
            +----+----+----+----+      |
            +----+----+----+----+      |
            +----+----+----+----+<-----+
            +----+----+----+----+
            +----+----+----+----+
            +----+----+----+----+
            +----+----+----+----+
            +----+----+----+----+255

Notes:

  • The table is identical to the one in the previous algorithm except that each entry has been reflected.

    +除了每个条目都被反映出来之外,该表与之前算法中的表完全相同。

  • The initial value of the register is the same as in the previous algorithm except that it has been reflected.

    +寄存器的初始值与之前的算法中的值相同,除了它已被反映出来。

  • The bytes of the message are processed in the same order as before (i.e. the message itself is not reflected).

    +消息的字节按照与以前相同的顺序进行处理(即消息本身不被反映)。

  • The message bytes themselves don't need to be explicitly reflected, because everything else has been!

    +消息字节本身不需要显式反映,因为其他一切都已经反映出来了!

At the end of execution, the register contains the reflection of the final CRC value (remainder). Actually, I'm being rather hard on whoever cooked this up because it seems that hardware implementations of the CRC algorithm used the reflected checksum value and so producing a reflected CRC was just right. In fact reflecting the world was probably a good engineering solution - if a confusing one.

在执行结束时,寄存器包含最终CRC值(余数)的反映。事实上,我对炮制这件事的人相当严厉,因为CRC算法的硬件实现似乎使用了反射的校验和值,因此产生反射的CRC是正确的。事实上,反映世界可能是一个很好的工程解决方案——即使是一个令人困惑的解决方案。

We will call this the REFLECTED algorithm.

我们称之为反射算法。

Whether or not it made sense at the time, the effect of having reflected algorithms kicking around the world's FTP sites is that about half the CRC implementations one runs into are reflected and the other half not. It's really terribly confusing. In particular, it would seem to me that the casual reader who runs into a reflected, table-driven implementation with the bytes "fed in the wrong end" would have Buckley's chance of ever connecting the code to the concept of binary mod 2 division.

无论当时是否有意义,在世界各地的FTP站点上运行反射算法的效果是,大约一半的CRC实现被反射,另一半没有。这真的非常令人困惑。特别是,在我看来,如果一个偶然的读者遇到一个反射的、表驱动的实现,其中字节“输入错误的一端”,那么巴克利就有机会将代码与二进制模2除法的概念联系起来。

It couldn't get any more confusing could it? Yes it could.

它不能再让人困惑了,不是吗?是的,可以。

12. "Reversed" Polys


As if reflected implementations weren't enough, there is another concept kicking around which makes the situation bizaarly confusing. The concept is reversed Polys.

好像反射实现还不够,还有另一个概念让情况变得混乱。这个概念是相反的Polys。

It turns out that the reflection of good polys tend to be good polys too! That is, if G=11101 is a good poly value, then 10111 will be as well. As a consequence, it seems that every time an organization (such as CCITT) standardizes on a particularly good poly ("polynomial"), those in the real world can't leave the poly's reflection alone either. They just HAVE to use it. As a result, the set of "standard" poly's has a corresponding set of reflections, which are also in use. To avoid confusion, we will call these the "reversed" polys.

事实证明,好的polys的反射往往也是好的polys!也就是说,如果G=11101是一个好的poly值,那么10111也是。因此,似乎每次一个组织(如CCITT)对一个特别好的poly(“多项式”)进行标准化时,现实世界中的组织也不能放过poly的反射。他们只需要使用它。因此,一组“标准”多边形有一组相应的反射,这些反射也在使用中。为了避免混淆,我们将这些称为“反向”poly。

X25   standard: 1-0001-0000-0010-0001
X25   reversed: 1-0000-1000-0001-0001

CRC16 standard: 1-1000-0000-0000-0101
CRC16 reversed: 1-0100-0000-0000-0011

Note that here it is the entire poly that is being reflected/reversed, not just the bottom W bits. This is an important distinction. In the reflected algorithm described in the previous section, the poly used in the reflected algorithm was actually identical to that used in the non-reflected algorithm; all that had happened is that the bytes had effectively been reflected. As such, all the 16-bit/32-bit numbers in the algorithm had to be reflected. In contrast, the ENTIRE poly includes the implicit one bit at the top, and so reversing a poly is not the same as reflecting its bottom 16 or 32 bits.

请注意,这里反射/反转的是整个多边形,而不仅仅是底部的W位。这是一个重要的区别。在前一节描述的反射算法中,反射算法中使用的多边形实际上与非反射算法中的多边形相同;所发生的只是字节已被有效地反映出来。因此,算法中的所有16位/32位数字都必须反映出来。相比之下,整多边形在顶部包含隐式的一个位,因此反转多边形并不等于反映其底部的16或32位。

The upshot of all this is that a reflected algorithm is not equivalent to the original algorithm with the poly reflected. Actually, this is probably less confusing than if they were duals.

所有这一切的结果是,反射算法并不等同于具有多边形反射的原始算法。事实上,这可能比它们是对偶的情况更不令人困惑。

If all this seems a bit unclear, don't worry, because we're going to sort it all out "real soon now". Just one more section to go before that.

如果这一切看起来有点不清楚,别担心,因为我们“很快”就会解决这一切。在那之前还有一节。

13. Initial and Final Values 初始值和最终值


In addition to the complexity already seen, CRC algorithms differ from each other in two other regards:

除了已经看到的复杂性之外,CRC算法在另外两个方面也有所不同:

  • The initial value of the register.

    +寄存器的初始值。

  • The value to be XORed with the final register value.

    +与最终寄存器值进行异或运算的值。

For example, the "CRC32" algorithm initializes its register to FFFFFFFF and XORs the final register value with FFFFFFFF.

例如,“CRC32”算法将其寄存器初始化为FFFFFFF,并用FFFFFFF对最终寄存器值进行异或运算。

Most CRC algorithms initialize their register to zero. However, some initialize it to a non-zero value. In theory (i.e. with no assumptions about the message), the initial value has no affect on the strength of the CRC algorithm, the initial value merely providing a fixed starting point from which the register value can progress. However, in practice, some messages are more likely than others, and it is wise to initialize the CRC algorithm register to a value that does not have "blind spots" that are likely to occur in practice. By "blind spot" is meant a sequence of message bytes that do not result in the register changing its value. In particular, any CRC algorithm that initializes its register to zero will have a blind spot of zero when it starts up and will be unable to "count" a leading run of zero bytes. As a leading run of zero bytes is quite common in real messages, it is wise to initialize the algorithm register to a non-zero value.

大多数CRC算法将其寄存器初始化为零。但是,有些会将其初始化为非零值。理论上(即没有关于消息的假设),初始值对CRC算法的强度没有影响,初始值仅提供寄存器值可以从中前进的固定起点。然而,在实践中,一些消息比其他消息更有可能,明智的做法是将CRC算法寄存器初始化为一个没有实践中可能出现的“盲点”的值。“盲点”是指不会导致寄存器改变其值的消息字节序列。特别是,任何将寄存器初始化为零的CRC算法在启动时都会有一个盲点为零,并且无法“计数”零字节的前导运行。由于零字节的前导运行在真实消息中很常见,因此将算法寄存器初始化为非零值是明智的。

14. Defining Algorithms Absolutely 绝对定义算法


At this point we have covered all the different aspects of table-driven CRC algorithms. As there are so many variations on these algorithms, it is worth trying to establish a nomenclature for them. This section attempts to do that.

至此,我们已经介绍了表驱动CRC算法的所有不同方面。由于这些算法有很多变体,因此值得尝试为它们建立一个命名法。本节试图做到这一点。

We have seen that CRC algorithms vary in:

我们已经看到CRC算法在以下方面有所不同:

  • Width of the poly (polynomial).

    +多项式的宽度。

  • Value of the poly.

    +poly的价值。

  • Initial value for the register.

    +寄存器的初始值。

  • Whether the bits of each byte are reflected before being processed.

    +每个字节的位在处理之前是否被反映出来。

  • Whether the algorithm feeds input bytes through the register or xors them with a byte from one end and then straight into the table.

    +该算法是通过寄存器馈送输入字节,还是从一端用一个字节对其进行异或运算,然后直接输入到表中。

  • Whether the final register value should be reversed (as in reflected versions).

    +是否应反转最终寄存器值(如反射版本)。

  • Value to XOR with the final register value.

    +值与最终寄存器值XOR。

In order to be able to talk about particular CRC algorithms, we need to able to define them more precisely than this. For this reason, the next section attempts to provide a well-defined parameterized model for CRC algorithms. To refer to a particular algorithm, we need then simply specify the algorithm in terms of parameters to the model.

为了能够讨论特定的CRC算法,我们需要能够更精确地定义它们。因此,下一节将尝试为CRC算法提供一个定义良好的参数化模型。要引用特定的算法,我们只需根据模型的参数指定算法。

15. A Parameterized Model For CRC Algorithms


In this section we define a precise parameterized model CRC algorithm
which, for want of a better name, we will call the "Rocksoft^tm Model
CRC Algorithm" (and why not? Rocksoft^tm could do with some free
advertising 😃.

The most important aspect of the model algorithm is that it focusses
exclusively on functionality, ignoring all implementation details. The
aim of the exercise is to construct a way of referring precisely to
particular CRC algorithms, regardless of how confusingly they are
implemented. To this end, the model must be as simple and precise as
possible, with as little confusion as possible.

The Rocksoft^tm Model CRC Algorithm is based essentially on the DIRECT
TABLE ALGORITHM specified earlier. However, the algorithm has to be
further parameterized to enable it to behave in the same way as some
of the messier algorithms out in the real world.

To enable the algorithm to behave like reflected algorithms, we
provide a boolean option to reflect the input bytes, and a boolean
option to specify whether to reflect the output checksum value. By
framing reflection as an input/output transformation, we avoid the
confusion of having to mentally map the parameters of reflected and
non-reflected algorithms.

An extra parameter allows the algorithm's register to be initialized
to a particular value. A further parameter is XORed with the final
value before it is returned.

By putting all these pieces together we end up with the parameters of
the algorithm:

  • NAME: This is a name given to the algorithm. A string value.

  • WIDTH: This is the width of the algorithm expressed in bits. This
    is one less than the width of the Poly.

  • POLY: This parameter is the poly. This is a binary value that
    should be specified as a hexadecimal number. The top bit of the
    poly should be omitted. For example, if the poly is 10110, you
    should specify 06. An important aspect of this parameter is that it
    represents the unreflected poly; the bottom bit of this parameter
    is always the LSB of the divisor during the division regardless of
    whether the algorithm being modelled is reflected.

  • INIT: This parameter specifies the initial value of the register
    when the algorithm starts. This is the value that is to be assigned
    to the register in the direct table algorithm. In the table
    algorithm, we may think of the register always commencing with the
    value zero, and this value being XORed into the register after the
    N'th bit iteration. This parameter should be specified as a
    hexadecimal number.

  • REFIN: This is a boolean parameter. If it is FALSE, input bytes are
    processed with bit 7 being treated as the most significant bit
    (MSB) and bit 0 being treated as the least significant bit. If this
    parameter is FALSE, each byte is reflected before being processed.

  • REFOUT: This is a boolean parameter. If it is set to FALSE, the
    final value in the register is fed into the XOROUT stage directly,
    otherwise, if this parameter is TRUE, the final register value is
    reflected first.

  • XOROUT: This is an W-bit value that should be specified as a
    hexadecimal number. It is XORed to the final register value (after
    the REFOUT) stage before the value is returned as the official
    checksum.

  • CHECK: This field is not strictly part of the definition, and, in
    the event of an inconsistency between this field and the other
    field, the other fields take precedence. This field is a check
    value that can be used as a weak validator of implementations of
    the algorithm. The field contains the checksum obtained when the
    ASCII string "123456789" is fed through the specified algorithm
    (i.e. 313233... (hexadecimal)).

With these parameters defined, the model can now be used to specify a
particular CRC algorithm exactly. Here is an example specification for
a popular form of the CRC-16 algorithm.

Name : "CRC-16"
Width : 16
Poly : 8005
Init : 0000
RefIn : True
RefOut : True
XorOut : 0000
Check : BB3D

16. A Catalog of Parameter Sets for Standards


At this point, I would like to give a list of the specifications for
commonly used CRC algorithms. However, most of the algorithms that I
have come into contact with so far are specified in such a vague way
that this has not been possible. What I can provide is a list of polys
for various CRC standards I have heard of:

X25 standard : 1021 [CRC-CCITT, ADCCP, SDLC/HDLC]
X25 reversed : 0811

CRC16 standard : 8005
CRC16 reversed : 4003 [LHA]

CRC32 : 04C11DB7 [PKZIP, AUTODIN II, Ethernet, FDDI]

I would be interested in hearing from anyone out there who can tie
down the complete set of model parameters for any of these standards.

However, a program that was kicking around seemed to imply the
following specifications. Can anyone confirm or deny them (or provide
the check values (which I couldn't be bothered coding up and
calculating)).

Name : "CRC-16/CITT"
Width : 16
Poly : 1021
Init : FFFF
RefIn : False
RefOut : False
XorOut : 0000
Check : ?

Name : "XMODEM"
Width : 16
Poly : 8408
Init : 0000
RefIn : True
RefOut : True
XorOut : 0000
Check : ?

Name : "ARC"
Width : 16
Poly : 8005
Init : 0000
RefIn : True
RefOut : True
XorOut : 0000
Check : ?

Here is the specification for the CRC-32 algorithm which is reportedly
used in PKZip, AUTODIN II, Ethernet, and FDDI.

Name : "CRC-32"
Width : 32
Poly : 04C11DB7
Init : FFFFFFFF
RefIn : True
RefOut : True
XorOut : FFFFFFFF
Check : CBF43926

17. An Implementation of the Model Algorithm 模型算法的一种实现


Here is an implementation of the model algorithm in the C programming language. The implementation consists of a header file (.h) and an implementation file (.c). If you're reading this document in a sequential scroller, you can skip this code by searching for the string "Roll Your Own".

这是用C编程语言实现的模型算法。该实现由一个头文件(.h)和一个实现文件(.c)组成。如果您正在按顺序滚动阅读此文档,则可以通过搜索字符串“Roll Your Own”来跳过此代码。

To ensure that the following code is working, configure it for the CRC-16 and CRC-32 algorithms given above and ensure that they produce the specified "check" checksum when fed the test string "123456789" (see earlier).

为确保以下代码正常工作,请为上述CRC-16和CRC-32算法进行配置,并确保在输入测试字符串“123456789”时,它们会产生指定的“检查”校验和(见上文)。


/******************************************************************************/
/*                             Start of crcmodel.h                            */
/******************************************************************************/
/*                                                                            */
/* Author : Ross Williams (ross@guest.adelaide.edu.au.).                      */
/* Date   : 3 June 1993.                                                      */
/* Status : Public domain.                                                    */
/*                                                                            */
/* Description : This is the header (.h) file for the reference               */
/* implementation of the Rocksoft^tm Model CRC Algorithm. For more            */
/* information on the Rocksoft^tm Model CRC Algorithm, see the document       */
/* titled "A Painless Guide to CRC Error Detection Algorithms" by Ross        */
/* Williams (ross@guest.adelaide.edu.au.). This document is likely to be in   */
/* "ftp.adelaide.edu.au/pub/rocksoft".                                        */
/*                                                                            */
/* Note: Rocksoft is a trademark of Rocksoft Pty Ltd, Adelaide, Australia.    */
/*                                                                            */
/******************************************************************************/
/*                                                                            */
/* How to Use This Package                                                    */
/* -----------------------                                                    */
/* Step 1: Declare a variable of type cm_t. Declare another variable          */
/*         (p_cm say) of type p_cm_t and initialize it to point to the first  */
/*         variable (e.g. p_cm_t p_cm = &cm_t).                               */
/*                                                                            */
/* Step 2: Assign values to the parameter fields of the structure.            */
/*         If you don't know what to assign, see the document cited earlier.  */
/*         For example:                                                       */
/*            p_cm->cm_width = 16;                                            */
/*            p_cm->cm_poly  = 0x8005L;                                       */
/*            p_cm->cm_init  = 0L;                                            */
/*            p_cm->cm_refin = TRUE;                                          */
/*            p_cm->cm_refot = TRUE;                                          */
/*            p_cm->cm_xorot = 0L;                                            */
/*         Note: Poly is specified without its top bit (18005 becomes 8005).  */
/*         Note: Width is one bit less than the raw poly width.               */
/*                                                                            */
/* Step 3: Initialize the instance with a call cm_ini(p_cm);                  */
/*                                                                            */
/* Step 4: Process zero or more message bytes by placing zero or more         */
/*         successive calls to cm_nxt. Example: cm_nxt(p_cm,ch);              */
/*                                                                            */
/* Step 5: Extract the CRC value at any time by calling crc = cm_crc(p_cm);   */
/*         If the CRC is a 16-bit value, it will be in the bottom 16 bits.    */
/*                                                                            */
/******************************************************************************/
/*                                                                            */
/* Design Notes                                                               */
/* ------------                                                               */
/* PORTABILITY: This package has been coded very conservatively so that       */
/* it will run on as many machines as possible. For example, all external     */
/* identifiers have been restricted to 6 characters and all internal ones to  */
/* 8 characters. The prefix cm (for Crc Model) is used as an attempt to avoid */
/* namespace collisions. This package is endian independent.                  */
/*                                                                            */
/* EFFICIENCY: This package (and its interface) is not designed for           */
/* speed. The purpose of this package is to act as a well-defined reference   */
/* model for the specification of CRC algorithms. If you want speed, cook up  */
/* a specific table-driven implementation as described in the document cited  */
/* above. This package is designed for validation only; if you have found or  */
/* implemented a CRC algorithm and wish to describe it as a set of parameters */
/* to the Rocksoft^tm Model CRC Algorithm, your CRC algorithm implementation  */
/* should behave identically to this package under those parameters.          */
/*                                                                            */
/******************************************************************************/

/*The following #ifndef encloses this entire*/
/*header file, rendering it indempotent.*/

# ifndef CM_DONE

# define CM_DONE

/******************************************************************************/

/*The following definitions are extracted from my style header file which*/
/*would be cumbersome to distribute with this package. The DONE_STYLE is the*/
/*idempotence symbol used in my style header file.*/

# ifndef DONE_STYLE

typedef unsigned long   ulong;
typedef unsigned        bool;
typedef unsigned char * p_ubyte_;

# ifndef TRUE

# define FALSE 0

# define TRUE  1

# endif

/*Change to the second definition if you don't have prototypes.*/

# define P_(A) A

/*#define P_(A) ()*/

/*Uncomment this definition if you don't have void.*/
/*typedef int void;*/

# endif

/******************************************************************************/

/*CRC Model Abstract Type*/
/*-----------------------*/
/*The following type stores the context of an executing instance of the*/
/*model algorithm. Most of the fields are model parameters which must be*/
/*set before the first initializing call to cm_ini.*/
typedef struct
  {
   int   cm_width;   /*Parameter: Width in bits [8,32].*/
   ulong cm_poly;    /*Parameter: The algorithm's polynomial.*/
   ulong cm_init;    /*Parameter: Initial register value.*/
   bool  cm_refin;   /*Parameter: Reflect input bytes?*/
   bool  cm_refot;   /*Parameter: Reflect output CRC?*/
   ulong cm_xorot;   /*Parameter: XOR this to output CRC.*/

   ulong cm_reg;     /*Context: Context during execution.*/
  } cm_t;
typedef cm_t *p_cm_t;

/******************************************************************************/

/*Functions That Implement The Model*/
/*----------------------------------*/
/*The following functions animate the cm_t abstraction.*/

void cm_ini P_((p_cm_t p_cm));
/* Initializes the argument CRC model instance.          */
/* All parameter fields must be set before calling this. */

void cm_nxt P_((p_cm_t p_cm,int ch));
/* Processes a single message byte [0,255]. */

void cm_blk P_((p_cm_t p_cm,p_ubyte_ blk_adr,ulong blk_len));
/*Processes a block of message bytes.*/

ulong cm_crc P_((p_cm_t p_cm));
/* Returns the CRC value for the message bytes processed so far. */

/******************************************************************************/

/*Functions For Table Calculation*/
/*-------------------------------*/
/*The following function can be used to calculate a CRC lookup table.*/
/*It can also be used at run-time to create or check static tables.*/

ulong cm_tab P_((p_cm_t p_cm,int index));
/* Returns the i'th entry for the lookup table for the specified algorithm.   */
/* The function examines the fields cm_width, cm_poly, cm_refin, and the      */
/* argument table index in the range [0,255] and returns the table entry in   */
/* the bottom cm_width bytes of the return value.                             */

/******************************************************************************/

/*End of the header file idempotence #ifndef*/

# endif

/******************************************************************************/
/*                             End of crcmodel.h                              */
/******************************************************************************/

/******************************************************************************/
/*                             Start of crcmodel.c                            */
/******************************************************************************/
/*                                                                            */
/* Author : Ross Williams (ross@guest.adelaide.edu.au.).                      */
/* Date   : 3 June 1993.                                                      */
/* Status : Public domain.                                                    */
/*                                                                            */
/* Description : This is the implementation (.c) file for the reference       */
/* implementation of the Rocksoft^tm Model CRC Algorithm. For more            */
/* information on the Rocksoft^tm Model CRC Algorithm, see the document       */
/* titled "A Painless Guide to CRC Error Detection Algorithms" by Ross        */
/* Williams (ross@guest.adelaide.edu.au.). This document is likely to be in   */
/* "ftp.adelaide.edu.au/pub/rocksoft".                                        */
/*                                                                            */
/* Note: Rocksoft is a trademark of Rocksoft Pty Ltd, Adelaide, Australia.    */
/*                                                                            */
/******************************************************************************/
/*                                                                            */
/* Implementation Notes                                                       */
/* --------------------                                                       */
/* To avoid inconsistencies, the specification of each function is not echoed */
/* here. See the header file for a description of these functions.            */
/* This package is light on checking because I want to keep it short and      */
/* simple and portable (i.e. it would be too messy to distribute my entire    */
/* C culture (e.g. assertions package) with this package.                     */
/*                                                                            */
/******************************************************************************/

# include "crcmodel.h"

/******************************************************************************/

/*The following definitions make the code more readable.*/

# define BITMASK(X) (1L << (X))

# define MASK32 0xFFFFFFFFL

# define LOCAL static

/******************************************************************************/

LOCAL ulong reflect P_((ulong v,int b));
LOCAL ulong reflect (v,b)
/* Returns the value v with the bottom b [0,32] bits reflected. */
/* Example: reflect(0x3e23L,3) == 0x3e26                        */
ulong v;
int   b;
{
 int   i;
 ulong t = v;
 for (i=0; i<b; i++)
   {
    if (t & 1L)
       v|=  BITMASK((b-1)-i);
    else
       v&= ~BITMASK((b-1)-i);
    t>>=1;
   }
 return v;
}

/******************************************************************************/

LOCAL ulong widmask P_((p_cm_t));
LOCAL ulong widmask (p_cm)
/* Returns a longword whose value is (2^p_cm->cm_width)-1.     */
/* The trick is to do this portably (e.g. without doing <<32). */
p_cm_t p_cm;
{
 return (((1L<<(p_cm->cm_width-1))-1L)<<1)|1L;
}

/******************************************************************************/

void cm_ini (p_cm)
p_cm_t p_cm;
{
 p_cm->cm_reg = p_cm->cm_init;
}

/******************************************************************************/

void cm_nxt (p_cm,ch)
p_cm_t p_cm;
int    ch;
{
 int   i;
 ulong uch  = (ulong) ch;
 ulong topbit = BITMASK(p_cm->cm_width-1);

 if (p_cm->cm_refin) uch = reflect(uch,8);
 p_cm->cm_reg ^= (uch << (p_cm->cm_width-8));
 for (i=0; i<8; i++)
   {
    if (p_cm->cm_reg & topbit)
       p_cm->cm_reg = (p_cm->cm_reg << 1) ^ p_cm->cm_poly;
    else
       p_cm->cm_reg <<= 1;
    p_cm->cm_reg &= widmask(p_cm);
   }
}

/******************************************************************************/

void cm_blk (p_cm,blk_adr,blk_len)
p_cm_t   p_cm;
p_ubyte_ blk_adr;
ulong    blk_len;
{
 while (blk_len--) cm_nxt(p_cm,*blk_adr++);
}

/******************************************************************************/

ulong cm_crc (p_cm)
p_cm_t p_cm;
{
 if (p_cm->cm_refot)
    return p_cm->cm_xorot ^ reflect(p_cm->cm_reg,p_cm->cm_width);
 else
    return p_cm->cm_xorot ^ p_cm->cm_reg;
}

/******************************************************************************/

ulong cm_tab (p_cm,index)
p_cm_t p_cm;
int    index;
{
 int   i;
 ulong r;
 ulong topbit = BITMASK(p_cm->cm_width-1);
 ulong inbyte = (ulong) index;

 if (p_cm->cm_refin) inbyte = reflect(inbyte,8);
 r = inbyte << (p_cm->cm_width-8);
 for (i=0; i<8; i++)
    if (r & topbit)
       r = (r << 1) ^ p_cm->cm_poly;
    else
       r<<=1;
 if (p_cm->cm_refin) r = reflect(r,p_cm->cm_width);
 return r & widmask(p_cm);
}

/******************************************************************************/
/*                             End of crcmodel.c                              */
/******************************************************************************/

18. Roll Your Own Table-Driven Implementation

Despite all the fuss I've made about understanding and defining CRC
algorithms, the mechanics of their high-speed implementation remains
trivial. There are really only two forms: normal and reflected. Normal
shifts to the left and covers the case of algorithms with Refin=FALSE
and Refot=FALSE. Reflected shifts to the right and covers algorithms
with both those parameters true. (If you want one parameter true and
the other false, you'll have to figure it out for yourself!) The
polynomial is embedded in the lookup table (to be discussed). The
other parameters, Init and XorOt can be coded as macros. Here is the
32-bit normal form (the 16-bit form is similar).

   unsigned long crc_normal ();
   unsigned long crc_normal (blk_adr,blk_len)
   unsigned char *blk_adr;
   unsigned long  blk_len;
   {
    unsigned long crc = INIT;
    while (blk_len--)
       crc = crctable[((crc>>24) ^ *blk_adr++) & 0xFFL] ^ (crc << 8);
    return crc ^ XOROT;
   }

Here is the reflected form:

   unsigned long crc_reflected ();
   unsigned long crc_reflected (blk_adr,blk_len)
   unsigned char *blk_adr;
   unsigned long  blk_len;
   {
    unsigned long crc = INIT_REFLECTED;
    while (blk_len--)
       crc = crctable[(crc ^ *blk_adr++) & 0xFFL] ^ (crc >> 8));
    return crc ^ XOROT;
   }

Note: I have carefully checked the above two code fragments, but I haven't actually compiled or tested them. This shouldn't matter to you, as, no matter WHAT you code, you will always be able to tell if you have got it right by running whatever you have created against the reference model given earlier. The code fragments above are really just a rough guide. The reference model is the definitive guide.

Note: If you don't care much about speed, just use the reference model
code!

19. Generating A Lookup Table

The only component missing from the normal and reversed code fragments in the previous section is the lookup table. The lookup table can be computed at run time using the cm_tab function of the model package given earlier, or can be pre-computed and inserted into the C program. In either case, it should be noted that the lookup table depends only on the POLY and RefIn (and RefOt) parameters. Basically, the polynomial determines the table, but you can generate a reflected table too if you want to use the reflected form above.

The following program generates any desired 16-bit or 32-bit lookup table. Skip to the word "Summary" if you want to skip over this code.

/******************************************************************************/
/*                             Start of crctable.c                            */
/******************************************************************************/
/*                                                                            */
/* Author  : Ross Williams (ross@guest.adelaide.edu.au.).                     */
/* Date    : 3 June 1993.                                                     */
/* Version : 1.0.                                                             */
/* Status  : Public domain.                                                   */
/*                                                                            */
/* Description : This program writes a CRC lookup table (suitable for         */
/* inclusion in a C program) to a designated output file. The program can be  */
/* statically configured to produce any table covered by the Rocksoft^tm      */
/* Model CRC Algorithm. For more information on the Rocksoft^tm Model CRC     */
/* Algorithm, see the document titled "A Painless Guide to CRC Error          */
/* Detection Algorithms" by Ross Williams (ross@guest.adelaide.edu.au.). This */
/* document is likely to be in "ftp.adelaide.edu.au/pub/rocksoft".            */
/*                                                                            */
/* Note: Rocksoft is a trademark of Rocksoft Pty Ltd, Adelaide, Australia.    */
/*                                                                            */
/******************************************************************************/

# include <stdio.h>

# include <stdlib.h>

# include "crcmodel.h"

/******************************************************************************/

/*TABLE PARAMETERS*/
/*================*/
/*The following parameters entirely determine the table to be generated. You*/
/*should need to modify only the definitions in this section before running*/
/*this program.*/
/**/
/*TB_FILE  is the name of the output file.*/
/*TB_WIDTH is the table width in bytes (either 2 or 4).*/
/*TB_POLY  is the "polynomial", which must be TB_WIDTH bytes wide.*/
/*TB_REVER indicates whether the table is to be reversed (reflected).*/
/**/
/*Example:*/
/**/
/*#define TB_FILE   "crctable.out"*/
/*#define TB_WIDTH  2*/
/*#define TB_POLY   0x8005L*/
/*#define TB_REVER  TRUE*/

# define TB_FILE   "crctable.out"

# define TB_WIDTH  4

# define TB_POLY   0x04C11DB7L

# define TB_REVER  TRUE

/******************************************************************************/

/*Miscellaneous definitions.*/

# define LOCAL static

FILE *outfile;

# define WR(X) fprintf(outfile,(X))

# define WP(X,Y) fprintf(outfile,(X),(Y))

/******************************************************************************/

LOCAL void chk_err P_((char *));
LOCAL void chk_err (mess)
/* If mess is non-empty, write it out and abort. Otherwise, check the error   */
/* status of outfile and abort if an error has occurred.                      */
char *mess;
{
 if (mess[0] != 0   ) {printf("%s\n",mess); exit(EXIT_FAILURE);}
 if (ferror(outfile)) {perror("chk_err");   exit(EXIT_FAILURE);}
}

/******************************************************************************/

LOCAL void chkparam P_((void));
LOCAL void chkparam ()
{
 if ((TB_WIDTH != 2) && (TB_WIDTH != 4))
    chk_err("chkparam: Width parameter is illegal.");
 if ((TB_WIDTH == 2) && (TB_POLY & 0xFFFF0000L))
    chk_err("chkparam: Poly parameter is too wide.");
 if ((TB_REVER != FALSE) && (TB_REVER != TRUE))
    chk_err("chkparam: Reverse parameter is not boolean.");
}

/******************************************************************************/

LOCAL void gentable P_((void));
LOCAL void gentable ()
{
 WR("/*****************************************************************/\n");
 WR("/*                                                               */\n");
 WR("/* CRC LOOKUP TABLE                                              */\n");
 WR("/* ================                                              */\n");
 WR("/* The following CRC lookup table was generated automagically    */\n");
 WR("/* by the Rocksoft^tm Model CRC Algorithm Table Generation       */\n");
 WR("/* Program V1.0 using the following model parameters:            */\n");
 WR("/*                                                               */\n");
 WP("/*    Width   : %1lu bytes.                                         */\n",
    (ulong) TB_WIDTH);
 if (TB_WIDTH == 2)
 WP("/*    Poly    : 0x%04lX                                           */\n",
    (ulong) TB_POLY);
 else
 WP("/*    Poly    : 0x%08lXL                                      */\n",
    (ulong) TB_POLY);
 if (TB_REVER)
 WR("/*    Reverse : TRUE.                                            */\n");
 else
 WR("/*    Reverse : FALSE.                                           */\n");
 WR("/*                                                               */\n");
 WR("/* For more information on the Rocksoft^tm Model CRC Algorithm,  */\n");
 WR("/* see the document titled \"A Painless Guide to CRC Error        */\n");
 WR("/* Detection Algorithms\" by Ross Williams                        */\n");
 WR("/* (ross@guest.adelaide.edu.au.). This document is likely to be  */\n");
 WR("/* in the FTP archive \"ftp.adelaide.edu.au/pub/rocksoft\".        */\n");
 WR("/*                                                               */\n");
 WR("/*****************************************************************/\n");
 WR("\n");
 switch (TB_WIDTH)
   {
    case 2: WR("unsigned short crctable[256] =\n{\n"); break;
    case 4: WR("unsigned long  crctable[256] =\n{\n"); break;
    default: chk_err("gentable: TB_WIDTH is invalid.");
   }
 chk_err("");

 {
  int i;
  cm_t cm;
  char *form    = (TB_WIDTH==2) ? "0x%04lX" : "0x%08lXL";
  int   perline = (TB_WIDTH==2) ? 8 : 4;

  cm.cm_width = TB_WIDTH*8;
  cm.cm_poly  = TB_POLY;
  cm.cm_refin = TB_REVER;

  for (i=0; i<256; i++)
    {
     WR(" ");
     WP(form,(ulong) cm_tab(&cm,i));
     if (i != 255) WR(",");
     if (((i+1) % perline) == 0) WR("\n");
     chk_err("");
    }

 WR("};\n");
 WR("\n");
 WR("/*****************************************************************/\n");
 WR("/*                   End of CRC Lookup Table                     */\n");
 WR("/*****************************************************************/\n");
 WR("");
 chk_err("");
}
}

/******************************************************************************/

main ()
{
 printf("\n");
 printf("Rocksoft^tm Model CRC Algorithm Table Generation Program V1.0\n");
 printf("-------------------------------------------------------------\n");
 printf("Output file is \"%s\".\n",TB_FILE);
 chkparam();
 outfile = fopen(TB_FILE,"w"); chk_err("");
 gentable();
 if (fclose(outfile) != 0)
    chk_err("main: Couldn't close output file.");
 printf("\nSUCCESS: The table has been successfully written.\n");
}

/******************************************************************************/
/*                             End of crctable.c                              */
/******************************************************************************/

20. Summary

This document has provided a detailed explanation of CRC algorithms explaining their theory and stepping through increasingly sophisticated implementations ranging from simple bit shifting through to byte-at-a-time table-driven implementations. The various implementations of different CRC algorithms that make them confusing to deal with have been explained. A parameterized model algorithm has been described that can be used to precisely define a particular CRC algorithm, and a reference implementation provided. Finally, a program to generate CRC tables has been provided.

21. Corrections

If you think that any part of this document is unclear or incorrect, or have any other information, or suggestions on how this document could be improved, please context the author. In particular, I would like to hear from anyone who can provide Rocksoft^tm Model CRC Algorithm parameters for standard algorithms out there.

A. Glossary

  • CHECKSUM - A number that has been calculated as a function of some message. The literal interpretation of this word "Check-Sum" indicates that the function should involve simply adding up the bytes in the message. Perhaps this was what early checksums were. Today, however, although more sophisticated formulae are used, the term "checksum" is still used.

  • CRC - This stands for "Cyclic Redundancy Code". Whereas the term "checksum" seems to be used to refer to any non-cryptographic checking information unit, the term "CRC" seems to be reserved only for algorithms that are based on the "polynomial" division idea.

  • G - This symbol is used in this document to represent the Poly.

  • MESSAGE - The input data being checksummed. This is usually structured as a sequence of bytes. Whether the top bit or the bottom bit of each byte is treated as the most significant or least significant is a parameter of CRC algorithms.

  • POLY - This is my friendly term for the polynomial of a CRC.

  • POLYNOMIAL - The "polynomial" of a CRC algorithm is simply the divisor in the division implementing the CRC algorithm.

  • REFLECT - A binary number is reflected by swapping all of its bits around the central point. For example, 1101 is the reflection of 1011.

  • ROCKSOFT^TM MODEL CRC ALGORITHM - A parameterized algorithm whose purpose is to act as a solid reference for describing CRC algorithms. Typically CRC algorithms are specified by quoting a polynomial. However, in order to construct a precise implementation, one also needs to know initialization values and so on.

  • WIDTH - The width of a CRC algorithm is the width of its polynomical minus one. For example, if the polynomial is 11010, the width would be 4 bits. The width is usually set to be a multiple of 8 bits.

B. References

[Griffiths87] Griffiths, G., Carlyle Stones, G., "The Tea-Leaf Reader Algorithm: An Efficient Implementation of CRC-16 and CRC-32", Communications of the ACM, 30(7), pp.617-620. Comment: This paper describes a high-speed table-driven implementation of CRC algorithms. The technique seems to be a touch messy, and is superceded by the Sarwete algorithm.

[Knuth81] Knuth, D.E., "The Art of Computer Programming", Volume 2: Seminumerical Algorithms, Section 4.6.

[Nelson 91] Nelson, M., "The Data Compression Book", M&T Books, (501 Galveston Drive, Redwood City, CA 94063), 1991, ISBN: 1-55851-214-4. Comment: If you want to see a real implementation of a real 32-bit checksum algorithm, look on pages 440, and 446-448.

[Sarwate88] Sarwate, D.V., "Computation of Cyclic Redundancy Checks via Table Look-Up", Communications of the ACM, 31(8), pp.1008-1013. Comment: This paper describes a high-speed table-driven implementation for CRC algorithms that is superior to the tea-leaf algorithm. Although this paper describes the technique used by most modern CRC implementations, I found the appendix of this paper (where all the good stuff is) difficult to understand.

[Tanenbaum81] Tanenbaum, A.S., "Computer Networks", Prentice Hall, 1981, ISBN: 0-13-164699-0. Comment: Section 3.5.3 on pages 128 to 132 provides a very clear description of CRC codes. However, it does not describe table-driven implementation techniques.

C. References I Have Detected But Haven't Yet Sighted

Boudreau, Steen, "Cyclic Redundancy Checking by Program," AFIPS Proceedings, Vol. 39, 1971.

Davies, Barber, "Computer Networks and Their Protocols," J. Wiley & Sons, 1979.

Higginson, Kirstein, "On the Computation of Cyclic Redundancy Checks by Program," The Computer Journal (British), Vol. 16, No. 1, Feb 1973.

McNamara, J. E., "Technical Aspects of Data Communication," 2nd Edition, Digital Press, Bedford, Massachusetts, 1982.

Marton and Frambs, "A Cyclic Redundancy Checking (CRC) Algorithm," Honeywell Computer Journal, Vol. 5, No. 3, 1971.

Nelson M., "File verification using CRC", Dr Dobbs Journal, May 1992, pp.64-67.

Ramabadran T.V., Gaitonde S.S., "A tutorial on CRC computations", IEEE Micro, Aug 1988.

Schwaderer W.D., "CRC Calculation", April 85 PC Tech Journal, pp.118-133.

Ward R.K, Tabandeh M., "Error Correction and Detection, A Geometric Approach" The Computer Journal, Vol. 27, No. 3, 1984, pp.246-253.

Wecker, S., "A Table-Lookup Algorithm for Software Computation of Cyclic Redundancy Check (CRC)," Digital Equipment Corporation memorandum, 1974.

----

posted @ 2024-08-16 17:04  QIYUEXIN  阅读(14)  评论(0编辑  收藏  举报