How to calulate CRC of 1 byte using stm32l1 CRC unit

1.1k views Asked by At

I'm trying to calculate the CRC of a one byte data input using the CRC calculation unit of the stm32l152.
The CRC unit accepts only 32Bit inputs. I was able to get it calculate the CRC for a 32Bit data input, but now I'm struggling to get it to work for a byte data input.

My test input data is 0x20 and I expect as output 0xD62B0954
CRC parameter: polynom 0x04C11DB7, init-val: 0xFFFFFFFF

How can I solve this, I'm running out of ideas?

Sample code for the 32 bit data:

CRC->DR = u32Input`  
u32Crc = CRC->DR;

I've also found this code snippet, to calculate the CRC byte-wise, but the output is different.

static uint32_t crc32_step(uint32_t crc, uint8_t data)
{
    crc = ~crc ^ data;
    CRC->DR = (~CRC->DR) ^ __RBIT(~(crc << 24));
    return (crc >> 8) ^ ~__RBIT(CRC->DR);
}

I get the following output using the code above with a provided crc of 0xFFFFFFFF: 0xC491DF37

I don't really understand what the crc32_step function does, but I'm not even sure if this is the right direction to go. I would greatly appreciate any hints or ideas.

2

There are 2 answers

2
Mark Adler On BEST ANSWER

The CRC being calculated appears to be a CRC-32/MPEG2. Here is some simple code in C to calculate it:

#include <stddef.h>
#include <stdint.h>

uint32_t crc32mpeg2(uint32_t crc, void const *mem, size_t len) {
    unsigned char const *data = mem;
    if (data == NULL)
        return 0xffffffff;
    while (len--) {
        crc ^= (uint32_t)(*data++) << 24;
        for (unsigned k = 0; k < 8; k++)
            crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
    }
    return crc;
}

The routine is called with NULL for mem to get the initial CRC value. So it would be used like this:

#include <stdio.h>

int main(void) {
    unsigned char data[1] = { 0x20 };
    uint32_t crc = crc32mpeg2(0, NULL, 0);
    crc = crc32mpeg2(crc, data, 1);
    printf("%08x\n", crc);
    return 0;
}

The output is:

d62b0954
1
Luis Colorado On

The most efficient way to calculate crcs in software is to use a table and doing the calculations eight bits at a time. Below is a table that allows you to calculate these:

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

static uint32_t crc32mpeg2_table[] = {
    0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
    0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
    0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
    0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
    0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
    0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
    0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
    0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
    0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
    0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
    0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
    0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
    0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
    0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
    0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
    0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
    0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
    0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
    0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
    0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
    0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
    0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
    0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
    0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
    0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
    0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
    0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
    0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
    0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
    0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
    0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
    0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
};

uint32_t crc32mpeg2(uint32_t state, uint8_t *buff, size_t buff_sz)
{
    while (buff_sz-- > 0) {
        int index = (state >> 24) ^ *buff++;
        state <<= 8;
        state ^= crc32mpeg2_table[index];
    }
    return state;
}

/* this main() function to demonstrate that the crc 
 * applied to a string with a single space character works */
int main()
{
    uint8_t buff[] = { 0x20 };
    uint32_t crc = crc32mpeg2(0xffffffff, buff, sizeof buff);
    printf("0x%08x\n", crc);
}

As you can see, the result, after aplying it to your input buffer is the expected value:

$ ./crcmpg3 
0xd62b0954
$ _

To generate the table, you can just run the proposed algorithm by Mark Adler (@MarkAdler) in his response, for the full set of possible input bytes (from 0x00 to 0xff) as in:

#include <stddef.h>
#include <stdio.h>
#include <stdint.h>

/* this is the function provided by Mark in his response */
uint32_t crc32mpeg2(uint32_t crc, void const *mem, size_t len) {
    unsigned char const *data = mem;
    if (data == NULL)
        return 0xffffffff;
    while (len--) {
        crc ^= (uint32_t)(*data++) << 24;
        for (unsigned k = 0; k < 8; k++)
            crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
    }
    return crc;
}

int main()
{
    int i;
    char *sep = "uint32_t crc32mpeg2_table[] = {\n\t";
    for (i = 0; i < 256; i++) {
        status = crc32mpeg2(i & 0xff, &ch, sizeof ch);
        printf("%s0x%08x", sep, status);
        sep = i % 8 == 7 ? ",\n\t" : ", ";
    }
    printf("\n};\n");
}

It will produce the table above. Once you have the table, you just need to use this function (far more quick, as you operate one full byte per iteration) to get the result:

uint32_t crc32mpeg2(uint32_t state, uint8_t *buff, size_t buff_sz)
{
    while (buff_sz-- > 0) {
        int index = (state >> 24) ^ *buff++;
        state <<= 8;
        state ^= crc32mpeg2_table[index];
    }
    return state;
}

The correct use of the function is to call it by passing the old state (which is the CRC upto the last byte processed) to get the new state (the new CRC after processing a new bunch of bytes) The first value for the state must be initialized to 0xffffffff, so to calculate the CRC of "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." you can use this main:

#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main()
{
    /* first piece */
    char *s = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, ";
    uint32_t crc = crc32mpeg2(0xffffffff, s, strlen(s)); /* intialize with 0xffffffff */
    printf("CRC(\"%s", s);

    /* second piece */
    s = "sed do eiusmod tempor incididunt ut labore et dolore ";
    crc = crc32mpeg2(crc, s, strlen(s));
    printf("%s", s);

    /* third piece */
    s = "magna aliqua.";
    crc = crc32mpeg2(crc, s, strlen(s));
    printf("%s\") => ", s);

    /* print crc of the whole string */
    printf("0x%08x\n", crc);
}

which should output:

$ crcmpg3.c
CRC("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") => 0x81e9201f
$ _