I happened to notice that the Arduino OneWire library uses a 256 entry lookup table for its CRC calculations. I did some research on this topic in 1992-1993, while working on Bulletin Board Systems, FidoNet code and file transfer protocols. These days memory is not at a premium on most computers, however on Arduino and microcontroller environments it definitely is, and I happen to know that table-lookup CRC can be done using two 16-entry tables! So I’ve dug up my documentation and code from the time, and applied it to the CRC-8 calculation for the Maxim (Dallas Semiconductor) OneWire bus protocol. I think this provides a neat trade-off between code size and speed.
License
For any of the below code, apply the following license (2-clause “simplified” BSD license), which should suffice for any use. If you do require another license, just ask.
CRC lookup tables, generator and use implementation by Arjen Lentz <arjen (at) lentz (dot) com (dot) au>
Copyright (C) 1992-2017 Arjen Lentz
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The Code
The below is a drop-in replacement for the chunk of code in OneWire.cpp, the two 16-entry tables I mentioned above are combined in to a single 32 entry array.
Copyright (c) 2007, Jim Studt (original old version - many contributors since) The latest version of this library may be found at: http://www.pjrc.com/teensy/td_libs_OneWire.html OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since January 2010. [...] // The 1-Wire CRC scheme is described in Maxim Application Note 27: // "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" // #if ONEWIRE_CRC8_TABLE // Dow-CRC using polynomial X^8 + X^5 + X^4 + X^0 static const uint8_t PROGMEM dscrc_table[] = { 0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83, 0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41, 0x00, 0x9D, 0x23, 0xBE, 0x46, 0xDB, 0x65, 0xF8, 0x8C, 0x11, 0xAF, 0x32, 0xCA, 0x57, 0xE9, 0x74 }; // // Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM // and the registers. uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) { uint8_t crc = 0; while (len--) { crc = *addr++ ^ crc; // just re-using crc as intermediate crc = pgm_read_byte(dscrc_table + (crc & 0x0f)) ^ pgm_read_byte(dscrc_table + 16 + ((crc >> 4) & 0x0f)); } return crc; } #else [...]
Generating CRC tables
CRC tables are always only presented as magic output, but the feedback terms simply represent the results of eight shifts/xor operations for all combinations of data and CRC register values. Actually, the table generator routine uses the old-style method shifting each bit.
Below is the rough code. For the Dow-CRC, the specified polynomial is X^8 + X^5 + X^4 + X^0. The first term tells us that it’s an 8 bit CRC. We work the rest in to the designate bits but from right to left, so we end up with binary 1000 1100 which is 0x8C in hex. This is the POLY value in the code.
for (i = 0; i <= 15; i++) { crc = i; for (j = 8; j > 0; j--) { if (crc & 1) crc = (crc >> 1) ^ POLY; else crc >>= 1; } crctab[i] = crc; } for (i = 0; i <= 15; i++) { crc = i << 4; for (j = 8; j > 0; j--) { if (crc & 1) crc = (crc >> 1) ^ poly; else crc >>= 1; } crctab[16 + i] = crc; }
If you want to generate a 256-entry table for the same:
for (i = 0; i <= 255; i++) { crc = i; for (j = 8; j > 0; j--) { if (crc & 1) crc = (crc >> 1) ^ POLY; else crc >>= 1; } crctab[i] = crc; }
Yes, this trickery basically works for any CRC (any number of bits), you just need to put in the correct polynomial. However, some CRCs are “up side down”, and thus require a bit of adjustment along the way – the CRC-16 as used by Xmodem and Zmodem worked that way, but I won’t bother you with that old stuff here as it’s not really used any more. Some CRCs also use different initialisation values, post-processing and check approaches. If you happen to need info on that, drop me a line as the info contained in this blog post is just a part of the docu I have here.
CRC – Polynomial Division
To calculate a CRC (Cyclic Redundancy Check) of a block of data, the data bits are considered to be the coefficients of a polynomial. This message (data) polynomial is first multiplied by the highest term in the polynomial (X^8, X^16 or X^32) then divided by the generator polynomial using modulo two arithemetic. The remainder left after the division is the desired CRC.
CRCs are usually expressed as an polynomial expression such as:
X^16 + X^12 + X^5 + 1
The generator polynomial number is determined by setting bits corresponding to the power terms in the polynomial equation in an (unsigned) integer. We do this backwards and put the highest-order term in the the lowest-order bit. The highest term is implied (X^16 here, just means its’s a 16-bit CRC), the LSB is the X^15 term (0 here), the X^0 term (shown as + 1) results in the MSB being 1.
Note that the usual hardware shift register implementation shifts bits into the lowest-order term. In our implementation, that means shifting towards the right. Why do we do it this way? Because the calculated CRC must be transmitted across a serial link from highest- to lowest-order term. UARTs transmit characters in order from LSB to MSB. By storing the CRC this way, we hand it to the UART in the order low-byte to high-byte; the UART sends each low-bit to high-bit; and the result is transmission bit by bit from highest- to lowest-order term without requiring any bit shuffling.
Credits
Stuff found on BBS, numerous files with information and implementations.
Comments in sources on CRC-32 calculation by Gary S. Brown.
All other people hereby credited anonymously, because unfortunately the documents and sources encountered did not contain the name of a specific person or organisation. But, each source provided a piece of the puzzle!
Sources / Docs
My original source code and full docu is also available: crc_agl.zip, as part of my archive of Lentz Software-Development‘s work in the 1990s.