CCS does not monitor this forum on a regular basis. Please do not post bug reports on this forum. Send them to support@ccsinfo.com
Author Message
kda406

Joined: 17 Sep 2003
Posts: 97
Location: Atlanta, GA, USA

How to use PIC18F CRC Hardware Module
Posted: Thu Apr 16, 2020 1:16 pm

I wanted to use the CRC module in the newer PIC18F devices. I have been using a CRC calculator that can do any polynomial up to an order of 32 bits for more than a decade and it works great. But it’s slower than hardware and takes up precious code space. I had problems getting started with the hardware CRC module. I worked them out and will share my results here. Once you see the code fragments, I hope you will find it much easier to use on your projects.

CRCs have a name, a polynomial, an initialization value, an XOR function, and directional indicators that are sometimes called reverse, reflected, or inversion. CRC definitions typically come with a “check” value. The check is the answer to the successful creation of a CRC from 9 bytes of ASCII data, 1-9. The CCS manual and the EX_CRC_HW.c example skips most of these required CRC items.

Starting with the polynomial, let's examine CRC-16-USB. It is x^16 + x^15 + x^2 + 1, often listed as X16+X15+X2+1, and referred to as 0x8005. The order of 16 bits is known and if you convert 8005h to binary, you’ll see there is a 1 on the 15th bit, a 1 on the 3rd bit, and a 1 at the end. Those correspond to the X15, X2, and 1 in the listed polynomial. Being a 16 bit CRC, the X16 is implied. You must understand this to setup the setup_crc() CCS function properly.

Knowing this, we can look at a simple example. The polynomial for Dallas/Maxim CRC-8 for 1-Wire devices is described as 0x31. Converting 0x31 to binary we see there is a 1 in the 5th, 4th bits and the LSB. That means its polynomial is X5+X4+1, right? Wrong!
 Code: setup_crc(5,4);   // Incorrect

As I said above, we must also include the “order” bit, which is not included in the polynomial value used to describe it. This is an 8 bit polynomial, so the correct setup for 0x31 is:
 Code: setup_crc(8,5,4);  // Correct

Note that the +1 is shown in the hex description of the polynomial, but is not included in the CCS setup command.

When the CRC is reflected, that means we need to use the CRC_LITTLE_ENDIAN definition in your device’s header file. The documentation on this is abysmal and I will show you how to do this below. NOTE: You must also reflect the answer when you use this method and CCS provides no automation for this! The Microchip hardware CRC module has a method to reflect the output, but it is not available through the CCS methods described in the manual.

The Microchip hardware CRC module also has XOR registers for the XOR function I mentioned above. Unfortunately, CCS seems to skip over this entirely. It is not that hard to XOR your result in software, so I show the work-aound in the USB and X.25 code snippets below.

I made these examples as straight code snippets instead of functions for educational simplicity:

 Code: char   iCrcTest[9] = {'1','2','3','4','5','6','7','8','9'};    UINT8 myCrc8;    UINT16   myCrc16;    fprintf(TERM,"\n\rCommon 8 Bit CRC tests:\n\r");    // CRC-8-MAXIM, X8+X5+X4+1, poly=0x31, init=0x00, refin=true, refout=true, xorout=0x00, check=0xa1    setup_crc(8,5,4,CRC_LITTLE_ENDIAN);                     // 0x31, reflected in    crc_init(0x00);                                       // Set accumulator to init value    myCrc8=crc_calc8(iCrcTest,9);                           // Hardware CRC calculation    myCrc8=CRC_FlexiReflect(myCrc8, 8);                     // Reflected output    fprintf(TERM,"0x%02X CRC-8-MAXIM (0xA1)\n\r",myCrc8);        // CRC-8-SMBUS, X8+X2+X1+1, poly=0x07, init=0x00, refin=false, refout=false, xorout=0x0, check=0xf4    setup_crc(8,2,1);                                       // 0x07, nonreflective    crc_init(0x00);                                       // Set accumulator to init value    myCrc8=crc_calc8(iCrcTest,9);                           // Hardware CRC calculation    fprintf(TERM,"0x%02X CRC-8-SMBUS (0xF4)\n\r",myCrc8);        // CRC-8/BLUETOOTH, X8+X7+X5+X2+X1+1, poly=0xa7, refin=true, refout=true, xorout=0x00, check=0x26    setup_crc(8,7,5,2,1,CRC_LITTLE_ENDIAN);               // 0xA7, reflective in    crc_init(0x00);                                       // Set accumulator to init value    myCrc8=crc_calc8(iCrcTest,9);                           // Hardware CRC calculation    myCrc8=CRC_FlexiReflect(myCrc8, 8);                     // Reflected output    fprintf(TERM,"0x%02X CRC-8-BLUETOOTH (0x26)\n\r",myCrc8);        fprintf(TERM,"\n\rCommon 16 Bit CRC tests:\n\r");    // CRC-16-XMODEM, X16+x12+X5+1, poly=0x1021, init=0x00, refin=false, refout=false, xorout=0x00, check=0x31c3    setup_crc(16,12,5);                                    // 0x1021, nonreflective    crc_init(0x00);                                       // Set accumulator to init value    myCrc16=crc_calc16(iCrcTest,9,8);                     // Hardware CRC calculation    fprintf(TERM,"0x%04LX CRC-16-XMODEM (0x31C3)\n\r",myCrc16);    // CRC-16-KERMIT, X16+x12+X5+1, poly=0x1021, init=0x00, refin=true, refout=true, xorout=0x00, check=0x2189    setup_crc(16,12,5,CRC_LITTLE_ENDIAN);                  // 0x1021, reflective in    crc_init(0x00);                                       // Set accumulator to init value    myCrc16=crc_calc16(iCrcTest,9,8);                     // Hardware CRC calculation    myCrc16=CRC_FlexiReflect(myCrc16, 16);                  // Reflected output    fprintf(TERM,"0x%04LX CRC-16-KERMET (0x2189)\n\r",myCrc16);        // CRC-16-X.25, X16+x12+X5+1, poly=0x1021, init=0xFFFF, refin=true, refout=true, xorout=0xFFFF, check=0x906e    setup_crc(16,12,5,CRC_LITTLE_ENDIAN);                  // 0x1021, reflective in    crc_init(0xFFFF);                                       // Set the accumulator to init value    myCrc16=crc_calc16(iCrcTest,9,8);                     // Hardware CRC calculation    myCrc16=CRC_FlexiReflect(myCrc16, 16);                  // Reflected output    myCrc16 ^= 0xFFFF;                                    // XOR output    fprintf(TERM,"0x%04LX CRC-16-X.25 (0x906E)\n\r",myCrc16);        // CRC-16-MODBUS, X16+x15+X2+1, poly=0x8005, init=0xFFFF, refin=true, refout=true, xorout=0x00, check=0x4b37    setup_crc(16,15,2,CRC_LITTLE_ENDIAN);                  // 0x8005, reflective in    crc_init(0xFFFF);                                       // Set the accumulator to init value    myCrc16=crc_calc16(iCrcTest,9,8);                     // Hardware CRC calculation    myCrc16=CRC_FlexiReflect(myCrc16, 16);                  // Reflected output    fprintf(TERM,"0x%04LX CRC-16-MODBUS (0x4B37)\n\r",myCrc16);        // CRC-16-USB, X16+x15+X2+1, poly=0x8005, init=0xFFFF, refin=true, refout=true, xorout=0xFFFF, check=0xb4c8    setup_crc(16,15,2,CRC_LITTLE_ENDIAN);         // CRC is 0x8005 X16+x15+X2+1 XOR 0xFFFF Reflective Init 0xFFFF  (USB)    crc_init(0xFFFF);            // Load the CRC accumulator    myCrc16=crc_calc16(iCrcTest,9,8); //Calculates the CRC    myCrc16=CRC_FlexiReflect(myCrc16, 16);      // Reflect the output    myCrc16 ^= 0xFFFF;    fprintf(TERM,"0x%04LX CRC-16-USB  (0xB4C8)\n\r",myCrc16);

