Spark Logic

Non blocking SPI DMA – Added callback to the SPI DMA functions (dmaSend, dmaTransfer…)

victor_pv Tue Feb 21, 2017 8:05 pm

UPDATE:
Working code in this post, and an explanation how to modify sdFat to use it in the next post after that:
http://www.stm32duino.com/viewtopic.php … =60#p30306

======================================================

This is something Roger has suggested several times, and it seems to only make sense.

Currently the dma TX/RX functions we added to the SPI library block until the DMA transfer is completed or has timed out.
This change would add 2 functions to the SPI library, similar to the Arduino official I2S library:

onTransmit(handler);
onReceive(handler);

Replies

RogerClark Tue Feb 21, 2017 8:34 pm

Victor

Its probably worth PM’ing @stevstrong if he does not see this thread, as he has done a lot of work in SPI recently.

I think I have one pending PR from stev, but it seems to slow the SPI down, hence I have not actioned it yet. But it may be necessary as it contains bug fixes.

I think there is a general problem with callbacks into C++ classes, as only static functions addresses can be accessed.
So it would need to be a shared ISR for all instances.

Other APIs I work with, let you pass a pointer to the callback function, into the Transfer function. Which seems logical to me.

victor_pv Tue Feb 21, 2017 8:50 pm

RogerClark wrote:Victor

Its probably worth PM’ing @stevstrong if he does not see this thread, as he has done a lot of work in SPI recently.

I think I have one pending PR from stev, but it seems to slow the SPI down, hence I have not actioned it yet. But it may be necessary as it contains bug fixes.

I think there is a general problem with callbacks into C++ classes, as only static functions addresses can be accessed.
So it would need to be a shared ISR for all instances.

Other APIs I work with, let you pass a pointer to the callback function, into the Transfer function. Which seems logical to me.

RogerClark Tue Feb 21, 2017 8:56 pm

Hi victor

