Xilinx / embeddedsw

Xilinx Embedded Software (embeddedsw) Development
Other
936 stars 1.07k forks source link

Problem receiving all SPI data in interrupt mode #146

Open cdelledonne opened 3 years ago

cdelledonne commented 3 years ago

I've been experiencing a problem when trying to receive SPI data in interrupt mode. More precisely, if the SPI clock frequency is not "fast enough", the last byte coming from the counterpart SPI device is not returned, and stalls in the Rx FIFO until the next transfer.

I'm generating standalone drivers using Vitis, and the resulting XSpiPs_Transfer and XSpiPs_InterruptHandler looks like the one in xspips.c.

XSpiPs_Transfer enables the Tx FIFO overwater interrupt

https://github.com/Xilinx/embeddedsw/blob/a60c084a0862559e2fa58fd4c82b0fe39b923e33/XilinxProcessorIPLib/drivers/spips/src/xspips.c#L373-L378

meaning that the interrupt handler will be triggered once the Tx FIFO has fewer entries than the configured watermark, which, as I understand, by default it will be triggered when the Tx FIFO becomes empty.

When the above interrupt occurs, XSpiPs_InterruptHandler will check the Tx FIFO overwater interrupt flag, and read bytes from the Rx FIFO if the flag is set

https://github.com/Xilinx/embeddedsw/blob/a60c084a0862559e2fa58fd4c82b0fe39b923e33/XilinxProcessorIPLib/drivers/spips/src/xspips.c#L919-L942

My interpretation of the problem is the following:

Does the above interpretation make sense?

My current workaround is to enable Rx-FIFO-not-empty interrupts after calling XSpiPs_Transfer and after setting a Rx FIFO watermark

XSpiPs_Transfer(&spi_instance, tx_buffer, rx_buffer, buffer_len);
/* Set Rx FIFO watermark. */
XSpiPs_WriteReg(spi_instance.Config.BaseAddress, XSPIPS_RXWR_OFFSET, buffer_len);
/* Enable Rx-FIFO-not-empty interrupt. */
XSpiPs_WriteReg(spi_instance.Config.BaseAddress, XSPIPS_IER_OFFSET, XSPIPS_IXR_RXNEMPTY_MASK);

and to implement a custom SPI interrupt handler, rather than using XSpiPs_InterruptHandler.

Do you have other suggestions to address this problem?

Amit-Kumar-Mahapatra commented 3 years ago

Hello @cdelledonne

Could you please le us know the SPI driver version, SPI ref clock and the prescaler value that you are using.

Regards, Amit

cdelledonne commented 3 years ago

Hi Amit, thanks for replying.

cdelledonne commented 3 years ago

Hi @Amit-Kumar-Mahapatra, any updates on this? I'm only curious to know whether the issue takes a long time to be looked into, or you just didn't have enough time.

Best, Carlo

Amit-Kumar-Mahapatra commented 3 years ago

Hello @cdelledonne, we have started looking into the issue, We will update you in a week time.

Regards, Amit

Amit-Kumar-Mahapatra commented 3 years ago

Hello @cdelledonne,

As you know that all the spi controller register read/write are sync with the ABP clock or pclk and all the spi date trasfer are in sync with the spi_clk(which is derived from spi ref clk). Coul you please let us know the pclk, spi ref clk in your design, because as per the controller specs there is a relationship between pclk and spi ref clk that need to be followed

APB clock (pclk) must be equal or less than half the frequency of reference clock(ref_clk).

If the pclk is too high and the spi clk is not fast enough. Then there is a possibility of the scenario that you described in you initial post.

If you could elaborate on your implementation(the interrupts that you are enabling and your customized interupt handler flow) then can analyze it and if possible suggest you a better way to adddress you concern.

Regards, Amit

cdelledonne commented 3 years ago

Dear @Amit-Kumar-Mahapatra,

Where is the requirement "APB clock (pclk) must be equal or less than half the frequency of reference clock(ref_clk)" mentioned? I guess this is platform specific, isn't it? I'm running on a Zynq-7000 SoC platform, and the technical reference manual (Section 17.4.2) mentions

Frequency Restriction Note: The SPI_Ref_Clk must be always be set to a higher frequency than the CPU_1x clock frequency.

In my specific case, SPI_Ref_Clk is 166 MHz, while CPU_1x is 111 MHz, thus the requirement seems to be satisfied.

I want to highlight again that the issue occurs at low SCLK frequencies, that have nothing to do with the frequency of SPI_Ref_Clk. Phrased differently, whatever the frequency of SPI_Ref_Clk is, if the SCLK is clocked (by the SPI master device) at a low frequency, and an interrupt is raised when the last byte of data starts being shifted out, the processor will always be fast enough to read the SPI Rx FIFO before the last byte is copied to the FIFO. Wouldn't you agree?

My use case is simple: I want to receive an interrupt when N bytes are received in the SPI Rx FIFO. How do I guarantee that, at all SCLK frequencies, I get the interrupt only when all bytes have been received, given that the default SPI interrupt handler provided by Xilinx (XSpiPs_InterruptHandler defined in xspips.c) only checks for the Tx FIFO empty interrupt flag?

Best, Carlo

Amit-Kumar-Mahapatra commented 3 years ago

Hello @cdelledonne

We agree with your point that with very low spi clk "the processor will always be fast enough to read the SPI Rx FIFO before the last byte is copied to the FIFO". In such scenario you have to enable the Rx-FIFO-not-empty interrupts and modify the interrupt handler accordingly.