Page 1 of 2

SPI data corruption

Posted: Tue Feb 04, 2014 7:14 am
by rocky
Hello,

To communicate my discovery board with an external device, it's required to use SPI in DMA mode with CRC enabled. Firstly we wrote a program using STM library directly, it works very well. Then we rewrote it using ChibiOS 2.6.1 and now got a data corruption. It appears we can send data to external device fine. But at first attempt to read 6 bytes from device we got all zero, although logical analyzer clearly shows the last byte is non-zero.

I guess this is related to CRC. By default ChibiOS doesn't support CRC, I enabled CRC in my code, I'm afraid I'm not doing it correctly. Following is the code, appreciated any suggestions.

Code: Select all

static const SPIConfig spi2_conf = {
        NULL,
        GPIOE,          /* chip select */
        7,              /* chip select */
        SPI_CR1_BR_0 |  /* quarter of APB2 frequency */
        SPI_CR1_CRCEN   /* enable CRC */
};

void exchange(const void *txbuf, void *rxbuf, int size)
{
        uint16_t crc;

        spiAcquireBus(&SPID2);
        spiStart(&SPID2, &spi2_conf);
        spiSelect(&SPID2);
        spiExchange(&SPID2, size, txbuf, rxbuf);

        /* Read DR (CRC) to clear RXNE flag */
        crc = SPID2.spi->DR;
        (void) crc; /* to quiet compiler warning */

        spiUnselect(&SPID2);
        spiReleaseBus(&SPID2);
}

Re: SPI data corruption

Posted: Tue Feb 04, 2014 9:18 am
by Giovanni
The driver does not support the CRC feature at all, it would require changes. Probably it is caused by how the "end of transfer" is handled in the driver, the DMA should be programmed to clock-in the final CRC.

Giovanni

Re: SPI data corruption

Posted: Wed Feb 05, 2014 6:30 am
by rocky
Thank you Giovanni.

I compared the version using STM library and using ChibiOS, it seems they are doing the similar things. Regarding 'end of transfer', ChibiOS waits for bit STM32_DMA_CR_EN be cleared, which is a good approach stated in STM32 manual. We tested the same approach in the version using STM library, it works well.

I read the STM32 manual regarding SPI+CRC+DMA, it listed 3 requirements:
1) CRC_ENABLE and SPI_ENABLE bit must be cleared and set before each transfer;
2) Must wait for DMA activity completes before disabling SPI;
3) At end of transmission, the CRC must be read in the SPI_DR register to clear the RXNE flag;
It seems my code has all above covered.

What we knew:
1) There's no CRC error reported, i.e. the spi->SR register doesn't have SPI_SR_CRCERR set;
2) The last byte of RX memory indeed get changed. If we preset it to a value, after spiExchange() it's get overwritten to zero (while logical analyzer shows non-zero was sent to STM board.

Giovanni what do you mean by "DMA should be programmed to clock-in the final CRC"? Can you please point out a direction for investigation? Thanks a lot!

Re: SPI data corruption

Posted: Wed Feb 05, 2014 8:04 am
by Giovanni
Hi,

I thought that the DMA should be programmed to transfer the CRC byte(s), apparently this is not the case.

Reading this:

Code: Select all

5. Start the communication and sustain the communication until all but one byte or halfword
have been transmitted or received.
– In full duplex or transmitter-only mode, when the transfers are managed by
software, when writing the last byte or half word to the Tx buffer, set the
CRCNEXT bit in the SPI_CR1 register to indicate that the CRC will be transmitted
after the transmission of the last byte.
– In receiver only mode, set the bit CRCNEXT just after the reception of the second
to last data to prepare the SPI to enter in CRC Phase at the end of the reception of
the last data. CRC calculation is frozen during the CRC transfer.
6. After the transfer of the last byte or half word, the SPI enters the CRC transfer and
check phase. In full duplex mode or receiver-only mode, the received CRC is
compared to the SPI_RXCRCR value. If the value does not match, the CRCERR flag in
SPI_SR is set and an interrupt can be generated when the ERRIE bit in the SPI_CR2
register is set.