I have not tested @stevstrongs latest PR, but the previous one was slower than what we had before :-(

Re: one ISR per SPI channel

Sounds OK to me.

thanks

roger

stevestrong Tue Feb 21, 2017 9:14 pm

While trying to optimze the ILI9486 lib i have already implemented the non-blocking DMA with callback at job end. This included a short isr at job end which called the cb function if set by the user within the isr.
Unfortunatelly the DMA slows down the CPU very strongly, so at the end, including the ovehead to init the DMA, no time saving could take place compared to the blocking non-DMA version. I tested it with the Adafruit graphics test. You can follow the results from that thread.
My version was designed to reserve the respective DMA channel (i think channel 3) only for SPI. Still, i was not happy with the result.

victor_pv Tue Feb 21, 2017 9:32 pm

stevestrong wrote:While trying to optimze the ILI9486 lib i have already implemented the non-blocking DMA with callback at job end. This included a short isr at job end which called the cb function if set by the user within the isr.
Unfortunatelly the DMA slows down the CPU very strongly, so at the end, including the ovehead to init the DMA, no time saving could take place compared to the blocking non-DMA version. I tested it with the Adafruit graphics test. You can follow the results from that thread.
My version was designed to reserve the respective DMA channel (i think channel 3) only for SPI. Still, i was not happy with the result.

stevestrong Tue Feb 21, 2017 10:00 pm

Sure, it depends on the application.
The display lib was also partially writing larger blocks, no overall speed gain achieved however.
Once again, the cpu is slowed down strongly by the DMA running in background.
Dont understand me wrong, i see the theoretical benefit, that is why i also tested it. Still, the results did not convince me.

As i dont have any other application where saved time would play larger role than in the display lib, i have given up to push it into the repo.
But feel free to get it run. If you think it could help i could share my local version.

victor_pv Wed Feb 22, 2017 1:02 am

stevestrong wrote:Sure, it depends on the application.
The display lib was also partially writing larger blocks, no overall speed gain achieved however.
Once again, the cpu is slowed down strongly by the DMA running in background.
Dont understand me wrong, i see the theoretical benefit, that is why i also tested it. Still, the results did not convince me.

As i dont have any other application where saved time would play larger role than in the display lib, i have given up to push it into the repo.
But feel free to get it run. If you think it could help i could share my local version.

racemaniac Wed Feb 22, 2017 10:20 am

stevestrong wrote:Sure, it depends on the application.
The display lib was also partially writing larger blocks, no overall speed gain achieved however.
Once again, the cpu is slowed down strongly by the DMA running in background.
Dont understand me wrong, i see the theoretical benefit, that is why i also tested it. Still, the results did not convince me.

As i dont have any other application where saved time would play larger role than in the display lib, i have given up to push it into the repo.
But feel free to get it run. If you think it could help i could share my local version.

stevestrong Wed Feb 22, 2017 11:39 am

and how can DMA not give a speedup even if the cpu slows down? your transfer will take equally long as with a blocking transfer, and even if the cpu slows down, at least it can do some work during the transfer.
You forgot the overhead to setup the DMA before each transaction. And if you implement the callback at job end, this will also take time and block completely the CPU from doing other tasks.
Thus, dependent on the SPI clock speed, the overhead together with the post-processing can take the time necessary to transfer, let’s say, 25 bytes.
So if you transfer 20 bytes without DMA, it is faster than transferring it with DMA.

Hence, again, to choose the appropriate strategy strongly depends on the application.
If you always write blocks of 256 bytes or more and have a lot of tasks to do between consecutive block writes (not only to wait for the previous SPI job to finish), then using DMA is clearly a good approach. Otherwise it can be slower than the non-DMA version.

racemaniac Wed Feb 22, 2017 12:06 pm

stevestrong wrote:and how can DMA not give a speedup even if the cpu slows down? your transfer will take equally long as with a blocking transfer, and even if the cpu slows down, at least it can do some work during the transfer.
You forgot the overhead to setup the DMA before each transaction. And if you implement the callback at job end, this will also take time and block completely the CPU from doing other tasks.
Thus, dependent on the SPI clock speed, the overhead together with the post-processing can take the time necessary to transfer, let’s say, 25 bytes.
So if you transfer 20 bytes without DMA, it is faster than transferring it with DMA.

Hence, again, to choose the appropriate strategy strongly depends on the application.
If you always write blocks of 256 bytes or more and have a lot of tasks to do between consecutive block writes (not only to wait for the previous SPI job to finish), then using DMA is clearly a good approach. Otherwise it can be slower than the non-DMA version.

stevestrong Wed Feb 22, 2017 3:06 pm

Extract from RM0008:
13.3 DMA functional description
The DMA controller performs direct memory transfer by sharing the system bus with the
Cortex®-M3 core. The DMA request may stop the CPU access to the system bus for some
bus cycles, when the CPU and DMA are targeting the same destination (memory or
peripheral)
. The bus matrix implements round-robin scheduling, thus ensuring at least half
of the system bus bandwidth (both to memory and peripheral) for the CPU.
So the CPU may be slowed down to the half of its speed capacity. The higher the SPI clock, the worse the situation for the CPU if performs a lot of memory accesses.

racemaniac Wed Feb 22, 2017 3:28 pm

stevestrong wrote:Extract from RM0008:
13.3 DMA functional description
The DMA controller performs direct memory transfer by sharing the system bus with the
Cortex®-M3 core. The DMA request may stop the CPU access to the system bus for some
bus cycles, when the CPU and DMA are targeting the same destination (memory or
peripheral)
. The bus matrix implements round-robin scheduling, thus ensuring at least half
of the system bus bandwidth (both to memory and peripheral) for the CPU.
So the CPU may be slowed down to the half of its speed capacity. The higher the SPI clock, the worse the situation for the CPU if performs a lot of memory accesses.

victor_pv Wed Feb 22, 2017 4:31 pm

racemaniac wrote:stevestrong wrote:Extract from RM0008:
13.3 DMA functional description
The DMA controller performs direct memory transfer by sharing the system bus with the
Cortex®-M3 core. The DMA request may stop the CPU access to the system bus for some
bus cycles, when the CPU and DMA are targeting the same destination (memory or
peripheral)
. The bus matrix implements round-robin scheduling, thus ensuring at least half
of the system bus bandwidth (both to memory and peripheral) for the CPU.
So the CPU may be slowed down to the half of its speed capacity. The higher the SPI clock, the worse the situation for the CPU if performs a lot of memory accesses.

racemaniac Wed Feb 22, 2017 7:28 pm

victor_pv wrote:racemaniac wrote:stevestrong wrote:Extract from RM0008:

So the CPU may be slowed down to the half of its speed capacity. The higher the SPI clock, the worse the situation for the CPU if performs a lot of memory accesses.

victor_pv Wed Feb 22, 2017 7:51 pm

racemaniac wrote:

indeed :). It would still be nice to be able to configure that you won’t reuse the dma channel for something else and get the cheaper dma setup (likely the most often usecase since so far dma is hardly used anyway ^^)

And i’m still wondering about the performance hit for what is running simultaneous to it :). i’m really going to give that a try when i have some time, it’s very good to know :).

any suggestions on what to benchmark it with? just a dhrystone benchmark or so?

victor_pv Thu Feb 23, 2017 2:51 am

racemaniac wrote:
indeed :). It would still be nice to be able to configure that you won’t reuse the dma channel for something else and get the cheaper dma setup (likely the most often usecase since so far dma is hardly used anyway ^^)

racemaniac Thu Feb 23, 2017 8:31 am

victor_pv wrote:racemaniac wrote:
indeed :). It would still be nice to be able to configure that you won’t reuse the dma channel for something else and get the cheaper dma setup (likely the most often usecase since so far dma is hardly used anyway ^^)

stevestrong Thu Feb 23, 2017 8:36 am

In my code I initialized the DMA once (dmaSendInit()), then I just updated the number of data to transfer and the pointer in dmaSendBuffer() and enabled the DMA, so that the overhead could be kept minimal.
I will post my version here in the evening.

Sign in to reply