Page 1 of 2
machine.SPI.write() taking too long?
Posted: Tue May 03, 2016 7:33 pm
by danielm
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?
Re: machine.SPI.write() taking too long?
Posted: Thu May 05, 2016 6:19 pm
by danicampora
Hello,
A way to speed it up is to cache the method in a local variable, e.g.:
Increasing the baudrate to 10 MHz might help as well.
Cheers,
Daniel
Re: machine.SPI.write() taking too long?
Posted: Fri May 13, 2016 5:29 pm
by danielm
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 (40.78 KiB) Viewed 15764 times

- logic_analyzer_pic2.png (40.35 KiB) Viewed 15764 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.
Re: machine.SPI.write() taking too long?
Posted: Thu May 19, 2016 8:42 am
by danielm
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 (51.19 KiB) Viewed 15731 times

- logic_analyzer_8MHz_16-bit_sequence.png (52.58 KiB) Viewed 15731 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?
Re: machine.SPI.write() taking too long?
Posted: Thu May 19, 2016 9:19 am
by pythoncoder
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')
Re: machine.SPI.write() taking too long?
Posted: Thu May 19, 2016 9:38 am
by Roberthh
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.
Re: machine.SPI.write() taking too long?
Posted: Thu May 19, 2016 10:13 am
by pythoncoder
@RoberthhThere's a big disparity between your entirely reasonable estimate of 12uS and the 42uS measured by @danielm. A GPIO toggle would be informative.
Re: machine.SPI.write() taking too long?
Posted: Thu May 19, 2016 11:27 am
by Roberthh
Obviously it is. I just wanted to point out, that the python part is often much slower than the low level drivers.
Re: machine.SPI.write() taking too long?
Posted: Thu May 19, 2016 2:11 pm
by danielm
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
Re: machine.SPI.write() taking too long?
Posted: Thu May 19, 2016 3:40 pm
by Roberthh
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