Some of those functions need the reflected output. I have used this function on many projects on many compilers over decades. I don't remember if I coded this myself, got it from a class, from the web, or a colleague. I make no ownership claims.
 Code: // reflects the lower 'bitnum' bits of 'crc' UINT32 CRC_FlexiReflect(UINT32 crc, INT8 bitnum) {    UINT32 i, j=1, crcout=0;    for (i=(UINT32)1<<(bitnum-1); i; i>>=1) {       if (crc & i) crcout|=j;       j<<= 1;    }    return (crcout); }

The code snippets above yield the following results:
 Code: Common 8 Bit CRC tests: 0xA1 CRC-8-MAXIM (0xA1) 0xF4 CRC-8-SMBUS (0xF4) 0x26 CRC-8-BLUETOOTH (0x26) Common 16 Bit CRC tests: 0x31C3 CRC-16-XMODEM (0x31C3) 0x2189 CRC-16-KERMET (0x2189) 0x906E CRC-16-X.25 (0x906E) 0x4B37 CRC-16-MODBUS (0x4B37) 0xB4C8 CRC-16-USB  (0xB4C8)

Notice how Xmodem, Kermit, and X.25 are all known as CRC-16-CCITT and are almost identical. They use different inversion, initialization, and XOR methods. The Microchip CRC module handles all these internally, but right now it appears CCS offers no way of doing so directly. You must XOR and reverse your data based on the order of your selected polynomial.

I have tested the above on PIC18F67K40 and PIC18F45K40 physical devices. My goal here is to help people get started with the CRC hardware module with less hassle than I experienced. I am sure there are code improvements to be made along the way.

If you are just getting started with CRC or need some references, here are some good ones that I use.

Overview of CRC polynomials and uses:
https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Polynomial_representations_of_cyclic_redundancy_checks

Extremely detailed CRC definitions for polynomials, initialization, reflection, XOR, check values:
http://reveng.sourceforge.net/crc-catalogue/all.htm#crc.cat.crc-8-bluetooth

Online CRC checking calculator to verify your results. Here are the 8 and 16 bit pages:
https://crccalc.com/?crc=123456789&method=crc8&datatype=ascii&outtype=hex
https://crccalc.com/?crc=123456789&method=crc16&datatype=ascii&outtype=hex

-Kyle

Last edited by kda406 on Fri Apr 17, 2020 9:55 am; edited 3 times in total
Ttelmah

Joined: 11 Mar 2010
Posts: 19412

 Posted: Fri Apr 17, 2020 1:39 am A nice bit of information. I've suggested this might be worth relocating to the code library as a reference for people working with the hardware CRC module. Thoughts?.
 Display posts from previous: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First
 All times are GMT - 6 Hours Page 1 of 1

 Jump to: Select a forum Software----------------General CCS C DiscussionCode LibraryEZ App LynxBest OfKnown Issues Hardware----------------CCS ICD / Mach X / Load-n-Go
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum