STM32 - Transfer complete is late on SPI Rx DMA - Page 1
Forum section: electronics / general

author: bezdelnik
author_rank: Newbie
timestamp: February 12, 2022, 07:09:00 pm
content: On a NUCLEO-H743ZI2, I want to periodically receive SPI data from a slave device as SPI master.I do this by using a timer synchronization signal: LPTIM1 is set up to trigger an SPI1_RX DMA request every 5 µs. DMA mode is set to circular, transferring data from peripheral to memory. The DMA request number is set to 1, and the data width is a 32-bit word on both sides.The data size on SPI1 is set to 17 bits at 7 MHz since that is what the SPI slave requires. Chip select is controlled in software by another timer (the SPI slave's communication scheme is a bit more involved, I won't go into the details here).After starting SPI1 via HAL_SPI_Receive_DMA() I can see SPI transfers happening every 5 µs. The data being sent by the slave looks good, too. So far so good.I was curious to determine at which point it is save to actually access the target memory that receices the SPI data. My system is not going to use interrupts because I need to run a really tight timing. But for this test I enabled them and toggled a pin in HAL_SPI_RxCpltCallback() so I could see when the DMA transfer was completed.To my surprise, the "transfer complete" interrupt did not happen after the SPI transfer was finished. Instead, it is only triggered shortly afterthe nexttransfer is started by LPTIM1. Reading the data from the target memory location confirms that the values transferred (as observed on the scope) are only available after the next transfer has been started, i.e. after the DMA transfer has actually been marked as completed.Timings look like this:[Edit: I can't seem to get the inline attachment working. %-) Please see the image attached to the end of this post.](Ch 2 and 3 are SPI chip select and clock, respectively. Ch 4 is the GPIO indicating DMA transfer complete.)This is not at all what I expected. And not what I have observed in a similar setup without DMA synchronization but repeated manual requests instead.Is this expected behaviour in this configuration, and if so, which part of the documentation am I missing?I am currently in the process of stripping down my minimal test firmware to not using the HAL, hopefully revealing a configuration error or something similar. My current versions are:STM32CubeMX version 6.2.0STM32CubeH7 Firmware Package V1.9.0 / 12-February-2021In the meantime, I just wanted to check if somebody has any experience with the SPI DMA on STM32H7 and might be able to point me in the right direction, helping me to solve this issue faster.

author: wek
author_rank: Frequent Contributor
timestamp: February 12, 2022, 10:49:58 pm
content: I don't understand your description (especially since you haven't posted a single register readout), and the 'H7's SPI is an overcomplicated beast, and I don't use the 'H7, but IMO a timer-triggered DMA is just that, a timer-triggered DMA, which upon triggering transfers some data from source to destination. So, it finishes just a few clocks after it was triggered.In other words, timer-triggered DMA does not care whether there is some data in SPI's Rx FIFO or not, nor does it directly start the transfer (it may start it indirectly by reading the FIFO, e.g. if Rx-only mode ("simplex receiver") is set in SPI_CFG2.COMM and SPI2S_CR1.MASRX is set).JW

author: Siwastaja
author_rank: Super Contributor
timestamp: February 13, 2022, 07:26:45 am
content: Quote from: bezdelnik on February 12, 2022, 07:09:00 pmSound like you have the whole thing wrong way around?You are the master, so you need to generate the SPI clocks, and as a side effect, data appears in RX data register. (Data appearing in RXDR then can generate an interrupt, or DMA request.)To generate SPI clocks, you write to the TXDR. You can of course set up a DMA channel, triggered by a timer, with peripheral register set as TXDR. If the data you send doesn't matter (can be all zeroes etc.), you can then save memory by turning Memory Increment bit off and just send the same constant all over again.Right now I don't understand how what you describe doesanything. SPI RX DMA request obviously comesfromthe SPI peripheral. You can't "trigger SPI RX DMA request", this sentence makes no sense at all.

author: bezdelnik
author_rank: Newbie
timestamp: February 13, 2022, 09:55:38 am
content: Quote from: wek on February 12, 2022, 10:49:58 pmAnything in particular that you are interested in? I thought it helpful to first broadly describe the general setup (and my intentions) instead of going full register dump. Because, like you said, this SPI is rather large and complex. The same goes for the DMA setup.Which registers at which point in the process would be helpful to you? Like I said, I am calling HAL_SPI_Receive_DMA() which does a lot of the necessary DMA and SPI setup.

