element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Personal Blogs
  • Community Hub
  • More
Personal Blogs
Michael Kellett's Blog Implementing a CRC on an ST micro
  • Blog
  • Documents
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: michaelkellett
  • Date Created: 4 Apr 2021 10:44 AM Date Created
  • Views 3772 views
  • Likes 9 likes
  • Comments 6 comments
  • crc
  • micro
  • st_micro
  • st microelectronics
Related
Recommended

Implementing a CRC on an ST micro

michaelkellett
michaelkellett
4 Apr 2021

This blog is about implementing an 8 bit CRC on an STM32G071xx micro. Several other ST micros have the same CRC hardware system so it will apply directly to them. There is also some discussion of CRC in general. Pretty much all this stuff is on the we but I have gathered some sources which may be helpful. The novel parts of the blog are the tentative suggestion of a general methodology for implementing CRC support and a C coding suggestion specific to the ST hardware which seems to have eluded several players.

CRC for a definition I can’t do better than Wikipedia:

A cyclic redundancy check is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data. Blocks of data entering these systems get a short check value attached, based on the remainder of a polynomial division of their contents.

I needed a way of implementing error detection in a medium integrity system which can use either UART or I2C comms for device to controller communications. The messages are short - typically a few bytes and the controller is expected to be another small micro running code written in C, maybe under a simple RTOS but not a full blown OS. This is important because it implies that the controller (running code written by other people and about which I have no knowledge and little influence) must implement the CRC calculations as well.

When you start delving into CRCs you very quickly find that there is not just one way of calculating an 8 bit CRC but a huge number of ways (at least 67 million ways). So we need not only to calculate the CRC but calculate the right CRC. In this context is the right 8 bit CRC is the one we specify in an un-ambiguous way so that the device designed (me) and the controller designer end up with the same thing.

The following reference will help you get to this point:

https://reveng.sourceforge.io/crc-catalogue/all.htm

But it lists 19 different 8 bit CRCs.

I found this helpful to pick one:

https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf

Section 7.2.1.1 defines 8-bit SAE J1850 CRC, and importantly gives a set of test vectors for it.

To sum up so far, we now have a definition for a traceable definition for an 8-bit CRC cross referenced from two reputable sources and a set of test vectors so we can tell if we have got a good implementation.

To give my controller design a reasonable level of support I need to provide standard C code to implement the CRC, and while I’m at it I shall compare the performance of a pure code solution with using the hardware on the chip I use in the device.

When you start Googling about CRCs and ST processors one of the things you find out quite quickly is that many people have a lot of trouble getting the hardware to do what they want. I have a guess as to why this is and a code suggestion that may be helpful.

I found some useful stuff here:

https://electronics.stackexchange.com/questions/33821/calculating-a-simple-crc

And a useful CRC calculator here:

http://www.sunshine2k.de/coding/javascript/crc/crc_js.html

 

From the title of the blog, you’ll guess that you need the ST reference document:

https://www.st.com/resource/en/reference_manual/dm00371828-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

This is a nice processor, available in tiny packages as well as more normal sizes. I has up to 128k of flash, 36k of ram, 64MHz core and lots of other good features. And it’s cheap.

One of the features is the hardware CRC engine.

 

 

I promised a methodology suggestion for choosing and implementing a CRC:

  • Decide if CRC is the right solution
  • If not already specified for your application, then pick a suitable one
  • Locate a third party set of test vectors
  • Decide if you need code for pure software, hardware or both
  • Write the code, including a test function

 

 

My example code may look a bit weird, especially the proliferation of u postfixes and the number of casts.

The original code was written to be MISRA 2012 compliant (Google it !).

MISRA is a code standard for embedded C and attempts to stop you from falling into C’s many traps for the unwary.

It’s a real pain to use, not because of MISRA 2012 itself, but because checking tools (essential) are either very expensive or not very good. (The cheap tool I have (PC Lint 9) isn’t very nice to use, expensive tools may be better - I don’t know).

I’m also trying to use the “Embedded C Coding Standard” (www.barrgroup.com). I’m not sticking exactly to the Barr document as written – it’s a style guide and I don’t agree with all of it.

I’ve edited some of the really odd things MISRA 2012 and PC Lint require because they do not help casual readers to understand the code.

(Comments re. coding standards and style are welcome.)

 

So at last we get to the code:

//
//       FILE: crc.c
//  COPYRIGHT: 
//
//     AUTHOR: MK
//       DATE: 01/04/2021
//     ORIGIN: New
//
//    PROJECT: 
//   HARDWARE: STM32G071Gxx
// TOOL CHAIN: KEIL
//

// INCLUDES
#include "main.h"
#include "crc.h"

