CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

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

How to use PIC18F CRC Hardware Module

 
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    CCS Forum Index -> Best Of
View previous topic :: View next topic  
Author Message
kda406



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

View user's profile Send private message

How to use PIC18F CRC Hardware Module
PostPosted: Thu Apr 16, 2020 1:16 pm     Reply with quote

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.

Arrow 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: 18065

View user's profile Send private message

PostPosted: Fri Apr 17, 2020 1:39 am     Reply with quote

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:   
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    CCS Forum Index -> Best Of All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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


Powered by phpBB © 2001, 2005 phpBB Group