This is archived content, mostly untouched since 2003. For newer content/updated versions, see netfuture.ch.

  W and AWaldvogel & Aschwanden
   Roman Pletka
   Nicola Aschwanden
   Lars Waldvogel
    Kinderlieder+Spiele
   Marcel Waldvogel
    Contact
    Publications
    Research
    Tutorials
     CRC
      Background
      Description
    Classes
    Software
    Archive
    Fun

  

Cyclic Redundancy Check sequences (CRC)

[ Tutorial/Introduction | Background/Other checks ]

As mentioned in the section on ISBN check digits, a CRC can be coarsely approximated as a check digit in a sequence where each input digit is multiplied with a different weight. But the confusion starts when different people start talking about the different ways one can view the CRC calculation:

Hardware designers
...often think of CRC calculation as the repeated conditional XOR as the data bitstream flows by.
Software engineers
...generally consider it to be something that you deal with unconditionally byte-per-byte using a table lookup.
Mathematicians
...prefer to explain it as the remainder in a polynomial division.
Cryptographers
...secretively mumble about operations in the Galois Field of order 2, obscurely abbreviating it as GF(2).

All of them do the same: They produce a weighted sum of all the bits in the data stream which, when joined with the "checksum", will give a remainder of 0 after a division. To understand the process and the advantages of the different approaches, we look at the CRC problem from all four vantage points.

Besides the four ways of performing the calculations, there are also different values that control the calculation and thus the final error-detection properties of the "checksum". This value has different names, depending on the viewpoint: generator function, generating polynomial, or characteristic function. This value has a width b associated with it, which determines the number of bits of the resulting checksum.

CRC Hardware Operation

Generator:  1 0 0 1 1

Operation Register Message
0 0 0 0 0 0110111
<-M 0 0 0 0 0 110111
<-M 0 0 0 0 1 10111
<-M 0 0 0 1 1 0111
<-M 0 0 1 1 0 111
<-M 0 1 1 0 1 11
<-M 1 1 0 1 1 1
XOR 0 1 0 0 0 1
<-M 1 0 0 0 1
XOR 0 0 0 1 0
<-0 0 0 1 0 0
<-0 0 1 0 0 0
<-0 1 0 0 0 0
XOR 0 0 0 1 1
<-0 0 0 1 1 0

Serial hardware CRC: Starting with a zero register, the message is shifted in (<-M), followed by 4 zeros (<-0). Whenever the MSB is 1, the register is XORed with the generator (XOR). The low-order four bits of the register are then the CRC to be added.

The hardware view is to process a bit stream bit-by-bit, resulting in a series of check digits which are appended to the original message. During this process, the CRC engine only needs to keep b+1 bits of state, independent of the message length.

Sending: For the initial generation, the message is first considered to be padded with b zeros. These imaginary appended zeros are then replaced by the calculated CRC. The message (with the check digits appended) is then sent over the transmission channel to the recipient.

Reception is even simpler: The entire message including check sequence is passed through the same circuit that was used to generate the check sequence. The result of the circuit should then be all-zeros, indicating that the CRC verification succeeded.1

The actual operation is illustrated in the image to the left. We assume an imaginary four-bit CRC, whose generator is 10011. Yes, you are right: These are five bits. But the most significant bit (MSB) is not really of interest, and is therefore not counted. First, the generator MSB is always 1. Second, whenever the register MSB becomes 1, the generator is XORed against the register, clearing the MSB. As a result, the MSB does not need to be transmitted.

Also, magically (at least for now), the resulting message-plus-CRC sequence, when passed through the CRC hardware, will clear the register. As an exercise, try feeding the sequence "01101110110" (this is our message with the CRC attached, so you do not need to push in the four zeros that were used when generating the message) into the shift register according to the rules. The register will be zero in the end.

When our CRC unit is to be used at very high data rates (today starting in the order of 1 Gbit/s), it is very hard to design circuits operating at that speed. Therefore, hardware engineers often process multiple bits at a time (e.g. Dittia 2001, Braun and Waldvogel 2001, Doering and Waldvogel 2004) or use lookup tables like our software engineers described below.

Software CRC Computation

/* Created with crc4tab[i] = crc(i); */
char crc4tab = {
   /* 0:*/ 0,  3,  6,  5,
   /* 4:*/12, 15, 10,  9,
   /* 8:*/11,  8, 13, 14,
   /*12:*/ 7,  4,  1,  2};
int nibble, sum = 0;

/* getNextNibble() also needs to return
a placeholder for the CRC when sending */

while (0 >= (nibble = getNextNibble()))
    sum = nibble ^ crc4tab[sum];
}
return sum;

Lookup table and sample code for our four-bit CRC

In software, you could do the same process as done in hardware: Shift each bit in and then conditionally XOR with the generator (in the C code, this is represented by the ^ operator). Unfortunately, this is often very slow, as software typically accesses data in byte (or even larger) chunks. Extracting the bits from the bytes and then processing them individually looks counterintuitive. Therefore, we need a way to process software-sized chunks.

To make the examples small, the example to the left operates on four bits (aka nibble) at a time, which matches the generator size. In your application, you may chose the chunk size independently of the generator size2 (an example with 8-bit processing size and a 32-bit generator is shown in IETF RFC 1952: GZIP file format specification 4.3 (Deutsch 1996)).

The process is sped up by using a lookup table returning the combined result of applying several single-bit operations, instead of using bit-by-bit processing. This is comparable to the fast hardware approach described above, but using the preferred mechanism available in software (lookup tables) instead of those suitable to hardware (combinatorial logic).

First, a lookup table is used, with a size corresponding to the chunk size, s: For s bits, 2s entries are needed. For s=4, we thus need 16 entries in our table. This table is calculated using the bit-by-bit method, and can be precomputed (i.e., the table is already included in the code and has been computed at or before compile time) or at run-time, when the program first starts up. Each table entry consists to the CRC corresponding to each index.

Second, we need code, that for each chunk translates the previous intermediate "sum" to what it would look like after shifting in s more bits. Then, the current data chunk is XORed to that value and gives the next intermediate sum. When creating the CRC, also the appropriate number of zero-filled chunks needs to be added to obtain the CRC to be placed in that zero-filled area. For verification, you do not zero-fill, but send the message and the transmitted CRC through the function, which returns 0 on a match.3

Step sum in crc4tab[sum] nibble sum out
1 00002=0 00002=0 (0)0112=3 00112=3
2 00112=3 01012=5 01112=7 00102=2
3 00102=2 01102=6 00002=0 01102=6

CRC of (0)011 01112 is 01102

An example using our well-known generator and test message can be seen to the left. The message has been expanded to be a multiple of the chunk size. For this, a zero has been added at the MSB.4 In the first step, sum has been initialized with 0. The first nibble is then XORed in (there is not enough data yet to trigger an XOR with the generator).

In step two, the previous data has been shifted left, and needs to go through zero to four XORs with the generator (reduction), depending on the input sum. These XORs are performed by looking up the previous sum in the lookup table. As the last operation in step two, the new nibble is XORed in.

These first two steps are (because the XOR operation is commutative) identical to having a register 2b bits wide, which starts out zeroed. Then, four bits of data are shifted in from the lower end, similar to the hardware approach. The value is guaranteed to still fit into b bits, so no reduction is necessary. Next, b more bits are then shifted in. This will not cause the register to overflow, as the top half was guaranteed to be zero before. Then, shifted versions of the generator are applied in turn, starting with the one shifted all the way left (by b-1 bits). After that, the upper half is again guaranteed to be zero.

For a longer message, of course, the steps would be repeated, terminating with the passing of the zero-fill data into the function (in our case, this is step 3).

Step sum in crc4tab[sum] nibble sum out
1 00002=0 00002=0 (0)0112=3 00112=3
2 00112=3 01012=5 01112=7 00102=2
3 00102=2 01102=6 01102=6 00002=0

Output of (0)011 0111 01102 is 00002: Correct

When verifying data, instead of zeroing the field after the message and then comparing to the CRC, the entire message including CRC can be passed through, as shown in the figure to the left. By looking at step 3, we now can see why this happens: The output of the last crc4tab[sum] step is the CRC, when the nibble is zero (crc4tab[sum] XOR 0 = crc). When the nibble has been filled with the CRC, we get 0 as the output (crc4tab[sum] XOR crc = 0). The two parenthesized statements are mathematically equivalent, as 0 is the neutral element of the XOR operation (i.e., XORing with 0 does not change the result), and XOR is its own inverse operation (XORing in a value can be undone by another XOR with the same value).

Mathematical Background

CRC as a division
This is again the hardware CRC operation, but laid out similar to paper-and-pencil division known from school
Now you should have a general feeling of what happens in a CRC operation and what can or cannot be done. This will help you when we change to a slightly more formal description of the behavior. The figure on the left shows how the CRC calculation is done when we compare it to pen-and-pencil division. In this discussion, we represent XOR as either + or -, to closer match standard division.

In stage A, we try to see whether we can subtract a multiple of the generator from the topmost 5 bits. As we work on binary numbers, the only two multiples are 0 (resulting in no subtraction, represented by the light blue background) and 1 (resulting in subtraction of the generator, indicated by dark blue background). Subtraction (recall, this is XOR in this section) of the generator is possible if it makes the number smaller. With XOR, this is the case when the MSB is 1.

Our number, 011 0111 00002 divided by 100112 using XOR instead of addition/subtraction, results in 01100102. This can be determined in the usual way. Here, you can also look at the light (0) vs. dark (1) blue, with the line in step A corresponding to the MSB and stage H delivering the LSB). This division results in a remainder of 01102.

By using this analogy, we can also use many of the arithmetic transformations known from school. For example, it also becomes obvious why the replacement of the zero-fill with the CRC results in a zero remainder after passing: Recall that the CRC is the remainder after dividing the padded message by the generator. Instead of padding with zeros, we want to pad with a bit sequence that makes the padded message evenly divisible by the generator (i.e., no remainder). In normal arithmetic, you would subtract the remainder from the padded message.5 Here, we do the same, except that our subtraction is XOR. Thus, we are guaranteed to have the CRC-padded message evenly divisible by the generator.

Operations in GF(2)

What has been defined informally in the previous section is also known as operations in the finite field GF(2), also known as Galois field of two elements (hence the GF(2) notation).

+ 0 1
0 0 1
1 1 0
Truth table of the addition in GF(2)
GF(2) consists of two operators, an addition and a multiplication. As we work on elements of this particular field and not on integers, they work slightly differently. Addition in the field is the same as XOR on integers.

+ 0 1
0 0 0
1 0 1
Truth table of the multiplication in GF(2)
Multiplication of two single-digit numbers is identical to integer multiplication or the logical AND operation. Multiplication of multi-digit elements is performed in close analogy to integer multiplication, namely shift-and-add, with the (by now hopefully usual) caveat that it uses the field's addition, the equivalent to integer XOR.

Both operators do have inverse counterparts, which are called subtraction and division, as usual. In our case, subtraction as the inverse of addition is — surprise! — identical to the addition; much the same as XOR is its own reverse (applying the operation a second time undoes the first operation).

This view already allows operations to

Polynomial view

Real-World CRCs

TODO: Examples of generators, pre-/post-whitening, bit order.

Footnotes

1 This means that the checksum matches the data; it does not say that the data has not been tampered with. But for reasonable CRCs, the probability of accidental changes to the message that retain the validity of the CRC, the probability is negligible. To prevent "intelligent" tampering, cryptographic means are inevitable.

2 Generators smaller than the chunk size require splitting of the chunk into two parts, the more significant part consisting of s-b bits, which are XORed with the previous sum (shifted left by s-2b bits) and then used as an index into the table. The rest of the chunk is then applied as shown in the example code.

3 Of course, you may also pass the received message (without CRC) and again zero-filled through the CRC function and then compare the computed CRC with the one received.

4 Prepending zeros has no impact on the CRC, as shifting in zeros into a register that is zero has no impact, as it can never set the MSB of the register to one, which could make the contents non-zero. (Such modifications are prevented by using pre-whitening.)

5 This would result in changing the message unless the remainder was zero, as the subtraction would cause the borrow to be carried from the padding bits into the message. As XOR is addition/subtraction with neither carry nor borrow, CRCs do not have this problem. (Even in normal arithmetic, it could be overcome by adding the generator again for nonzero remainders.)


Copyright 2004 Marcel Waldvogel.