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

QIYUEXIN發表於2024-08-16

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.

----

相關文章