// USEFUL REFERENCE DATA
// http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
// https://electronics.stackexchange.com/questions/33821/calculating-a-simple-crc
// https://reveng.sourceforge.io/crc-catalogue/all.htm
// https://users.ece.cmu.edu/~koopman/crc/
// https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
// 
// CRC is SAE J1850 CRC8, test spec from Autosar document page 22
//
// definition according to crc-catalogue:
// width=8 poly=0x1d init=0xff refin=false refout=false xorout=0xff check=0x4b residue=0xc4 name="CRC-8/SAE-J1850"
//

// DEFINITIONS

#define CRC_POLY  (uint8_t)0x1d
//#define CRC_HARDWARE



static uint8_t crc8_table[256];                                     // 8-bit table 
static bool b_table_made = false;

#ifndef CRC_HARDWARE
void crc_init(void)
{
    uint16_t i;
    uint16_t j;
    uint8_t crc;

    if (!b_table_made) 
    {
        for (i = 0u; i < 256u; i++) 
        {
            crc = (uint8_t) i;
            for (j = 0u; j < 8u; j++)
            {
                crc = (crc << 1u) ^ (((crc & 0x80u) != 0u) ? CRC_POLY : 0u);
            }
          crc8_table[i] = crc & 0xffu;
         }
        b_table_made = true;
    }
}
#else
void crc_init(void)
{
    SET_BIT(RCC->AHBENR, RCC_AHBENR_CRCEN);
    delay_us(0);

    CRC->CR = 0x10u;                        // set bit operation 
    CRC->INIT = 0xffu;
    CRC->POL = CRC_POLY;
    
}  
#endif

#ifndef CRC_HARDWARE
uint8_t crc8(uint8_t *bytes, uint8_t n_bytes)
{
    uint8_t crc = 0xffu;                    // initial value
    
    while(0u < n_bytes--)
    {
        crc = crc8_table[(crc) ^ (*bytes++)];
    }
    return(crc ^ 0xffu);
}
#else
uint8_t crc8(uint8_t *bytes, uint8_t n_bytes)
{
    uint8_t *p_dr8 = CRC;                    // magic sauce - force 8 bit hardware access to CRC-DR register
                                            // illegal cast according to MISRA 12, no MISRA legal way to do it
    
    CRC->CR |= 1u;                           // reset CRC
    while (0u < n_bytes--)
    {
        *p_dr8 = *bytes++;                    // use the magic sauce 
    }
    return(((uint8_t)(CRC->DR)) ^ 0xffu);
}
#endif

// test passes if return value is 1, anything else is a fail
uint8_t crc8_test(void)
{
    uint8_t testcases[] =   {0x04,0x59,0x00,0x00,0x00,0x00,
                             0x03,0x37,0xf2,0x01,0x83,
                             0x04,0x79,0x0f,0xaa,0x00,0x55,
                             0x04,0xb8,0x00,0xff,0x55,0x11,
                             0x09,0xcb,0x33,0x22,0x55,0xaa,0xbb,0xcc,0xdd,0xee,0xff,
                             0x03,0x8c,0x92,0x6b,0x55,
                             0x04,0x74,0xff,0xff,0xff,0xff,
                             0x09,0xcb,0x33,0x22,0x55,0xaa,0xbb,0xc3,0xdd,0xee,0xff};   // error in this line
    
    // each test line, number of test bytes, crc result expected, test bytes
                             
    uint8_t n_cases = 8u;                             
    uint8_t *p_test_data = testcases;
    uint8_t n_test_bytes;
    uint8_t ref;   
    uint8_t n_fails = 0u;
    uint8_t crc;                         
                             
    while(n_cases-- > 0u)
    {
        n_test_bytes = *p_test_data++;
        ref = *p_test_data++;
        crc = crc8(p_test_data, n_test_bytes);
        if (crc != ref)
        {
            n_fails++;
        }
        p_test_data += n_test_bytes;
    }                         
    return(n_fails);
}

 

The include files reference the ST library definitions and give external refernces for the crc functions.

 

 

The software implementation is based on the code at the electronics.stackexchange.com link.

The hardware and test code are mine, the test using the vectors from the Autosar document.

 

I shan’t go through the code line by line, just draw your attention to lines 89 and 95.

The ST ref manual says this (section 13.3.3):

The CRC_DR register can be accessed by word, right-aligned half-word and right-aligned byte. For the other registers only 32-bit access is allowed.

You can set the polynomial size in the control register but the paragraph above is the only clue about how to control the data size.

The ARM core (in this chip) can write to peripheral memory in 8 bit, 16 bit and 32 bit widths. This is a hardware thing.

The Keil C compiler will use an 8 bit hardware write if it is writing 8 bit data – this behaviour may not be the case with all C ompilers.

The standard peripheral definitions provided by ST for the processor define CRC as a struct and indirectly CRC->DR as a pointer to 32 bit data at the same location (DR is the first register in the struct).

So we need to force the data to be written as 8 bits, in line 89 *p_dr8 is defined as a pointer to 8 bit data and in line 95 it is used to force an 8 bit write to the CRC data register (CRC->DR).

