CRC32 computation differences between SAMD21 and SAMD20 - Page 1
Forum section: electronics / general

author: jesuscf
author_rank: Frequent Contributor
timestamp: March 28, 2019, 04:04:20 am
content: Using the code bellow to compute the CRC32 with the DSU of the SAMD20E18A works fine when called multiple consecutive times. DSU_init() is called only once before start calling Obtain_CRC32().Code:void DSU_init (void){// Enable DSU modulePM->AHBMASK.reg |= PM_AHBMASK_DSU;PM->APBBMASK.reg |= PM_APBBMASK_DSU;PAC1->WPCLR.reg = 0x02; // Enable writes to DSU}static uint32_t Obtain_CRC32 (uint32_t address, uint32_t nbytes){DSU->DATA.reg = 0xFFFFFFFFL; // Inital value of CRCDSU->ADDR.reg = address; // Address must be word alignedDSU->LENGTH.reg = nbytes; // Lenth must be word alignedDSU->STATUSA.bit.DONE = 1; // Clear the done bitDSU->CTRL.bit.CRC = 1; // Begin CRC calculationwhile(!DSU->STATUSA.bit.DONE); // Wait until CRC calculation completereturn DSU->DATA.reg; // Return CRC out of Data register}The same code only works in the SAMD21E18A the first time Obtain_CRC32() is called. After that the returned CRC32 is always incorrect. The solution I found was to Stop/Reset the DSU before starting a new CRC32 calculation in the SAMD21E18A as shown in the code below. I wonder if that is indeed an issue with the SAMD21E18A or I am just missing something?Code:static uint32_t Obtain_CRC32 (uint32_t address, uint32_t nbytes){DSU->CTRL.bit.SWRST = 1; // Stop/Reset DSU moduleDSU->DATA.reg = 0xFFFFFFFFL; // Inital value of CRCDSU->ADDR.reg = address; // Address must be word alignedDSU->LENGTH.reg = nbytes; // Lenth must be word alignedDSU->STATUSA.bit.DONE = 1; // Clear the done bitDSU->CTRL.bit.CRC = 1; // Begin CRC calculationwhile(!DSU->STATUSA.bit.DONE); // Wait until CRC calculation completereturn DSU->DATA.reg; // Return CRC out of Data register}

author: ataradov
author_rank: Super Contributor
timestamp: March 28, 2019, 04:59:58 am
content: A couple notes:1. Do not use code like thisCode:DSU->STATUSA.bit.DONE = 1; // Clear the done bitDSU->CTRL.bit.CRC = 1; // Begin CRC calculationto write individual bits. In this instance it is not the source of the problem, but even here all flags in the STATUSA register will be cleared, not just DONE.Use code like this:Code:DSU->STATUSA.reg = DSU_STATUSA_DONE;DSU->CTRL.reg = DSU_CTRL_CRC;2. I remember there was some weirdness with the way CRC32 unit works on some parts. I think it was also related to what memory area was checked (Flash vs SRAM).And there was some magic write into some magic location that would make it work. I don't remember the details, but I will find that magic code tomorrow, when I'm back in the office.This might actually be in the errata.

author: ataradov
author_rank: Super Contributor
timestamp: March 28, 2019, 05:02:20 am
content: Yes, there is errata "1.8.3 CRC32 Computation Failure"QuoteI'm not sure what this code disables, but the original workaround was to only do the before part, so it is not critical for MCU operation, but may affect performance.

author: jesuscf
author_rank: Frequent Contributor
timestamp: March 28, 2019, 05:16:32 am
content: Quote from: ataradov on March 28, 2019, 05:02:20 amThat is a different problem which I encountered as well in the SAMD21E18A, but not in the SAMD20E18A; but is well documented:Code:volatile uint32_t myram[]={0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444};uint32_t crc1, crc2, start, length;...start=(uint32_t) myram;length=sizeof(myram);printf("\nCRC32 test 3. Start=0x%08lx, Length=0x%08lx\n", start, length);// From 'Silicon Errata and Data Sheet Clarification' section 1.8.3, CRC32 Computation Failure:// Before using the CRC32 on RAM, execute the following code:*((volatile unsigned int*) 0x41007058) &= ~0x30000UL;crc1=Obtain_CRC32(start, length);// After using the CRC32, execute the following code:*((volatile unsigned int*) 0x41007058) |= 0x20000UL;printf("CRC32 (using DSU)=0x%08lx\n", crc1);crc2=doFCS((unsigned char *)start, length, 1);printf("CRC32 (computed) =0x%08lx\n", crc2);

author: ataradov
author_rank: Super Contributor
timestamp: March 28, 2019, 05:18:30 am
content: So even with this workaround, it still does not work when you run it two times in a row?

author: jesuscf
author_rank: Frequent Contributor
timestamp: March 28, 2019, 06:21:48 am
content: Quote from: ataradov on March 28, 2019, 05:18:30 amIt does! Even worst, I can't reproduce the original problem that lead me to this post! I must have been doing something really stupid (also, I really need to have some sleep). Anyhow here is the code I am using now:Code:static uint32_t Obtain_CRC32 (uint32_t address, uint32_t nbytes){uint32_t x;// From 'Silicon Errata and Data Sheet Clarification' section 1.8.3, CRC32 Computation Failure:// Before using the CRC32 on RAM, execute the following code:*((volatile unsigned int*) 0x41007058) &= ~0x30000UL;DSU->DATA.reg = 0xFFFFFFFFL; // Initial value of CRCDSU->ADDR.reg = address; // Address must be word alignedDSU->LENGTH.reg = nbytes; // Length must be word alignedDSU->STATUSA.reg = DSU_STATUSA_DONE; // Clear the done bitDSU->CTRL.reg = DSU_CTRL_CRC; // Begin CRC calculationwhile(!DSU->STATUSA.bit.DONE); // Wait until CRC calculation completex=DSU->DATA.reg; // Return CRC out of Data register// After using the CRC32, execute the following code:*((volatile unsigned int*) 0x41007058) |= 0x20000UL;return x;}And here is a sample output:Code:CRC32 test 1. Start=0x00000000, Length=0x00040000CRC32 (using DSU)=0x7fd25d07CRC32 (computed) =0x7fd25d07CRC32 test 2. Start=0x00001000, Length=0x00002000CRC32 (using DSU)=0x3f48b810CRC32 (computed) =0x3f48b810CRC32 test 3. Start=0x2000204c, Length=0x00000014CRC32 (using DSU)=0x6a629edcCRC32 (computed) =0x6a629edcAtaradov I can't thank you enough!

author: ataradov
author_rank: Super Contributor
timestamp: March 28, 2019, 06:29:45 am
content: When it fails, note the memory type you are checking at the time, since apparently it is important.I know that I check flash in my bootloaders in sequential update mode (no setting of DSU->DATA.reg to 0xFFFFFFFF between the chunks), and it works reliably without workarounds. And that obviously involves multiple calls to the same code, which looks pretty much like yours.And the only SRAM issue I'm aware of is the one from the errata.Checking RAM can be tricky if there are variables that are being updated (may be from interrupts). So check that this does not happen.