1 2 Previous Next 19 Replies Latest reply on May 12, 2016 3:29 PM by Intel_Alvarado

    Edison SPI speed issue using ioctl()

    elembivos

      Hello,

       

      I'm working on a project that involves analog inputs and outputs using an SPI ADC and DAC.  I'm using the Edison mini breakout with the Arduino IDE.  I know I should use the IoT Eclipse, but I'm trying to port a project that is currently running on an Arduino Uno, and thought it would be easier this way.  The Uno version is running with 8-bit I/O at ~19kHz, and I was hoping to beat that and do 12-bit I/O >19kHz with the Edison.  Anyway I'm having issues with the SPI speed.  I have verified that the clock will tick at whatever rate I ask, all the way to 25MHz, but this doesn't actually have much effect on the overall transfer rate.  I think I've tracked the issue down to the ioctl() method call in the SPIClass::transfer() function.  I modified the library to add 2 fastGpio writes so I can see where the time is being lost.  Here's the modified SPIClass::transfer():

       

      uint8_t SPIClass::transfer(uint8_t txData)

      {

        uint8_t rxData = 0xFF;

        struct spi_ioc_transfer msg;

       

        memset(&msg, 0, sizeof(msg));

       

        msg.tx_buf = (__u64) &txData;

        msg.rx_buf = (__u64) &rxData;

        msg.len = sizeof(uint8_t);

       

      fastGpioPciDigitalWrite(10,LOW);

        if (ioctl (this->fd, SPI_IOC_MESSAGE(1), &msg) < 0)

        trace_error("Failed to execute SPI transfer\n");

      fastGpioPciDigitalWrite(10,HIGH);

       

        return rxData;

      }

       

      After J20-10 (GP41, Arduino pin 10) goes low ioctl() is called, but it takes >50usec for the CS to go low and the CLK to start ticking out the data.  Then it takes another >40usec after the CS goes back up to get to the next fastGpio line.  So the time spent clocking out the data is tiny compared to the total duration of the ioctl() call.  I've looked into the libmraa library, and see the same usage of ioctl(), and suspect it would have the same issue.  Has anyone else run into this problem?  Is there another way to access the SPI hardware without using ioctl()?  I could toggle the gpio's manually to get the data to/from the DAC/ADC which would be faster than ioctl(), but would be no faster than the Uno, which would defeat the purpose.  Anyone have any ideas?

       

      Thanks,

      Mike

        • 1. Re: Edison SPI speed issue using ioctl()
          Intel_Alvarado

          Hi,

           

          You can try using spidev.http://linux-sunxi.org/SPIdev  . Using  spidev, you can write a code in C or Python to try to get the maximum performance of the SPI. Take a look at spidev 3.0 : Python Package Index and http://tightdev.net/SpiDev_Doc.pdf .

           

          Sergio

          • 2. Re: Edison SPI speed issue using ioctl()
            elembivos

            Hi Sergio,

             

            Thanks for the quick response.  I've been looking through these websites, and the library supplied with the Arduino IDE.  It looks like the Arduino SPI library already uses the SPIdev library and is very similar to the example in the link.  Anyway, I bypassed the Arduino library to use spidev directly and I get the same results.  This code:

             

            #include <fcntl.h>

            #include <sys/ioctl.h>

            #include <linux/spi/spidev.h>

             

            uint32_t maxSpeedHz = 4000000;

            uint8_t lsbFirst = 0;

            uint8_t linuxSpiMode = SPI_MODE_0;

            uint8_t rxData = 0xFF;

            uint8_t txData = 0x55;

            struct spi_ioc_transfer msg;

            int file;

             

            void setup() {

              pinMode(10,OUTPUT);

              digitalWrite(10,HIGH);

              memset(&msg, 0, sizeof(msg));

              msg.tx_buf = (__u64) &txData;

              msg.rx_buf = (__u64) &rxData;

              msg.len = sizeof(uint8_t);

              file = open("/dev/spidev5.1",O_RDWR);

              ioctl(file,SPI_IOC_WR_MAX_SPEED_HZ,&maxSpeedHz);

              ioctl(file,SPI_IOC_WR_LSB_FIRST,&lsbFirst);

              ioctl(file,SPI_IOC_WR_MODE,&linuxSpiMode);

            }

             

            void loop() {

              digitalWrite(10,LOW); digitalWrite(10,HIGH);

              ioctl(file,SPI_IOC_MESSAGE(1),&msg);

              digitalWrite(10,LOW); digitalWrite(10,HIGH);

            }

             

            produces the same result as this code:

             

            #include <SPI.h>

             

            void setup()

            {

              pinMode(10, OUTPUT);

              SPI.begin();

            }

             

            void loop()

            {

              digitalWrite(10,LOW); digitalWrite(10,HIGH);

              SPI.transfer(0x55);

              digitalWrite(10,LOW); digitalWrite(10,HIGH);

            }

             

            After the first toggle of pin 10 it takes ~55usec before the SPI clock starts to tick. Then the clock ticks out the byte in ~1.8usec @ 4MHz.  Then there is another delay of ~42usec before the next pin 10 toggle.  The actual clock ticks do respond to changes in the frequency setting, but this is more or less irrelevant due to the 97usec of extra time for the ioctl() call.  Does anyone have an idea why the ioctl() call takes so long?  Is there another way to access the spi hardware?

             

            Mike

            • 3. Re: Edison SPI speed issue using ioctl()
              elembivos

              Again, using IoT Eclipse and mraa:

               

              #include "mraa.hpp"

               

              int main()

              {

                mraa::Spi* spi = new mraa::Spi(0);

                mraa::Gpio* gpio = new mraa::Gpio(45);

                gpio->dir(mraa::DIR_OUT);

                uint8_t* out=0;

                uint8_t* in=0;

                   for (int i = 0; i < 1000; i++) {

                  gpio->write(0);

                  spi->transfer(out,in,1);

                  gpio->write(1);

                   }

                delete spi;

                delete gpio;

              }

               

              After GPIO45 goes low the SPI clock doesn't start ticking for ~80usec.  Then when the clock finishes ticking GPIO45 doesn't go high again for ~75usec.  I looked into the source code for mraa and find it also uses spidev and the same ioctl() calls.  I'm sure if I drilled down and put the GPIO45 toggles immediately around the ioctl() I'd find the same ~55/42usec unexplained time.  I had high hopes for Edison, but it doesn't look like it's going to work the way I want it to.  Maybe after the next Yocto update?  Or maybe I'll try Ubilinux, but I read it is built on the same kernel as Yacto, so likely has the same /dev/spidev5.1 driver.  If anyone out there has a clue what is going on I'd love to hear it.  It may be there's a perfectly good reason why I shouldn't expect the SPI communication to work as fast as I want.

               

              Mike

              • 4. Re: Edison SPI speed issue using ioctl()
                Frederic Philips

                Hi elembivos,

                Enable the memory mapped GPIO for GPIO-45. This should make the switching faster.

                Refer to this post:Re: MRAA SPI transmits wierd data

                 

                Cheers

                Frederic

                • 5. Re: Edison SPI speed issue using ioctl()
                  elembivos

                  Thanks for the tip Frederic.  Updated the code to use memory mapped IO for GPIO-45:

                   

                  #include "mraa.hpp"

                   

                  int main()

                  {

                    fprintf(stdout, "hello mraa\n Version: %s\n", mraa_get_version());

                    mraa::Spi* spi = new mraa::Spi(0);

                       mraa_gpio_context gpio;

                       gpio = mraa_gpio_init(45);

                   

                       mraa_gpio_use_mmaped(gpio, 1);

                   

                    uint8_t* out=0;

                    uint8_t* in=0;

                       for (int i = 0; i < 1000; i++) {

                      mraa_gpio_write(gpio , 0);

                      spi->transfer(out,in,1);

                      mraa_gpio_write(gpio , 1);

                       }

                    delete spi;

                  }

                   

                  Slightly faster than the non memory mapped version, and now matches the Arduino IDE using the built in library, or SPIDEV directly.  There is a 60usec lag from the GPIO45 toggle before the SPI clock starts to tick, and a 40usec lag from when the clock finishes ticking until GPIO45 is toggled back.  This time is spent in the ioctl() call for the SPI transfer.  Is the issue in the /dev/spidev5.1 driver?  Is there something fundamental about how c is accessing the SPI driver that causes these lags?  I can use the memory mapped GPIOs to write software SPI read and write routines that can do better than this.  Still not close to the speed the hardware should be capable of.  I may just do it that way for the time being, but it just seems wrong, when the board has hardware dedicated for the purpose.

                   

                  Mike

                  • 6. Re: Edison SPI speed issue using ioctl()
                    MPayne

                    The problem is in the underlying SPI driver.  We have been analyzing the behavior you're describing, in order to implement a fix.  I believe the issue was tracked in the release notes (R2) as EDISON-2033 here: http://downloadmirror.intel.com/24698/eng/edison-bsp_rn_332032-007.pdf (page 11).

                    • 7. Re: Edison SPI speed issue using ioctl()
                      KurtE

                      Yes, I do believe from some other threads that that release note is related.  However I am not sure if it covers all of the cases I am interested in as we only see the title of the issue and not the full description/conversation in it.

                       

                      What I see there are at least two issues.

                      1) How long it takes between packets, which sounds like this issue.

                      2) How long it takes between bytes within the same packet.  Not sure if this is covered or not.  But I would hope that this hardware has a fifo for SPI, that can be used to keep the SPI buss active. Currently the SPI buss, there is a pretty good gap between bytes (like longer than the actual time to output a byte).  The code looks like it sets up to write one byte, wait for it to complete, retrieve it and then setup for next byte.  I would hope you could set it up to put as many bytes on the FIFO as it will hold, and then only at end of packet wait until the last byte completes...

                      • 8. Re: Edison SPI speed issue using ioctl()
                        MPayne

                        Yes, I'd agree to your points.  There were three things flagged, which I'm collectively referring to as 'fixing SPI':

                         

                        • There is a big delay at runtime suspend/resume. We mitigated it by adding a delay before runtime-suspending.
                        • The DMA is not activated (and does not work 'out of box')
                        • The FIFOs are not used. There is at least 50% overhead by reading registers in a non-optimal way.

                         

                        The theory we're working right now is that fixing/mitigating the above should bring it in line with what we're all after.  There were already several patches merged in the main line, and it's a pretty active defect.  Early results looked promising.  It's looking like it'll get fixed at some point.

                        • 9. Re: Edison SPI speed issue using ioctl()
                          elembivos

                          Thanks for the explanation MPayne.  I was suspecting it had to do with the driver.  Let me know if you need anyone to beta test a new driver, I'd be happy to help.

                           

                          Mike

                          • 10. Re: Edison SPI speed issue using ioctl()
                            elembivos

                            I noticed a new version if Yacto had been released so I thought I'd revisit my Edison and see if I could get the SPI working properly.  First I had a look into the release notes to see if issue EDISON-2033 that MPayne pointed to above had been resolved.  Package 2.0 Beta Revision 007 of the release notes listed unresolved issue EDISON-2033 as "Delay between packets in SPI," and looked like it might be related to this. Unfortunately the release notes Package 2.1 Revision 008 and Revision 009 have no mention of EDISON-2033 in either the resolved issues, or unresolved issues.  I didn't know if I should take that as a good or bad sign.  So I flashed one of my Edison boards a few weeks ago with the image from edison-image-ww18-15.zip and have finally gotten around to doing some more testing.  Though the behavior of the SPI has changed a bit, the overall performance has not improved.  Since all my previous testing produced similar results weather I used the Arduino IDE, or Eclipse with MRAA I decided to start with the Arduino IDE so I could skip the time it takes to get mraa installed.  Here's the code I used:

                             

                            #include <SPI.h>

                             

                            void setup() {

                              pinMode(10,OUTPUT); digitalWrite(10,HIGH);

                              SPI.begin();

                            }

                             

                            void loop() {

                              fastGpioPciDigitalWrite(10,LOW);

                              fastGpioPciDigitalWrite(10,HIGH);

                              SPI.transfer(0x55);

                              fastGpioPciDigitalWrite(10,LOW);

                              fastGpioPciDigitalWrite(10,HIGH);

                            }

                             

                            I then observed the "Arduino pin 10," the SPI CS pin, and SPI CLK pin on an oscilloscope to watch what was going on.

                            the fastGpio toggel, exiting of the loop(), re-entering the loop() and another fastGpio toggel total around 1usec for both versions 2.0 and 2.1.  Which is negligible compared to the time in SPI.transfer().  Here's a few observations:

                             

                            Version 2.0 time from the SPI.transfer(0x55) call to when CS goes low: 53usec

                            Version 2.1 time from the SPI.transfer(0x55) call to when CS goes low: 50usec

                             

                            Version 2.0 time from when CS goes low to when the clock starts ticking: 2usec

                            Version 2.1 time from when CS goes low to when the clock starts ticking: 28usec (What?)

                             

                            Version 2.0 time from when the clock stops ticking to when CS goes high: 5.2usec

                            Version 2.1 time from when the clock stops ticking to when CS goes high: 5.2usec

                             

                            Version 2.0 time from when CS goes high to the exit of SPI.transfer(0x55): 37usec

                            Version 2.1 time from when CS goes high to the exit of SPI.transfer(0x55): 28usec

                             

                            I measured many transfers and took the minimum to try to mitigate the OS influence.  So it looks like the time from CS going low to when the clock starts ticking actually got worse. The time from when CS goes high to the exit of SPI.transfer() may have gotten slightly better, but overall the single byte transfer rate is now slower.  Maybe something has improved for multi-byte transfers, I haven't tried testing that yet.

                             

                            Does anyone know what happened to EDISON-2033?  Am I not looking in the right place for the release notes?

                             

                            Mike

                            • 11. Re: Edison SPI speed issue using ioctl()
                              KurtE

                              I totally agree with you.  There is no way to track the state of the different bug reports, other than what they say that was fixed and what they say are known in the release notes.  But what is the state of the bugs mentioned in previous release notes that are not mentioned?

                               

                              Also SPI wise, for me it is DOA with this release.  None of the issues were resolved,  And there is some form of system hang that happens when you use SPI, which I believe that has been reported in several threads including: Uninterruptible sleep during Edison SPI

                              I know that the couple of different tests I ran, like trying to use TFT display totally hang the machine.

                               

                              My guess is it is some code like:

                               

                              <place something on a queue or system register>

                              <clear the state of some flag or register>

                              <Spin Waiting for the state of the flag or register to be set>

                              And the code that sets the flag/register runs quick enough that it is set before we do the clear... And so we spin forever (or until watchdog detects it)

                               

                              Alternatively could be a deadly embrace where two pieces of code use two different things like mutex's in opposite order...

                               

                              Anyway they say it is known and there is no current work around.  Hopefully they can/will either release a patch and/or new release soon.

                              • 12. Re: Edison SPI speed issue using ioctl()
                                Sriranjan

                                I also faced the exact same issue while trying to use a codec that works on SPI. So did people find a solution to this problem.Now I am planning to use an SPI ADC MCP3002 and I am afraid if the same SPI issue pops up there would be lot of problem. So is there a method like a software SPI that is faster? Please let me know KurtE elembivos

                                • 13. Re: Edison SPI speed issue using ioctl()
                                  elembivos

                                  Hello Sriranjan,  It's unfortunate the SPI issue didn't get fixed in the last Yacto release.  After I realized that, I pretty much gave up on using Edison for the project I had in mind.  In theory you could toggle the GPIOs in software and get much faster results than with the current hardware driver.  I considered doing this, but then I realized it wouldn't be any faster than the Arduino UNO version I was already using, so I lost my motivation.  It's a shame that the SPI is crippled like this when the hardware has so much potential.  I'll come back to this in a few months and see if the drivers have been fixed, but for now I don't have any projects that don't need SPI, so I have no projects I can use Edison for.

                                  • 14. Re: Edison SPI speed issue using ioctl()
                                    nsrb

                                    Same issue here. Any news after one year?

                                    1 2 Previous Next