If you don’t do this but just use CRC->DR = (uint32_t)*bytes++ the compiler does a 32 bit write and the CRC engine does not do what you need.

 

Finally, how quick is the hardware:

All timing done with 16MHz processor core clock.

The software implementation takes 2.096ms to initialise and 66us to run the test.

The hardware implementation takes 3.55us to initialise and 64.8us to run the test.

The software implementation needs 256 bytes of RAM or FLASH for the table.

If you need to change the CRC (because the application needs more than one type) the hardware is much more efficient in both memory and time.

It’s probably faster with longer data streams and polynomials with more bits but I didn’t test that.

 

 

I'm happy to explain anything which isn't clear and engage in discussions about style, MISRA, c coding and almost anything else except politics.

 

MK

  • Sign in to reply

Top Comments

  • michaelkellett
    michaelkellett over 4 years ago in reply to shabaz +2
    Hello shabaz , thanks for the heads-up re. C-Lion. I've enver looked at JetBrains beyond PyCharm before - quite a lot of interesting stuff. I'll give C-Lion a go just as a MISRA checker. Not sure how well…
  • michaelkellett
    michaelkellett over 4 years ago +2
    maybe you could pull the start value out of the routine and have it as a #define I'm always rather reluctant to #define something that appears only once in the code. Having said that it sort of appears…
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to shabaz +1
    shabaz wrote: Hi Michael, Great blog post, .... It is! A great subject and a great post. I've been reviewing the subject too, using a few controllers that have hardware accelerators for CRC and other integrity…
  • michaelkellett
    michaelkellett over 4 years ago

    maybe you could pull the start value out of the routine and have it as a #define

     

    I'm always rather reluctant to #define something that appears only once in the code.

     

    Having said that it sort of appears twice so probably should have got a #define anyway.image

     

    I think the Barr guide wants all numbers #defined - but then you get into #define ZEROU 0u

     

    (It must be nearly time to go home !)

     

    MK

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jc2048
    jc2048 over 4 years ago

    Thanks for an interesting blog to read.

     

    Some of the Microchip PIC microcontrollers have hardware CRC generators. I can remember it appearing in at least some of the 16-bit PIC24 family.

     

    I don't have much to say on the coding [I'm no great shakes as a programmer] other than maybe you could pull the start value out of the routine and have it as a #define. To generalise it further, you could also define the bit-order in the message bytes and anything that happens to the final CRC value.

     

    As an aside, the table method seems to go back quite a long way.

     

    Numerical Recipes in C [Press, Teukolsky, Vetterling, and Flannery, 1992] gives an algorithm for a 16-bit CRC using it.

    The explanation itself is quite brief, and they do a very quick whizz through the maths, so after four pages my head was spinning.

     

    They give as a reference this article in Byte magazine by Jerry LeVan [November 1987]

     

    https://archive.org/details/byte-magazine-1987-11/page/n395/mode/2up

     

    where it's used for calculating a CRC for XMODEM. From reading, it looks like XMODEM could be either checksum or CRC, with people at the time struggling to do the CRC fast enough at high baud rates. That one's a 16-bit CRC because the packets are [can be?] quite long.

     

    He gives the name of someone who suggested it to him, but there the trail goes a little cold [perhaps it leaked out of an alternative universe].

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • michaelkellett
    michaelkellett over 4 years ago in reply to shabaz

    Hello shabaz , thanks for the heads-up re. C-Lion. I've enver looked at JetBrains beyond PyCharm before - quite a lot of interesting stuff.

     

    I'll give C-Lion a go just as a MISRA checker. Not sure how well it will play with Keil toolset - but mostly I check modules

    one at a time so it may not matter much.

     

    The limit to ST micros won't worry me at the moment - all my major projects use them !

     

    MK

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 4 years ago

    Incidentally, by coincidence, I noticed the latest version of CLion has some MISRA 2012 support listed here. I'm using an older version of it, however, which doesn't support it, so I cannot say if it's any good at it or not.

     

    CLion is quite a nice IDE, however it cannot be used to build projects for all microcontrollers via GUI, but only ST Micro chips are supported via GUI.

    It can compile Windows programs too, although I mainly use it for the very good editor and search capabilities. It is not free, but has a perpetual license (and free trial license). (£69 for individual or £149 per org user), for the first year and it converts to a perpetual license after that).. but probably worth exploring with the 30-day free trial first!

     

    image

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to shabaz

    shabaz  wrote:

     

    Hi Michael, Great blog post, ....

    It is! A great subject and a great post.

    I've been reviewing the subject too, using a few controllers that have hardware accelerators for CRC and other integrity/security/encryption related functions.

    The word "accelerator" seems to indicate that the functionality needs clock pulses, instead of fully off-loading the work to a peripheral.

    I used it on a Tiva, Zero Gecko, ST - and have a few others to try it on. They do perform faster - with less flash space requirements, than the pure software libraries I compared them to.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube