9 Replies Latest reply on Oct 8, 2014 2:45 PM by kompatibel

    I/O speeds?


      Hello! Seems i might have the first post on this whole board!


      I'm very curious about galileo's GPIO performance on the digital and analog pins.


      i2c maxes out at 400khz, but all 14 digital pins seem to be connected to the cypress IO chip over i2c. After overhead, it would seem to me that there's only a few khz of throughput per pin available. Is there a fixed I/O clock? can I go any faster if i only use 1 or 2 digital out pins?


      SPI can be 25mhz but defaults to 4mhz. The A/D converter says it's 10-bit, 1msps. how will this be throttled if i stick to 4mhz SPI? If i up the SPI clock, will the A/D be doing round-robin on all 6 analog inputs so i'm capped at ~166ksps, or will i be able to eek out a full 1msps if i use a single pin.


      Finally, I looked through the source code to find details about pin interrupts. It seems that they're all done in software... any word on performance? Any hardware assistance from the cypress chip? (couldn't find that level of detail yet). How long a level am i going to need to detect an 'edge interrupt'?



      I'm really interested in the horsepower galileo can provide, but at face value i worry that the GPIO is still way behind an ancient 8-bit AVR...




        • 1. Re: I/O speeds?


          • GPIO
            • Default max throghput is 230 Hz
            • IO2 and IO3 if configured as OUTPUT_FAST - will clock in at 477 kHz - to 2.93 MHz
              • digitalWrite() will achieve 477 kHz
              • fastGpioDigitalWrite() - an Intel extension of the Arduino API will achieve 680 kHz
              • fastGpioDigitalWriteDestructive - can achieve GPIO speeds of about 2.93 MHz
            • Example sketch usage below
          • SPI
            • The Arduino API does byte-by-byte SPI transactions - which are not especially fast
            • Intel has provided an API extension to allow transfer of larger blocks of SPI data transferBuffer
            • This extension should allow Arduino sketches to take better advantage of the SPI interface.
            • I don't have the throughput number to hand - will search for it.
          • ADC
            • The throughput of the ADC is constrained by the SPI
          • Interrupts
            • In theory it would be possible to circumvent the GPIO input mechanism to kick a task in user-space
            • Right now we allow
              • GPIO lib based callbacks
              • HPET driven callbacks with granularity up to 1 kHz
            • As with all systems based on a general purpose operating system and even some systems that claim determinism - your reaction time to any given event is a function of the workload the processors is engaged in. A quiescent system is likely to react very quickly to a GPIO even if its triggering a user-space application. A system under load could have significant jitter. This is down to the choices made by the application engineer re: how much concurrent processing is appropriate.

          Galileo - has two pins IO2 and IO3 through which we can drive significant data rates.

          By default these two pins are routed to the cypress.

          There are three methods to communicate with these pins - which have increasing throughput


          1. digitalWrite()
            1. Using this method it is possible to toggle an individual pin in a tight loop @ about 477 kHz
            2. pinMode(2, OUTPUT_FAST);
            3. pinMode(3, OUTPUT_FAST);
            4. This is a read-modify-write
              1. To toggle a bit - first we read
              2. Then we update
              3. Then we write back
          2. fastGpioDigitalWrite(register uint8_t gpio, register uint8_t val)
            1. This function actually lets you write directly to the registers - without going through the code around digitalWrite() and consequently has better performance than a straight digitalWrite
            2. Using this method it is possible to toggle an individual pin (GPIO_FAST_IO2, GPIO_FAST_IO3) at about 680 kHz
            3. pinMode(2, OUTPUT_FAST);
            4. pinMode(3, OUTPUT_FAST);
              1. fastGpioDigitalWrite(GPIO_FAST_IO2, 1);
              2. fastGpioDigitalWrite(GPIO_FAST_IO3, 0);
            5. Again this uses read/modify/write - and can toggle one GPIO at a time


          3. fastGpioDigitalWriteDestructive(register uint8_t gpio_mask);
            1. Using this method it is possible to achieve 2.93 Mhz data toggle rate on IO2/IO3 individually or simultaneously
            2. pinMode(2, OUTPUT_FAST);
            3. pinMode(3, OUTPUT_FAST);
            4. It is the responsibility of the application to maintain the state of the GPIO registers directly
            5. To enable this a function called fastGpioDigitalLatch() is provided - which allows the calling logic to latch the initial state of the GPIOs - before updating later
            6. This method just writes GPIO bits straight to the GPIO register - i.e. a destructive write - for this reason it is approximately 2 x faster then read/modify/write
            7. Twice as fast for a given write - means four times faster for a given wave form - hence ~700kHz (680kHz) becomes ~2.8Mhz (2.93 MHz)


          Example-1 - outputs 477kHz waveform on IO2:


              pinMode(2, OUTPUT_FAST);




              register int x = 0;


                  digitalWrite(2, x);

                  x =!x;




          Example-2 - outputs 683kHz waveform on IO3:


              pinMode(3, OUTPUT_FAST);




              register int x = 0;


                  fastGpioDigitalWrite(GPIO_FAST_IO3, x);

                  x =!x;



          Example-3 - outputs 2.93MHz waveform on IO3:

          uint32_t latchValue;


              pinMode(3, OUTPUT_FAST);

              latchValue = fastGpioDigitalLatch();






                 latchValue ^= GPIO_FAST_IO3;



          Example-4 - outputs 2.93MHz waveform on both IO2 and IO3:

          uint32_t latchValue;


              pinMode(2, OUPUT_FASTMODE);

              pinMode(3, OUPUT_FASTMODE);

              latchValue = fastGpioDigitalLatch(); // latch initial state






                  if(latchValue & GPIO_FAST_IO3){

                      latchValue |= GPIO_FAST_IO2;

                      latchValue &= ~ GPIO_FAST_IO3;


                      latchValue |= GPIO_FAST_IO3;

                      latchValue &= GPIO_FAST_IO2;





          In other words the responsibility lies with the application designer in cases 3 and 4 to ensure the GPIO register values are correct - assuming - these values matter to the application use-case

          • 2. Re: I/O speeds?

            Awesome, thanks for all the details and such a quick response.


            It really seems like a unique beast, i'm sure i'll be making interesting use of it, though i doubt it'll be in typical arduino-like scenarios. I imagine galileo will be the cheapest quark CRB available though.


            I might have to try a CY7C68013A over the usb host port or find a pcie i/o board just to see what it'll be capable of, unconstrained by the arduino form factor. I never use shields anyway.


            Any ideas if there will be places to pre-order boards or request evaluation units?




            • 3. Re: I/O speeds?

              Hi Joe,


              Please be patient and hopefully by end of November Galileo will be available for sale.




              • 4. Re: I/O speeds?


                • The throughput of the ADC is constrained by the SPI

                And also by the 0.15nF capacitor in series with a 5K resistor gives a time constant of 0.75mS which rules out any sort of audio sampling. At least a real Arduino will sample at 10KHz.

                The I/O sort of lets this board down greatly, negating the advantages of memory and speed.

                • 5. Re: I/O speeds?

                  This post by SerkeyK might be of interest to those following this thread.



                  • 6. Re: I/O speeds?



                    I need some advise on how to best troubleshoot a performance issue on Galileo that I believe is SPI related. We are in the process of porting to Galileo a project that includes a CANbus shield (CANdiy) that makes use of SPI to communicate with the CANbus chip set. Some of the CAN peripherals in the project broadcast multiple messages at 50 msec intervals so the message load is significant.


                    Unfortunately initial tests with Galileo are 6 times slower than with the Arduino UNO board. On the UNO we can process an average of 100 messages per second, on Galileo we are getting 16 or so.  The code below is using polling instead of interrupts because the UNO could not keep up with the interrupt frequency but polling worked OK. Galileo has itself some interrupt handling limitations documented in the release notes so we have not tried them.


                    In addition to SPI activity there is significant string manipulation and timestamping going on that may work differently in Galileo impacting performance.


                    I will be happy to send a CAN shield and the libraries to someone at Intel looking into Shield support. We are trying to present the project with Galileo at the Miami Maker Faire on 11/16 so timing is really tight.



                    Here is the code we use for CAN testing:



                    Can bus sniffer - bernardo.starosta@terabatt.com



                    #include <SPI.h>

                    #include <mcp_can.h> // CAN library, a variation of the Seeduino library



                    // CAN definitions


                    INT32U CANid;

                    unsigned char CANmsgLength=8; // in this program we will use 8 byte messages. 4 byte messages will be treated as special cases if needed.

                    unsigned char CANbuffer[8] ={0};

                    #define SERIAL_OUTPUT 100


                    void setup(void)


                      int returnValue;


                      delay (4000);


                      Serial.println ("Initializing");




                      returnValue = initializeCAN (CAN_500KBPS, SPICS); // passing PIN for CAN select

                      if (returnValue != CAN_OK) handleException ("initializeCAN", returnValue, "FATAL", 0);

                      else Serial.println ("CAN OK");


                      Serial.println ("\nSetup: COMPLETE");





                    void loop(void)







                    **********************************  Check Devices      *********************************



                    void checkDevices(){


                      if ( CAN.checkReceive() == CAN_MSGAVAIL) {

                        processCANqueue (&CANid, &CANmsgLength, CANbuffer);

                        // print messages allowed by the filters

                        printCANsniff_message (CANid,CANmsgLength, CANbuffer,SERIAL_OUTPUT );






                    **********************************  PROCESS CAN QUEUE     ********************************



                    int processCANqueue (INT32U* CANid, unsigned char* bufferLength, unsigned char CANbuffer[]) {


                      CAN.readMsgBuf (bufferLength, CANbuffer);

                      *CANid = CAN.getCanId();





                    **********************************  INITIALIZE PINS ***************************************


                    void initializePins() {


                      // set pin 53 instead of 10 as OUTPUT per documentation for SPI interface to work properly on MEGA

                      pinMode (10, OUTPUT); // Ethernet

                      pinMode (4, OUTPUT); // SD Ethernet shield

                      pinMode (5, OUTPUT); // CAN select pin (manual change in board due to conflict with Ethernet on 10

                      pinMode (9, OUTPUT); // SD CANbus shield



                      // Deselect all SPI devices

                      digitalWrite (10, HIGH); // de-select Etheret and be ready to init SD first

                      digitalWrite (9, HIGH); // start the SD in CANshield in "unselected (high) mode

                      digitalWrite (4, HIGH); // start the SD in Ethernet in unselected mode

                      digitalWrite (5, HIGH); // start CAN in unselected mode







                    **********************************  INITIALIZE CAN  *************************************




                    int initializeCAN (int CANbusSpeed, byte CAN_SPICS){


                      int CAN_init_status=0;



                      /* Implementation notes:

                       1. For now messages will be read using polling mode from DoWork.

                       The most time-critical function of this system is maintaining CAN communication through boradcast

                       It is acceptable to fall behind in reading CAN messages provided that devices stay in communicaiton


                      Serial.print ("CAN init: CAN SPICS pin = ");

                      Serial.println (CAN_SPICS);

                      digitalWrite (CAN_SPICS, LOW); // enable CAN shield (low = enabled)

                      CAN_init_status = CAN.begin(CAN_500KBPS);                       // init can bus : baudrate = 500k



                      /* Code below sets filters when needed



                       // Mask 1 needs to be set even though it is not used bow. Saving this for the charger - Mask 1 has up to two filters 

                       CAN.init_Mask (0,0,0x00ff);// dummy, just in case both need to get used

                       CAN.init_Filt (0,0,0x1234);// dummy

                       CAN.init_Filt (1,0,0x1233);// dummy


                       // MAsk 2 has up to 4 filters, we are using that one for the Batteries


                       CAN.init_Mask (1,0,0xffff);// compare all bits for now

                       CAN.init_Filt (2,0,0x0400);

                       CAN.init_Filt (3,0,0x0410);

                       CAN.init_Filt (4,0,0x0450);

                       CAN.init_Filt (5,0,0x0460);





                      return (CAN_init_status);






                    **********************************  PRINT CAN MSG       ********************************



                    void printCANsniff_message (INT32U CANid, unsigned char  bufferLength, unsigned char CANbuffer[], int outputDestination){

                      char sprintfBuffer[80];

                      char CANdumpBuffer[80];



                      if (outputDestination == SERIAL_OUTPUT) {



                        sprintf (sprintfBuffer, "%ld\tCAN ID = %x\t\0", millis(), CANid);



                        if (bufferLength == 8) {

                          sprintf (CANdumpBuffer, "%x\t%x\t%x\t%x\t%x\t%x\t%x\t%x\0", CANbuffer[0], CANbuffer[1], CANbuffer[2], CANbuffer[3], CANbuffer[4], CANbuffer[5], CANbuffer[6], CANbuffer[7]);


                        else sprintf (CANdumpBuffer, "%x\t%x\t%x\t%x\0", CANbuffer[0], CANbuffer[1], CANbuffer[2], CANbuffer[3]); // length is 4



                        strcat (sprintfBuffer, CANdumpBuffer);











                    **********************************  HANDLE EXCEPTION ***************************************



                    void handleException (char* callingFunction, int code, char* severity, int logOutput){


                      if (logOutput == SERIAL_OUTPUT) {

                        Serial.print ("Exception from ");

                        Serial.print (callingFunction);

                        Serial.print ("  Code:");


                        Serial.print ("  Severity = ");






                    • 7. Re: I/O speeds?

                      It has been shown that is possible to performed A/D conversions at a rate of 10 kHz using a Atmel ATmega328P by directly writing to the registers (not using analogRead(A0)). (This is described here: http://www.marulaberry.co.za/index.php/tutorials/code/arduino-adc/



                      Is there a similar way to increase the A/D conversion speed on the Galileo Board by directly writing to the registers in a similar fashion?

                      • 8. Re: I/O speeds?



                        Is there a way to increase the A/D conversion speed on the Galileo Board by directly writing to the registers??

                        • 9. Re: I/O speeds?

                          or to be more precise, change the prescaler of the ADC clocks


                          ADCSRA |= (1 << ADPS2) | (1 << ADPS1);