author: bezdelnik
author_rank: Newbie
timestamp: February 13, 2022, 09:56:37 am
content: Quote from: Siwastaja on February 13, 2022, 07:26:45 amYes, you are right. This was a rather high-level description from the HAL SPI API's point of view. What makes this work is that HAL_SPI_Receive_DMA() (seehttps://github.com/STMicroelectronics/STM32CubeH7/blob/v1.9.0/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_spi.c#L2224) internally calls HAL_SPI_TransmitReceive_DMA() to generate the necessary clock pulses by sending dummy data, as you described. (Which is also the reason why this won't work in an Rx-only or single-line configuration.)

author: wek
author_rank: Frequent Contributor
timestamp: February 13, 2022, 09:59:12 am
content: QuoteContent of DMA, DMAMUX, SPI and perhaps LPTIM registers. Not in decimal.QuoteThe mcu does not work out of some library calls, but out of the registers.JW

author: bezdelnik
author_rank: Newbie
timestamp: February 13, 2022, 10:11:57 am
content: Quote from: wek on February 13, 2022, 09:59:12 amYeah, I know.I just wanted to give context, and this HAL API is what I am currently using. Also, since my question was about the (possibly intended) timing of the "transfer complete" signal in this kind of DMA transfers, the question was directed more at people that may have experience with this particular MCU and can confirm that it does what it is supposed to do (or not). Not so much at diving into debugging the particular register setup right away. But I am glad if someone is taking a closer look, too.So I will post that register dump (at the point when that HAL function is done setting things up and enables the SPI to kick things off). Will take a while though because I currently don't have the hardware right here.

author: wek
author_rank: Frequent Contributor
timestamp: February 13, 2022, 10:28:11 am
content: > since my question was about the (possibly intended) timing of the "transfer complete" signalI may of course be wrong, but I'm willing to bet I've described the mechanism correctly above (maybe except the Rx-only part, only to be replaced by the Tx DMA, which if its request is set automagically to SPI Tx trigger by the Cube/HAL, in case of circular DMA works exactly just like Rx-only).Thus, as I've said above, it's not SPI's transfer complete what you are looking at, but the DMA transfer complete. As I've said, DMA does not care about SPI, as soon as its trigger arrives, it picks a datum on input side and dumps it at output side, and then signals transfer complete (in case of NDTR>1 it would decrement NDTR etc.). If you trigger DMA from timer, you are essentially seing that timer trigger in the transfer complete interrupt, slightly delayed by the two transfers.I am honestly not interested in the registers' content - it's you who should be. I am of course willing to have a look.JW

author: bezdelnik
author_rank: Newbie
timestamp: February 13, 2022, 10:58:46 am
content: Quote from: wek on February 13, 2022, 10:28:11 amYes, it's the DMA transfer complete that I am talking about. Apparently, I have not made that clear enough.Quote from: wek on February 13, 2022, 10:28:11 amMaybe I am misunderstanding the inner workings of the this SPI and DMA coupling. If the SPI does its own thing, completely ignoring what the timer-triggered DMA does, who or what could generate the repeated SPI transfers? Without repeatedly kicking them off in code. I don't mean "in my setup" (I am aware that you would have to have a look at the registers first) but generally. How would that look like?I mean, itdoesdo the repeated SPI transfers. But maybe I have the wrong idea ofwhyit does.Quote from: wek on February 13, 2022, 10:28:11 amRest assured, I went through the register configuration before posting here. And to me it all checks out (as far as I understand the reference manual). This is the reason why I am posting here in the first place, because I am apparently missing something.

author: bezdelnik
author_rank: Newbie
timestamp: February 13, 2022, 11:16:48 am
content: BTW, as addendum: When I removed the DMA synchronization on the timer for debugging earlier this week, I would see a constant stream of SPI clock cycles. But only if the DMA was configured in circular mode. Without circular mode, there would be a single transfer (the configured 17 SPI clocks) and then nothing else. This is why I was sure that it is actually the timer-triggered DMA requests that kick off the repeated SPI transfers.But like I said, maybe I am misunderstanding how this link between SPI and DMA actually works on the H7.

author: Siwastaja
author_rank: Super Contributor
timestamp: February 13, 2022, 11:39:28 am
content: DMA SPI master:Something (usually a timer, but could be another peripheral, like ADC completion) creates DMA requests. So far, no connection to the SPI peripheral: it's a timer->DMA stream connection. The DMA stream then just performs a write to SPI->TXDR on every trigger. Circular mode just prevents DMA stream from being disabled after each completion, so that no reconfiguration of DMA channel is needed. In this step, "completion" just means writing to SPI->TXDR register succeeded.TLDR; timer -> DMA stream -> write to SPI_TXDR.Then, as a separate process, when SPI notices that TXDR was written, it starts generating SCK cycles. While it does that, it fills the internal RX register. Once enough bits have been received, this internal RX register is copied into accessible RXDR, and if DMA mode is enabled, DMA RX DMA request is generated. Now you need a different, separate DMA stream triggered on this RX DMA request, and it will handle reading the RXDR, and writing it to memory address of your choice.Setting up NDTR (and possibly double-buffered mode) is now important so that the memory pointer resets to where you want it after a full datagram possibly consisting of multiple words.

author: bezdelnik
author_rank: Newbie
timestamp: February 13, 2022, 11:58:06 am
content: Quote from: Siwastaja on February 13, 2022, 11:39:28 amOk, this seems to be exactly what this setup is: one DMA request for SPI1_RX (which I care about for receiving the data) and one for SPI1_TX (which is necessary to get the SPI transfer going since we, as SPI master, must initiate communication, generating the clocks).Both DMA requests are synchronized to the timer. So it makes sense that the first RX request is not yet serviced when the TX request kicks off the initial SPI transfer. So the RX request is served only on the following timer event, hence the observed delay of the RX DMA transfer complete.There is only one detail I do not quite get about this: The first RX request is sent and cannot be served. So it hangs there waiting for RXDR to be filled with the incoming data. Should it then not be served right after that data is received by SPI? I confirmed earlier that no error is ever signalled for either RX DMA, TX DMA, or SPI. So it does not look to me like the first DMA RX request should just be lost. %-)

author: Siwastaja
author_rank: Super Contributor
timestamp: February 13, 2022, 12:09:42 pm
content: Quote from: bezdelnik on February 13, 2022, 11:58:06 amThis is wrong. The RX DMA should be triggered by the RX DMA request from the SPI. No timer involved. Then it is served exactly when new data is actually available, no sooner, no later.

author: bezdelnik
author_rank: Newbie
timestamp: February 13, 2022, 12:28:26 pm
content: Quote from: Siwastaja on February 13, 2022, 12:09:42 pmYes, this makes sense. I assume this is also what wek was getting at. I did not quite understand that previously.So I am sitting at the hardware now, testing this change. And it looks much better already. The DMA RX transfer complete still comes quite a bit after the SPI transfer is done, but the basic issue seems to be solved.Thanks, to both of you.

author: Siwastaja
author_rank: Super Contributor
timestamp: February 13, 2022, 01:13:43 pm
content: Yes, DMA RX complete interrupt must come later than SPI transfer done interrupt. I don't remember offhand how the H7 SPI implementation work, but the "SPI done" interrupt might come as early as the TXDR is consumed into the internal shift register! Or, even if it comes after the RXDR is written with the last data, there is still memory access delay because DMA needs to read this register, wait for bus access, and write it in memory. And of course you can access the data in memory only after DMA has finished its job, hence you should use the DMA finished IRQ.Finally, if the datagrams are short enough to fit into the FIFOs of the SPI device and you need a processing interrupt anyway, a non-DMA solution might be faster in the end. This is because the H7 SPI implementation allows clever access to the FIFO so that depending on the bus operation width, you can read or write 1, 2 or 4 bytes at once, and the FIFO can hold, what, was it 8 or 16 bytes depending on which SPI instance it is. For example, for less than 11-byte transfers, worst-case write/read pattern is then the 7-bit access for which you need 3 bus cycles: 32-bit + 16-bit + 8-bit access.