We are using an Atmel SAM G55 in one of our applications. At some point, we want to calculate a CRC32 on an x86 system and pass it to the G55 (Arm 32Bit).
For the G55 we use the CRCCU to compute the CRC32:
uint32_t crccu_crc32(uint8_t *p_buffer, uint32_t ul_length, uint32_t *pcrc)
{
uint32_t ul_crc;
uint32_t ul_timeout = 0;
if (*pcrc == ~0) {
/* Reset the CRCCU */
crccu_reset(CRCCU);
}
memset((void *)&_crc_dscr, 0, sizeof(crccu_dscr_type_t));
_crc_dscr.ul_tr_addr = (uint32_t) p_buffer;
/* Transfer width: byte, interrupt enable */
_crc_dscr.ul_tr_ctrl = (uint32_t)ul_length | (2 << 24);
/* Configure CRCCU mode */
crccu_configure_mode(CRCCU, CRCCU_MR_ENABLE | CRCCU_MR_PTYPE_CCITT8023 | CRCCU_MR_DIVIDER(0));
/* Start the CRC calculation */
crccu_enable_dma(CRCCU);
/* Wait for calculation ready */
while ((crccu_get_dma_status(CRCCU) == CRCCU_DMA_SR_DMASR) &&
(ul_timeout++ < 0xFFFFFFFF)) {
}
if (ul_timeout == 0xFFFFFFFF) {
return 1;
}
/* Get CRC value */
*pcrc = crccu_read_crc_value(CRCCU);
return 0;
}
This function is loosely related to the CRC-HAL of ASF4 and works fine (at least we think so). However, we are not able to calculate the same CRC32 on our x86 machine.
To test the CRC32 calculation we use the following test-array:
uint32_t chksmtest[8] = {6448116,148714,884134,43,5487416,45486,8484384,64817};
Calculating the CRC32 with the CRCCU yields 0x4d4ee8cc
as result.
We tried several CRC32 implementations on our x86 machine and can not replicate this result. We know the the CRCCU calculates the CRC32 LSB first (G55 reference page 901) and the polynomial is 0x04C11DB7
(G55 reference page 916).
Even with reversing the bit and/or byte order, we are not able to replicate the result. Which CRC32 implementation do we have to use, to archive the same result as the CRCCU? We tried the libiberty implementation as well as the W3 implementation.
Your hardware is computing a non-reflected 32-bit CRC using the polynomial 0x04c11db7
, with an initial value of 0xffffffff
and no final exclusive or. That CRC has a name: CRC-32/MPEG-2.
As noted, the 32-bit values you are providing are processed in little-endian order, with the least significant byte first.
If and only if the C code below is run on a little-endian machine (e.g. x86), it will produce your desired result:
#include <stddef.h>
unsigned crc32_mpeg2(unsigned crc, void const *mem, size_t len) {
unsigned char const *data = mem;
if (data == NULL)
return 0xffffffff;
while (len--) {
crc ^= (unsigned)(*data++) << 24;
for (unsigned k = 0; k < 8; k++)
crc = crc & 0x80000000 ? (crc << 1) ^ 0x4c11db7 : crc << 1;
}
return crc & 0xffffffff;
}
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint32_t chksmtest[8] =
{6448116,148714,884134,43,5487416,45486,8484384,64817};
printf("0x%08x\n", crc32_mpeg2(crc32_mpeg2(0, NULL, 0), chksmtest, 8 * 4));
return 0;
}
Output:
0x4d4ee8cc
The above is a simple bit-wise implementation. You can look at crcany for the generation of more efficient CRC implementations in software for that CRC.