machine.SPI.write() taking too long?

Questions and discussion about The WiPy 1.0 board and CC3200 boards.
Target audience: Users with a WiPy 1.0 or CC3200 board.
danielm
Posts: 167
Joined: Mon Oct 05, 2015 12:24 pm

machine.SPI.write() taking too long?

Post by danielm » Tue May 03, 2016 7:33 pm

I measured how many CPU ticks does it take to write 2 bytes with machine.SPI.write() function and it is always more than 9000 CPU ticks. SPI baudrate is set to 1000000.

Is it somehow possible to accelerate SPI write command execution time?

User avatar
danicampora
Posts: 342
Joined: Tue Sep 30, 2014 7:20 am
Contact:

Re: machine.SPI.write() taking too long?

Post by danicampora » Thu May 05, 2016 6:19 pm

Hello,

A way to speed it up is to cache the method in a local variable, e.g.:

Code: Select all

write = spi.write

write(...)


Increasing the baudrate to 10 MHz might help as well.

Cheers,
Daniel

danielm
Posts: 167
Joined: Mon Oct 05, 2015 12:24 pm

Re: machine.SPI.write() taking too long?

Post by danielm » Fri May 13, 2016 5:29 pm

Daniel,

I did several tests and here are the results:
Cashing spi.write method speeds up sequential SPI writes by approx. 2,5%
Switching baudrate from 1MHz to 10Mhz speeds up sequential SPI writes by approx. 7%
Combined - approx. 10%

As you see, the difference in sequential execution of SPI writes is not significant.

Here are traces recorded by logic analyzer for sending of integer converted to 2-byte payload two times via cashed spi.write method. I believe that there could be some more efficient way how to do the conversion.

Code: Select all

class GalvoBoard():
...
	def _sendGalvoData(self, data):
	    data_bytes = data.to_bytes(2)
	    data_array = bytearray([data_bytes[1],data_bytes[0]])
	    self.spiNSS.value(0)
	    self.spiWrite(data_array)
	    self.spiNSS.value(1)       
The test sequence:

Code: Select all

    def seqTest(self):
        print("Starting sequence test")
        startTime = time.ticks_ms()
        self.galvoBoard._sendGalvoData(16962)
        self.galvoBoard._sendGalvoData(16962)        
        print("Scan test ended in %sms" % (time.ticks_ms() - startTime))       
And the traces:
Signals: CS/NSS, SCK, MOSI, MISO
logic_analyzer_pic1.png
logic_analyzer_pic1.png (40.78 KiB) Viewed 11675 times
logic_analyzer_pic2.png
logic_analyzer_pic2.png (40.35 KiB) Viewed 11675 times
As you can see, time difference between those two SPI transfers is really long. Is there anything I could do to make it shorter?
It is necessary to raise CS/NSS pin of SPI slave (2-channel 12-bit DAC MCP4922) to high level after every two transffered bytes.

danielm
Posts: 167
Joined: Mon Oct 05, 2015 12:24 pm

Re: machine.SPI.write() taking too long?

Post by danielm » Thu May 19, 2016 8:42 am

Daniel, I did some more experiments using following simple code in main.py:

Code: Select all

import gc
gc.disable
spi = machine.SPI(machine.SPI.MASTER, baudrate=8000000, polarity=1, phase=1, bits=16, pins=('GP14', 'GP16', 'GP15'))
spiWrite = spi.write
while True:
	spiWrite(b'\x00\x00')
I omitted switching of CS pin on purpose just to determine raw speed of SPI interface. I set the frequency to 8MHz because of limited sample rate of my logic analyzer. Interface is set to operate in 16-bit mode because this is word width my SPI slave is using.

Here are two traces from the logic analyzer - detail and sequence.
logic_analyzer_8MHz_16-bit_detail.png
logic_analyzer_8MHz_16-bit_detail.png (51.19 KiB) Viewed 11642 times
logic_analyzer_8MHz_16-bit_sequence.png
logic_analyzer_8MHz_16-bit_sequence.png (52.58 KiB) Viewed 11642 times
As you can see, time difference between two transfers of 2-byte payload is approx. 42us, which translates to 47,6 kbytes/sec.

In this thread user Oliv suggested to activate DMA mode in WiPy's FW: http://forum.micropython.org/viewtopic. ... ency#p7709
You explained that it is not enabled because of impact to heap space. Could you please elaborate more on how significant the impact would be?

Is it possible to implement some global settings whether to use DMA for SPI at the expense of free heap size?

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: machine.SPI.write() taking too long?

Post by pythoncoder » Thu May 19, 2016 9:19 am

I'm not convinced DMA would help. It's sending the two bytes quickly: the time is taken executing the while loop. It might be instructive to look at the timing for (fragment):

Code: Select all

while True:
   spiWrite(b'\x00\x00')
   spiWrite(b'\x00\x00')
Peter Hinch
Index to my micropython libraries.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: machine.SPI.write() taking too long?

Post by Roberthh » Thu May 19, 2016 9:38 am

I was thinking of doing some raw test, by just toggling a GPIO pin. From tests I made with PyBoard I learned that the while loop plus function call would take about 6us per loop at 168 MHz (see https://github.com/robert-hh/SSD1963-TF ... or-PyBoard, README.md, section Remarks). Since WiPy runs at 80MHz, at least twice the time should be needed.
The underlying SPI tranfer code should be fast. Therefore data should be sent & received in as big chunks as possible.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: machine.SPI.write() taking too long?

Post by pythoncoder » Thu May 19, 2016 10:13 am

@RoberthhThere's a big disparity between your entirely reasonable estimate of 12uS and the 42uS measured by @danielm. A GPIO toggle would be informative.
Peter Hinch
Index to my micropython libraries.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: machine.SPI.write() taking too long?

Post by Roberthh » Thu May 19, 2016 11:27 am

Obviously it is. I just wanted to point out, that the python part is often much slower than the low level drivers.

danielm
Posts: 167
Joined: Mon Oct 05, 2015 12:24 pm

Re: machine.SPI.write() taking too long?

Post by danielm » Thu May 19, 2016 2:11 pm

Unfortunatelly I cannot send data in big chunks because I need to toggle CS pin in between each two bytes sent to SPI slave.

Please take a look at these test results of forum member @manitou. Not sure if he made the comparison for CC3200 platform with pure compiled C code or with MicroPython. It seems that DMA might help to speed up SPI communication.

CC3200 LAUNCHPAD @ 80MHz (SPI max clock 20mhz)
SPI
SPI clock transfer DMA (4-byte)
2MHz 1.2 mbs 1.9
4MHz 1.7 mbs 3.8
5MHz 4.8
10MHz 2.2 mbs 9.2
20MHz 2.7 mbs 17.2

https://github.com/manitou48/DUEZoo/blo ... PIperf.txt

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: machine.SPI.write() taking too long?

Post by Roberthh » Thu May 19, 2016 3:40 pm

The loop test gave 12.8 µs per loop iteration. Any additional code makes it slower. Test code:

Code: Select all

from machine import Pin
#
p_out = Pin('GP3', mode=Pin.OUT)
while True:
    p_out.toggle()
Regards

Post Reply