The DMA should be programmed to transfer N-1 frames, the last frame should be transferred after setting that CRCNEXT bit. This is where the current implementation differs. Something that is not clear to me is how you get informed about the end of the CRC transmission, is there an ISR for that?

About reporting CRC errors: you may add a flag to the SPIDriver structure with the status of the last operation.

Note that on the new SPI present on the F0 and F3 the CRC transmission is automatic, there is no need to do all the above, you just program you DMA to receive one or two extra frames, the CRC.

Giovanni

Re: SPI data corruption

Posted: Wed Feb 05, 2014 5:23 pm
by rocky
Thank you Giovanni.

To be clear I'm using STM32F407-discovery board. I think we only need to take care of CRCNEXT in software mode. In DMA mode this is supposed to be managed automatically. Following is extracted from STM32F4 manual:

When SPI communication is enabled with CRC communication and DMA mode, the
transmission and reception of the CRC at the end of communication are automatic that is
without using the bit CRCNEXT. After the CRC reception, the CRC must be read in the
SPI_DR register to clear the RXNE flag.
At the end of data and CRC transfers, the CRCERR flag in SPI_SR is set if corruption
occurs during the transfer.


And this is exactly what we are doing in the version using STM library (without ChibiOS). DMA transfer size is set to user data size (not include the CRC byte), no need to pay attention to CRCNEXT, it works well.

I added some code in ChibiOS to check CRCERR flag at after DMA transfer finishes, it shows CRCERR never got set. Which seems mean that the board has received the user data and CRC correctly and passed CRC verification, but somehow write wrong data (at least the last byte) into receiving memory, which sound very weird to me. Appreciate any suggestions.

Re: SPI data corruption

Posted: Wed Feb 05, 2014 5:44 pm
by Giovanni
I never used the CRC mode so I can only make assumptions there. One thing that would worry me is: the DMA RX end of transfer would happen before the CRC has been fully received.

Is the DMA programmed to read just the data or the data *and* the CRC? has the CRC been checked by the time the RX DMA is triggered?

Giovanni

Re: SPI data corruption

Posted: Wed Feb 05, 2014 6:50 pm
by rocky
In our code which using ST library, the DMA RX is programmed to read just the data, *no* CRC byte included. The STMF4 manual seems indicate everything is handled by Hardware, by the time RX DMA interrupt triggered, as long as we wait for DMA ENABLE bit be cleared, CRC check should have been done and CRCERR flag set accordingly...

Re: SPI data corruption

Posted: Wed Feb 05, 2014 7:28 pm
by Giovanni
I think that the DMA RX interrupt is triggered as soon the specified amount of data has been transferred. This means that the CRC frames are most likely still being received at that time.

Giovanni

Re: SPI data corruption

Posted: Wed Feb 05, 2014 7:51 pm
by rocky
OK thanks, that's reasonable assumption. But we do have code to wait for all activity to finishes, like following:

In ChibiOS, in RX DMA interrupt handler, we have code to clear DMA_CR_EN, and then wait for DMA_CR_EN becomes zero.

Code: Select all

while (((dmastp)->stream->CR & STM32_DMA_CR_EN) != 0);
dmaStreamClearInterrupt(dmastp);

In our version using STM library, we did the same thing as in ChibiOS and it works. We also tried another way to wait for SPI finish, it also works well:

Code: Select all

void DMA1_Stream3_IRQHandler(void)
{
    uint8_t iTemp;

    while (!(SPI2->SR & SPI_I2S_FLAG_TXE)); /* wait until transmit complete */
    while (SPI2->SR & SPI_I2S_FLAG_BSY); /* wait until SPI is not busy */

    iTemp = SPI2->DR;
    DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3);
}


So as long as we wait for HW finish its job, CRC should have been taken care of by HW, and we should have data in stable state, right?

Re: SPI data corruption

Posted: Wed Feb 05, 2014 8:01 pm
by Giovanni
Yes but doing software polling within an interrupt handler is a recipe for a much degraded RT performance. Expect to wait there for the time required to receive the CRC.

Is there an interrupt source that could signal the end of the CRC operation? if so from the DMA handler you enable the other interrupt and the rest of the processing would be delayed there without waiting.

Giovanni